Skip to content
This repository has been archived by the owner on Mar 25, 2022. It is now read-only.

Commit

Permalink
feat(fbcnms-ui): Organization detail
Browse files Browse the repository at this point in the history
  • Loading branch information
HannaFar committed Jan 19, 2022
1 parent 23a1c0f commit 3ef04f4
Show file tree
Hide file tree
Showing 5 changed files with 971 additions and 255 deletions.
63 changes: 63 additions & 0 deletions fbcnms-packages/fbcnms-ui/components/Colors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/**
* Copyright 2004-present Facebook. All Rights Reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/

export const colors = {
primary: {
white: '#FFFFFF',
selago: '#F4F7FD',
concrete: '#F2F2F2',
mercury: '#E5E5E5',
nobel: '#B3B3B3',
gullGray: '#9DA7BB',
comet: '#545F77',
brightGray: '#323845',
mirage: '#171B25',
},
secondary: {
malibu: '#88B3F9',
dodgerBlue: '#3984FF',
mariner: '#1F5BC4',
},
button: {
lightOutline: '#CCD0DB',
fill: '#FAFAFB',
},
state: {
positive: '#31BF56',
positiveAlt: '#229A41',
error: '#E52240',
errorAlt: '#B21029',
errorFill: '#FFF8F9',
warning: '#F5DD5A',
warningAlt: '#B69900',
warningFill: '#FFFCED',
},
alerts: {
severe: 'E52240',
major: '#E36730',
minor: '#F5DD5A',
other: '#88B3F9',
},
data: {
coral: '#FF824B',
flamePea: '#E36730',
portage: '#A07EEA',
studio: '#6649A6',
},
code: {
crusta: '#F76D47',
pelorous: '#39B6C8',
electricViolet: '#7D4DFF',
orchid: '#DA70D6',
chelseaCucumber: '#91B859',
candlelight: '#FFD715',
mischka: '#D4D8DE',
},
};
266 changes: 266 additions & 0 deletions fbcnms-packages/fbcnms-ui/components/DataGrid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
/**
* Copyright 2004-present Facebook. All Rights Reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
* @format
*/
import typeof SvgIcon from '@material-ui/core/@@SvgIcon';

import Card from '@material-ui/core/Card';
import CardHeader from '@material-ui/core/CardHeader';
import Collapse from '@material-ui/core/Collapse';
import Divider from '@material-ui/core/Divider';
import ExpandLess from '@material-ui/icons/ExpandLess';
import ExpandMore from '@material-ui/icons/ExpandMore';
import Grid from '@material-ui/core/Grid';
import IconButton from '@material-ui/core/IconButton';
import Input from '@material-ui/core/Input';
import InputAdornment from '@material-ui/core/InputAdornment';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import React from 'react';
import Visibility from '@material-ui/icons/Visibility';
import VisibilityOff from '@material-ui/icons/VisibilityOff';

import {colors} from './Colors';
import {makeStyles} from '@material-ui/styles';

const useStyles = makeStyles(theme => ({
dataHeaderBlock: {
display: 'flex',
alignItems: 'center',
padding: 0,
},
dataHeaderContent: {
display: 'flex',
alignItems: 'center',
},
dataHeaderIcon: {
fill: colors.primary.comet,
marginRight: theme.spacing(1),
},
dataBlock: {
boxShadow: `0 0 0 1px ${colors.primary.concrete}`,
},
dataLabel: {
color: colors.primary.comet,
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
},
dataValue: {
color: colors.primary.brightGray,
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
width: props =>
props.hasStatus
? 'calc(100% - 16px)'
: props.hasIcon
? 'calc(100% - 32px)'
: '100%',
},
dataObscuredValue: {
color: colors.primary.brightGray,
width: '100%',

'& input': {
whiteSpace: 'nowrap',
overflow: 'hidden',
textOverflow: 'ellipsis',
},
},
dataBox: {
width: '100%',
padding: props => (props.collapsed ? '0' : null),

'& > div': {
width: '100%',
},
},
dataIcon: {
display: 'flex',
alignItems: 'center',

'& svg': {
fill: colors.primary.comet,
marginRight: theme.spacing(1),
},
},
list: {
padding: 0,
},
}));

// Data Icon adds an icon to the left of the value
function DataIcon(icon: SvgIcon, val: string) {
const props = {hasIcon: true};
const classes = useStyles(props);
const Icon = icon;
return (
<Grid container alignItems="center">
<Grid item className={classes.dataIcon}>
<Icon />
</Grid>
<Grid item className={classes.dataValue}>
{val}
</Grid>
</Grid>
);
}

// Data Obscure makes the field into a password type filed with a visibility toggle for more sensitive fields.
function DataObscure(value: number | string, category: ?string) {
const [showPassword, setShowPassword] = React.useState(false);
return (
<Input
type={showPassword ? 'text' : 'password'}
fullWidth={true}
value={value}
disableUnderline={true}
readOnly={true}
data-testid={`${category ?? value} obscure`}
endAdornment={
<InputAdornment position="end">
<IconButton
aria-label="toggle password visibility"
onClick={() => setShowPassword(!showPassword)}
onMouseDown={event => event.preventDefault()}>
{showPassword ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>
}
/>
);
}

function DataCollapse(data: Data) {
const props = {collapsed: true};
const classes = useStyles(props);
const [open, setOpen] = React.useState(true);
const dataEntryValue = data.value + (data.unit ?? '');
return (
<List
key={`${data.category ?? data.value}Collapse`}
className={classes.list}>
<ListItem button onClick={() => setOpen(!open)}>
<CardHeader
data-testid={data.category}
title={data.category}
className={classes.dataBox}
subheader={
data.icon
? DataIcon(data.icon, dataEntryValue)
: data.obscure === true
? DataObscure(data.value, data.category)
: dataEntryValue
}
titleTypographyProps={{
variant: 'caption',
className: classes.dataLabel,
title: data.category,
}}
subheaderTypographyProps={{
variant: 'body1',
className: classes.dataValue,
title: data.tooltip ?? dataEntryValue,
}}
/>
{open ? <ExpandLess /> : <ExpandMore />}
</ListItem>
<Divider />
<Collapse key={data.value} in={open} timeout="auto" unmountOnExit>
{data.collapse ?? <></>}
</Collapse>
</List>
);
}

type Data = {
icon?: SvgIcon,
category?: string,
value: number | string,
obscure?: boolean,
//$FlowFixMe TODO: Needs a ComponentType argument
collapse?: ComponentType | boolean,
unit?: string,
statusCircle?: boolean,
statusInactive?: boolean,
status?: boolean,
tooltip?: string,
};

export type DataRows = Data[];

type Props = {data: DataRows[], testID?: string};

export default function DataGrid(props: Props) {
const classes = useStyles();
const dataGrid = props.data.map((row, i) => (
<Grid key={i} container direction="row">
{row.map((data, j) => {
const dataEntryValue = data.value + (data.unit ?? '');

return (
<React.Fragment key={`data-${i}-${j}`}>
<Grid
item
container
alignItems="center"
xs={12}
md
key={`data-${i}-${j}`}
zeroMinWidth
className={classes.dataBlock}>
<Grid item xs={12}>
{data.collapse !== undefined && data.collapse !== false ? (
DataCollapse(data)
) : (
<CardHeader
data-testid={data.category}
className={classes.dataBox}
title={data.category}
titleTypographyProps={{
variant: 'caption',
className: classes.dataLabel,
title: data.category,
}}
subheaderTypographyProps={{
variant: 'body1',
className:
data.obscure === true
? classes.dataObscuredValue
: classes.dataValue,
title: data.tooltip ?? dataEntryValue,
}}
subheader={
data.icon
? DataIcon(data.icon, dataEntryValue)
: data.obscure === true
? DataObscure(data.value, data.category)
: dataEntryValue
}
/>
)}
</Grid>
</Grid>
</React.Fragment>
);
})}
</Grid>
));
return (
<Card elevation={0}>
<Grid
container
alignItems="center"
justify="center"
data-testid={props.testID ?? null}>
{dataGrid}
</Grid>
</Card>
);
}
Loading

0 comments on commit 3ef04f4

Please sign in to comment.