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

Commit 3ef04f4

Browse files
committed
feat(fbcnms-ui): Organization detail
1 parent 23a1c0f commit 3ef04f4

File tree

5 files changed

+971
-255
lines changed

5 files changed

+971
-255
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* Copyright 2004-present Facebook. All Rights Reserved.
3+
*
4+
* This source code is licensed under the BSD-style license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
* @format
9+
*/
10+
11+
export const colors = {
12+
primary: {
13+
white: '#FFFFFF',
14+
selago: '#F4F7FD',
15+
concrete: '#F2F2F2',
16+
mercury: '#E5E5E5',
17+
nobel: '#B3B3B3',
18+
gullGray: '#9DA7BB',
19+
comet: '#545F77',
20+
brightGray: '#323845',
21+
mirage: '#171B25',
22+
},
23+
secondary: {
24+
malibu: '#88B3F9',
25+
dodgerBlue: '#3984FF',
26+
mariner: '#1F5BC4',
27+
},
28+
button: {
29+
lightOutline: '#CCD0DB',
30+
fill: '#FAFAFB',
31+
},
32+
state: {
33+
positive: '#31BF56',
34+
positiveAlt: '#229A41',
35+
error: '#E52240',
36+
errorAlt: '#B21029',
37+
errorFill: '#FFF8F9',
38+
warning: '#F5DD5A',
39+
warningAlt: '#B69900',
40+
warningFill: '#FFFCED',
41+
},
42+
alerts: {
43+
severe: 'E52240',
44+
major: '#E36730',
45+
minor: '#F5DD5A',
46+
other: '#88B3F9',
47+
},
48+
data: {
49+
coral: '#FF824B',
50+
flamePea: '#E36730',
51+
portage: '#A07EEA',
52+
studio: '#6649A6',
53+
},
54+
code: {
55+
crusta: '#F76D47',
56+
pelorous: '#39B6C8',
57+
electricViolet: '#7D4DFF',
58+
orchid: '#DA70D6',
59+
chelseaCucumber: '#91B859',
60+
candlelight: '#FFD715',
61+
mischka: '#D4D8DE',
62+
},
63+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
/**
2+
* Copyright 2004-present Facebook. All Rights Reserved.
3+
*
4+
* This source code is licensed under the BSD-style license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
* @format
9+
*/
10+
import typeof SvgIcon from '@material-ui/core/@@SvgIcon';
11+
12+
import Card from '@material-ui/core/Card';
13+
import CardHeader from '@material-ui/core/CardHeader';
14+
import Collapse from '@material-ui/core/Collapse';
15+
import Divider from '@material-ui/core/Divider';
16+
import ExpandLess from '@material-ui/icons/ExpandLess';
17+
import ExpandMore from '@material-ui/icons/ExpandMore';
18+
import Grid from '@material-ui/core/Grid';
19+
import IconButton from '@material-ui/core/IconButton';
20+
import Input from '@material-ui/core/Input';
21+
import InputAdornment from '@material-ui/core/InputAdornment';
22+
import List from '@material-ui/core/List';
23+
import ListItem from '@material-ui/core/ListItem';
24+
import React from 'react';
25+
import Visibility from '@material-ui/icons/Visibility';
26+
import VisibilityOff from '@material-ui/icons/VisibilityOff';
27+
28+
import {colors} from './Colors';
29+
import {makeStyles} from '@material-ui/styles';
30+
31+
const useStyles = makeStyles(theme => ({
32+
dataHeaderBlock: {
33+
display: 'flex',
34+
alignItems: 'center',
35+
padding: 0,
36+
},
37+
dataHeaderContent: {
38+
display: 'flex',
39+
alignItems: 'center',
40+
},
41+
dataHeaderIcon: {
42+
fill: colors.primary.comet,
43+
marginRight: theme.spacing(1),
44+
},
45+
dataBlock: {
46+
boxShadow: `0 0 0 1px ${colors.primary.concrete}`,
47+
},
48+
dataLabel: {
49+
color: colors.primary.comet,
50+
whiteSpace: 'nowrap',
51+
overflow: 'hidden',
52+
textOverflow: 'ellipsis',
53+
},
54+
dataValue: {
55+
color: colors.primary.brightGray,
56+
whiteSpace: 'nowrap',
57+
overflow: 'hidden',
58+
textOverflow: 'ellipsis',
59+
width: props =>
60+
props.hasStatus
61+
? 'calc(100% - 16px)'
62+
: props.hasIcon
63+
? 'calc(100% - 32px)'
64+
: '100%',
65+
},
66+
dataObscuredValue: {
67+
color: colors.primary.brightGray,
68+
width: '100%',
69+
70+
'& input': {
71+
whiteSpace: 'nowrap',
72+
overflow: 'hidden',
73+
textOverflow: 'ellipsis',
74+
},
75+
},
76+
dataBox: {
77+
width: '100%',
78+
padding: props => (props.collapsed ? '0' : null),
79+
80+
'& > div': {
81+
width: '100%',
82+
},
83+
},
84+
dataIcon: {
85+
display: 'flex',
86+
alignItems: 'center',
87+
88+
'& svg': {
89+
fill: colors.primary.comet,
90+
marginRight: theme.spacing(1),
91+
},
92+
},
93+
list: {
94+
padding: 0,
95+
},
96+
}));
97+
98+
// Data Icon adds an icon to the left of the value
99+
function DataIcon(icon: SvgIcon, val: string) {
100+
const props = {hasIcon: true};
101+
const classes = useStyles(props);
102+
const Icon = icon;
103+
return (
104+
<Grid container alignItems="center">
105+
<Grid item className={classes.dataIcon}>
106+
<Icon />
107+
</Grid>
108+
<Grid item className={classes.dataValue}>
109+
{val}
110+
</Grid>
111+
</Grid>
112+
);
113+
}
114+
115+
// Data Obscure makes the field into a password type filed with a visibility toggle for more sensitive fields.
116+
function DataObscure(value: number | string, category: ?string) {
117+
const [showPassword, setShowPassword] = React.useState(false);
118+
return (
119+
<Input
120+
type={showPassword ? 'text' : 'password'}
121+
fullWidth={true}
122+
value={value}
123+
disableUnderline={true}
124+
readOnly={true}
125+
data-testid={`${category ?? value} obscure`}
126+
endAdornment={
127+
<InputAdornment position="end">
128+
<IconButton
129+
aria-label="toggle password visibility"
130+
onClick={() => setShowPassword(!showPassword)}
131+
onMouseDown={event => event.preventDefault()}>
132+
{showPassword ? <Visibility /> : <VisibilityOff />}
133+
</IconButton>
134+
</InputAdornment>
135+
}
136+
/>
137+
);
138+
}
139+
140+
function DataCollapse(data: Data) {
141+
const props = {collapsed: true};
142+
const classes = useStyles(props);
143+
const [open, setOpen] = React.useState(true);
144+
const dataEntryValue = data.value + (data.unit ?? '');
145+
return (
146+
<List
147+
key={`${data.category ?? data.value}Collapse`}
148+
className={classes.list}>
149+
<ListItem button onClick={() => setOpen(!open)}>
150+
<CardHeader
151+
data-testid={data.category}
152+
title={data.category}
153+
className={classes.dataBox}
154+
subheader={
155+
data.icon
156+
? DataIcon(data.icon, dataEntryValue)
157+
: data.obscure === true
158+
? DataObscure(data.value, data.category)
159+
: dataEntryValue
160+
}
161+
titleTypographyProps={{
162+
variant: 'caption',
163+
className: classes.dataLabel,
164+
title: data.category,
165+
}}
166+
subheaderTypographyProps={{
167+
variant: 'body1',
168+
className: classes.dataValue,
169+
title: data.tooltip ?? dataEntryValue,
170+
}}
171+
/>
172+
{open ? <ExpandLess /> : <ExpandMore />}
173+
</ListItem>
174+
<Divider />
175+
<Collapse key={data.value} in={open} timeout="auto" unmountOnExit>
176+
{data.collapse ?? <></>}
177+
</Collapse>
178+
</List>
179+
);
180+
}
181+
182+
type Data = {
183+
icon?: SvgIcon,
184+
category?: string,
185+
value: number | string,
186+
obscure?: boolean,
187+
//$FlowFixMe TODO: Needs a ComponentType argument
188+
collapse?: ComponentType | boolean,
189+
unit?: string,
190+
statusCircle?: boolean,
191+
statusInactive?: boolean,
192+
status?: boolean,
193+
tooltip?: string,
194+
};
195+
196+
export type DataRows = Data[];
197+
198+
type Props = {data: DataRows[], testID?: string};
199+
200+
export default function DataGrid(props: Props) {
201+
const classes = useStyles();
202+
const dataGrid = props.data.map((row, i) => (
203+
<Grid key={i} container direction="row">
204+
{row.map((data, j) => {
205+
const dataEntryValue = data.value + (data.unit ?? '');
206+
207+
return (
208+
<React.Fragment key={`data-${i}-${j}`}>
209+
<Grid
210+
item
211+
container
212+
alignItems="center"
213+
xs={12}
214+
md
215+
key={`data-${i}-${j}`}
216+
zeroMinWidth
217+
className={classes.dataBlock}>
218+
<Grid item xs={12}>
219+
{data.collapse !== undefined && data.collapse !== false ? (
220+
DataCollapse(data)
221+
) : (
222+
<CardHeader
223+
data-testid={data.category}
224+
className={classes.dataBox}
225+
title={data.category}
226+
titleTypographyProps={{
227+
variant: 'caption',
228+
className: classes.dataLabel,
229+
title: data.category,
230+
}}
231+
subheaderTypographyProps={{
232+
variant: 'body1',
233+
className:
234+
data.obscure === true
235+
? classes.dataObscuredValue
236+
: classes.dataValue,
237+
title: data.tooltip ?? dataEntryValue,
238+
}}
239+
subheader={
240+
data.icon
241+
? DataIcon(data.icon, dataEntryValue)
242+
: data.obscure === true
243+
? DataObscure(data.value, data.category)
244+
: dataEntryValue
245+
}
246+
/>
247+
)}
248+
</Grid>
249+
</Grid>
250+
</React.Fragment>
251+
);
252+
})}
253+
</Grid>
254+
));
255+
return (
256+
<Card elevation={0}>
257+
<Grid
258+
container
259+
alignItems="center"
260+
justify="center"
261+
data-testid={props.testID ?? null}>
262+
{dataGrid}
263+
</Grid>
264+
</Card>
265+
);
266+
}

0 commit comments

Comments
 (0)