@centreon/ui 26.11.0 → 26.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@centreon/ui",
3
- "version": "26.11.0",
3
+ "version": "26.11.2",
4
4
  "description": "Centreon UI Components",
5
5
  "scripts": {
6
6
  "update:deps": "pnpx npm-check-updates -i --format group",
@@ -154,7 +154,7 @@ const checkGraphWidth = (): void => {
154
154
  .and('equal', '392');
155
155
 
156
156
  cy.findByTestId('graph-interaction-zone').then((graph) => {
157
- expect(Number(graph[0].attributes.width.value)).to.be.greaterThan(1167);
157
+ expect(Number(graph[0].attributes.width.value)).to.be.greaterThan(1149);
158
158
  });
159
159
  };
160
160
 
@@ -177,9 +177,6 @@ describe('Line chart', () => {
177
177
 
178
178
  cy.contains('06/18/2023').should('be.visible');
179
179
 
180
- cy.contains('0.56 s').should('be.visible');
181
- cy.contains('73.25%').should('be.visible');
182
-
183
180
  cy.makeSnapshot();
184
181
  });
185
182
 
@@ -291,7 +288,7 @@ describe('Line chart', () => {
291
288
 
292
289
  cy.findByTestId('graph-interaction-zone')
293
290
  .should('have.attr', 'width')
294
- .and('equal', '1230');
291
+ .and('equal', '1220');
295
292
 
296
293
  cy.get('[data-icon="true"]')
297
294
  .eq(0)
@@ -743,12 +740,10 @@ describe('Lines and bars', () => {
743
740
 
744
741
  checkGraphWidth();
745
742
 
746
- cy.get(
747
- 'path[d="M7.473829201101932,289.92398616599974 h56.26446280991734 h1v1 v100.07601383400026 a1,1 0 0 1 -1,1 h-56.26446280991734 a1,1 0 0 1 -1,-1 v-100.07601383400026 v-1h1z"]'
748
- ).should('be.visible');
749
- cy.get(
750
- 'path[d="M23.953168044077135,235.36727985204413 h23.305785123966935 a17.479338842975203,17.479338842975203 0 0 1 17.479338842975203,17.479338842975203 v19.598028628005196 v17.479338842975203h-17.479338842975203 h-23.305785123966935 h-17.479338842975203v-17.479338842975203 v-19.598028628005196 a17.479338842975203,17.479338842975203 0 0 1 17.479338842975203,-17.479338842975203z"]'
751
- ).should('be.visible');
743
+ cy.get('[data-testid="stacked-bar-2-0-1243"]').should('be.visible');
744
+ cy.get('[data-testid="stacked-bar-10-0-8520.53622137828"]').should(
745
+ 'be.visible'
746
+ );
752
747
 
753
748
  cy.makeSnapshot();
754
749
  });
@@ -44,7 +44,7 @@ export const useComputeYAxisMaxCharacters = ({
44
44
  isHorizontal
45
45
  });
46
46
 
47
- // Always add a character space in case the algorithm does not compute the displayed value in axis.
47
+ // Always add a space in case the algorithm does not compute the displayed value in axis.
48
48
  const maxLeftAxisCharacters = useMemo(() => {
49
49
  if (!leftScale) {
50
50
  return 0;
@@ -55,7 +55,7 @@ export const useComputeYAxisMaxCharacters = ({
55
55
 
56
56
  return isEmpty(formattedTicks)
57
57
  ? 2
58
- : Math.max(...formattedTicks.map((value) => value.length), 2) + 1;
58
+ : Math.max(...formattedTicks.map((value) => value.length), 2) + 3;
59
59
  }, [leftScale, axisLeft]);
60
60
 
61
61
  const maxRightAxisCharacters = useMemo(() => {
@@ -68,7 +68,7 @@ export const useComputeYAxisMaxCharacters = ({
68
68
 
69
69
  return isEmpty(formattedTicks)
70
70
  ? 2
71
- : Math.max(...formattedTicks.map((value) => value.length), 2) + 1;
71
+ : Math.max(...formattedTicks.map((value) => value.length), 2) + 3;
72
72
  }, [rightScale, axisRight]);
73
73
 
74
74
  return {
@@ -45,6 +45,7 @@ declare module '@mui/material/styles' {
45
45
  main: string;
46
46
  };
47
47
  statusBackground: StatusBackground;
48
+ tile: TypeTile;
48
49
  }
49
50
  interface StatusBackground {
50
51
  error: string;
@@ -64,6 +65,13 @@ declare module '@mui/material/styles' {
64
65
  main: string;
65
66
  };
66
67
  statusBackground: StatusBackground;
68
+ tile: TypeTile;
69
+ }
70
+
71
+ interface TypeTile {
72
+ background: string;
73
+ border: string;
74
+ borderHover: string;
67
75
  }
68
76
 
69
77
  interface TypeBackground {
@@ -294,6 +302,11 @@ export const lightPalette: PaletteOptions = {
294
302
  primary: '#000000',
295
303
  secondary: '#666666'
296
304
  },
305
+ tile: {
306
+ background: '#FFFFFF',
307
+ border: '#E3E3E3',
308
+ borderHover: '#CCCCCC'
309
+ },
297
310
  warning: {
298
311
  contrastText: '#000',
299
312
  dark: '#FC7E00',
@@ -436,6 +449,11 @@ export const darkPalette: PaletteOptions = {
436
449
  primary: '#FFFFFF',
437
450
  secondary: '#CCCCCC'
438
451
  },
452
+ tile: {
453
+ background: '#2E2E2E',
454
+ border: '#4A4A4A',
455
+ borderHover: '#6D6D6D'
456
+ },
439
457
  warning: {
440
458
  contrastText: '#fff',
441
459
  main: '#C55400'
@@ -112,6 +112,11 @@
112
112
  --color-status-background-warning: #fd9b27;
113
113
  --color-success-main: #88b922;
114
114
 
115
+ /* Tile container colors */
116
+ --color-tile-background: #ffffff;
117
+ --color-tile-border: #e3e3e3;
118
+ --color-tile-border-hover: #cccccc;
119
+
115
120
  /* Text colors */
116
121
  --color-text-disabled: #999999;
117
122
  --color-text-primary: #000000;
@@ -195,6 +200,9 @@
195
200
  --color-stauts-background-unknown: #666666;
196
201
  --color-status-background-warning: #c55400;
197
202
  --color-success-main: #5f8118;
203
+ --color-tile-background: #2e2e2e;
204
+ --color-tile-border: #4a4a4a;
205
+ --color-tile-border-hover: #6d6d6d;
198
206
  --color-text-disabled: #666666;
199
207
  --color-text-primary: #ffffff;
200
208
  --color-text-secondary: #cccccc;
@@ -1,6 +1,5 @@
1
1
  import type { SeverityCode } from '@centreon/ui';
2
2
 
3
- import { Fragment } from 'react';
4
3
  import { Link } from 'react-router';
5
4
  import { makeStyles } from 'tss-react/mui';
6
5
 
@@ -8,29 +7,23 @@ import StatusCounter from './StatusCounter';
8
7
 
9
8
  const useStyles = makeStyles()((theme) => ({
10
9
  container: {
11
- display: 'inline-block',
10
+ alignItems: 'center',
11
+ display: 'flex',
12
+ gap: theme.spacing(1),
12
13
  listStyle: 'none',
13
14
  margin: 0,
14
- padding: 0,
15
- [theme.breakpoints.down(1025)]: {
16
- flexFlow: 'row wrap'
17
- }
15
+ padding: 0
18
16
  },
19
17
  item: {
20
- display: 'inline-block',
18
+ alignItems: 'center',
19
+ display: 'flex',
21
20
  margin: 0,
22
- padding: 0,
23
- paddingRight: theme.spacing(0.25)
21
+ padding: 0
24
22
  },
25
23
  link: {
24
+ alignItems: 'center',
25
+ display: 'flex',
26
26
  textDecoration: 'none'
27
- },
28
- splitter: {
29
- display: 'none',
30
- [theme.breakpoints.down(1025)]: {
31
- display: 'block',
32
- marginBottom: theme.spacing(0.25)
33
- }
34
27
  }
35
28
  }));
36
29
 
@@ -38,6 +31,7 @@ export interface CounterProps {
38
31
  counters: Array<{
39
32
  ariaLabel: string;
40
33
  count: string | number;
34
+ detail?: string | number;
41
35
  onClick: (e: React.MouseEvent) => void;
42
36
  severityCode: SeverityCode;
43
37
  to: string;
@@ -50,20 +44,21 @@ export default ({ counters }: CounterProps): JSX.Element => {
50
44
  return (
51
45
  <ul className={classes.container}>
52
46
  {counters.map(
53
- ({ to, ariaLabel, onClick, count, severityCode }, index) => (
54
- <Fragment key={to.toString().replace(/\W/g, '')}>
55
- {index === 2 && <li className={classes.splitter} />}
56
- <li className={classes.item}>
57
- <Link
58
- aria-label={ariaLabel}
59
- className={classes.link}
60
- onClick={onClick}
61
- to={to}
62
- >
63
- <StatusCounter count={count} severityCode={severityCode} />
64
- </Link>
65
- </li>
66
- </Fragment>
47
+ ({ to, ariaLabel, onClick, count, detail, severityCode }) => (
48
+ <li className={classes.item} key={to.toString().replace(/\W/g, '')}>
49
+ <Link
50
+ aria-label={ariaLabel}
51
+ className={classes.link}
52
+ onClick={onClick}
53
+ to={to}
54
+ >
55
+ <StatusCounter
56
+ count={count}
57
+ detail={detail}
58
+ severityCode={severityCode}
59
+ />
60
+ </Link>
61
+ </li>
67
62
  )
68
63
  )}
69
64
  </ul>
@@ -1,63 +1,72 @@
1
- import { Badge, Tooltip } from '@mui/material';
1
+ import { Tooltip } from '@mui/material';
2
2
 
3
3
  import { getStatusColors, type SeverityCode } from '@centreon/ui';
4
4
 
5
5
  import numeral from 'numeral';
6
+ import { isNil } from 'ramda';
6
7
  import { makeStyles } from 'tss-react/mui';
7
8
 
8
9
  export interface StyleProps {
9
10
  severityCode?: SeverityCode | null;
10
11
  }
11
12
 
12
- const useStyles = makeStyles<StyleProps>()((theme, { severityCode }) => ({
13
- badge: {
14
- background: severityCode
15
- ? getStatusColors({ severityCode, theme })?.backgroundColor
16
- : 'transparent',
17
- borderRadius: theme.spacing(1.25),
18
- color: theme.palette.common.black,
19
- cursor: 'pointer',
20
- fontSize: theme.typography.body2.fontSize,
21
- fontWeight: theme.typography.fontWeightBold,
22
- height: theme.spacing(2.125),
23
- lineHeight: theme.spacing(2.125),
24
- minWidth: theme.spacing(2.125),
25
- position: 'relative',
26
- right: 0,
27
- top: 0,
28
- transform: 'none'
29
- }
30
- }));
13
+ const useStyles = makeStyles<StyleProps>()((theme, { severityCode }) => {
14
+ const statusColors = severityCode
15
+ ? getStatusColors({ severityCode, theme })
16
+ : null;
17
+
18
+ return {
19
+ container: {
20
+ alignItems: 'center',
21
+ color: theme.palette.mode === 'dark' ? '#EAEEF7' : '#1B2233',
22
+ cursor: 'pointer',
23
+ display: 'inline-flex',
24
+ gap: '3px'
25
+ },
26
+ count: {
27
+ fontFamily: 'ui-monospace, "Roboto Mono", Menlo, monospace',
28
+ fontSize: '12px',
29
+ fontWeight: theme.typography.fontWeightMedium,
30
+ lineHeight: 1
31
+ },
32
+ dot: {
33
+ backgroundColor: statusColors?.backgroundColor ?? theme.palette.divider,
34
+ borderRadius: '50%',
35
+ flexShrink: 0,
36
+ height: '9px',
37
+ width: '9px'
38
+ }
39
+ };
40
+ });
31
41
 
32
42
  export interface Props {
33
43
  className?: string;
34
44
  count: string | number;
45
+ detail?: string | number;
35
46
  severityCode?: SeverityCode | null;
36
47
  }
37
48
 
38
49
  const StatusCounter = ({
39
50
  severityCode = null,
40
51
  count,
52
+ detail,
41
53
  className
42
54
  }: Props): JSX.Element => {
43
55
  const { classes, cx } = useStyles({ severityCode });
44
- const shouldDisableTooltip = Number(count) < 1000;
56
+ const hasDetail = !isNil(detail);
57
+ const shouldDisableTooltip = !hasDetail && Number(count) < 1000;
45
58
  const formattedCount = numeral(count).format('0.[0]a');
46
59
 
47
60
  return (
48
61
  <Tooltip
49
62
  disableHoverListener={shouldDisableTooltip}
50
63
  followCursor
51
- title={count}
64
+ title={hasDetail ? detail : count}
52
65
  >
53
- <Badge
54
- badgeContent={formattedCount}
55
- classes={{
56
- badge: cx(classes.badge, className)
57
- }}
58
- max={Number.POSITIVE_INFINITY}
59
- overlap="circular"
60
- />
66
+ <span className={cx(classes.container, className)}>
67
+ <span className={classes.dot} />
68
+ <span className={classes.count}>{formattedCount}</span>
69
+ </span>
61
70
  </Tooltip>
62
71
  );
63
72
  };
@@ -1,79 +1,48 @@
1
1
  import ExpandLessIcon from '@mui/icons-material/ExpandLess';
2
2
  import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
3
3
  import type { SvgIconProps } from '@mui/material';
4
- import { Badge, ClickAwayListener } from '@mui/material';
4
+ import { ClickAwayListener, Tooltip } from '@mui/material';
5
5
 
6
- import { ComponentType, useEffect, useState } from 'react';
6
+ import type { ComponentType, MouseEvent } from 'react';
7
+ import { useEffect, useState } from 'react';
8
+ import { Link } from 'react-router';
7
9
  import { makeStyles } from 'tss-react/mui';
8
10
 
9
11
  import useCloseOnLegacyPage from './useCloseOnLegacyPage';
10
12
 
11
13
  const useStyles = makeStyles()((theme) => ({
12
- button: {
13
- '& > svg': {
14
- height: '0.9em',
15
- margin: `-${theme.spacing(0.5)}`,
16
- [theme.breakpoints.down(1025)]: {
17
- margin: `-${theme.spacing(0.5)}`
18
- }
19
- },
20
- appearance: 'none',
21
- background: 'none',
22
- border: 0,
23
- color: theme.palette.common.white,
24
- cursor: 'pointer',
25
- display: 'flex',
26
-
27
- [theme.breakpoints.up(1025)]: {
28
- alignItems: 'center',
29
- flexFlow: 'row no-wrap',
30
- marginTop: theme.spacing(0.5)
31
- },
32
-
33
- [theme.breakpoints.down(1025)]: {
34
- alignItems: 'center',
35
- flexFlow: 'column wrap',
36
- order: 1
37
- },
38
-
39
- padding: '0'
40
- },
41
14
  container: {
15
+ alignItems: 'center',
16
+ display: 'flex',
42
17
  position: 'relative'
43
18
  },
44
- header: {
45
- [theme.breakpoints.down(1025)]: {
46
- display: 'flex',
47
- flexFlow: 'row no-wrap'
48
- }
49
- },
50
- iconWrapper: {
51
- [theme.breakpoints.up(1025)]: {
52
- position: 'absolute',
53
- top: '0'
54
- }
19
+ icon: {
20
+ '& > svg': {
21
+ display: 'block',
22
+ height: '16px',
23
+ width: '16px'
24
+ },
25
+ alignItems: 'center',
26
+ color: 'inherit',
27
+ display: 'inline-flex',
28
+ textDecoration: 'none'
55
29
  },
56
30
  indicators: {
31
+ alignItems: 'center',
32
+ display: 'inline-flex',
57
33
  lineHeight: 1,
58
34
  [theme.breakpoints.down(600)]: {
59
35
  display: 'none'
60
- },
61
- [theme.breakpoints.down(1025)]: {
62
- flex: 'initial',
63
- marginLeft: theme.spacing(0.5),
64
- order: 2
65
- },
66
- [theme.breakpoints.up(1025)]: {
67
- height: theme.spacing(2.5),
68
- marginLeft: theme.spacing(3.75)
69
36
  }
70
37
  },
71
38
  subMenu: {
72
39
  backgroundColor: theme.palette.background.default,
40
+ borderRadius: theme.spacing(1),
73
41
  boxShadow: theme.shadows[3],
74
42
  boxSizing: 'border-box',
75
43
  left: 0,
76
44
  minWidth: theme.spacing(20),
45
+ overflow: 'hidden',
77
46
  position: 'absolute',
78
47
  textAlign: 'left',
79
48
  top: `calc(100% + ${theme.spacing(1.25)})`,
@@ -83,38 +52,53 @@ const useStyles = makeStyles()((theme) => ({
83
52
  subMenuOpen: {
84
53
  visibility: 'visible'
85
54
  },
86
- textWrapper: {
55
+ tile: {
56
+ '& > svg': {
57
+ height: '16px',
58
+ width: '16px'
59
+ },
60
+ '&:hover': {
61
+ borderColor: theme.palette.tile.borderHover
62
+ },
87
63
  alignItems: 'center',
64
+ appearance: 'none',
65
+ background: theme.palette.tile.background,
66
+ border: `1px solid ${theme.palette.tile.border}`,
67
+ borderRadius: theme.spacing(1),
68
+ color: theme.palette.text.primary,
88
69
  display: 'inline-flex',
89
- flex: '100%',
90
- fontSize: theme.typography.body1.fontSize,
91
- fontWeight: theme.typography.fontWeightBold,
92
- lineHeight: '1',
93
- whiteSpace: 'nowrap',
94
- [theme.breakpoints.down(1025)]: {
95
- display: 'none'
96
- }
70
+ flexFlow: 'row nowrap',
71
+ gap: theme.spacing(1),
72
+ padding: '5px 9px'
73
+ },
74
+ tileButton: {
75
+ cursor: 'pointer'
97
76
  }
98
77
  }));
99
78
 
100
79
  interface TopCounterLayoutProps {
101
80
  Icon: ComponentType<SvgIconProps>;
81
+ iconLink?: string;
82
+ iconOnClick?: (e: MouseEvent<HTMLAnchorElement>) => void;
102
83
  renderIndicators: () => JSX.Element;
103
- renderSubMenu: (params: { closeSubMenu: () => void }) => JSX.Element;
104
- showPendingBadge?: boolean;
84
+ renderSubMenu?: (params: { closeSubMenu: () => void }) => JSX.Element;
105
85
  title: string;
86
+ tooltipDescription?: string;
106
87
  }
107
88
 
108
89
  const TopCounterLayout = ({
109
90
  Icon,
110
91
  title,
92
+ iconLink,
93
+ iconOnClick,
111
94
  renderIndicators,
112
95
  renderSubMenu,
113
- showPendingBadge
96
+ tooltipDescription
114
97
  }: TopCounterLayoutProps): JSX.Element => {
115
98
  const { classes, cx } = useStyles();
116
99
  const [toggled, setToggled] = useState(false);
117
100
  const subMenuId = title.replace(/[^A-Za-z]/, '-');
101
+ const isExpandable = Boolean(renderSubMenu);
118
102
  useCloseOnLegacyPage({ setToggled });
119
103
 
120
104
  useEffect(() => {
@@ -137,6 +121,38 @@ const TopCounterLayout = ({
137
121
  };
138
122
  }, [toggled]);
139
123
 
124
+ const iconWithTooltip = (
125
+ <Tooltip
126
+ disableInteractive
127
+ enterDelay={500}
128
+ enterNextDelay={500}
129
+ placement="bottom"
130
+ title={tooltipDescription ?? title}
131
+ >
132
+ <Icon />
133
+ </Tooltip>
134
+ );
135
+
136
+ if (!isExpandable) {
137
+ return (
138
+ <div className={classes.tile}>
139
+ {iconLink ? (
140
+ <Link
141
+ aria-label={title}
142
+ className={classes.icon}
143
+ onClick={iconOnClick}
144
+ to={iconLink}
145
+ >
146
+ {iconWithTooltip}
147
+ </Link>
148
+ ) : (
149
+ <span className={classes.icon}>{iconWithTooltip}</span>
150
+ )}
151
+ <span className={classes.indicators}>{renderIndicators()}</span>
152
+ </div>
153
+ );
154
+ }
155
+
140
156
  return (
141
157
  <ClickAwayListener
142
158
  onClickAway={(): void => {
@@ -147,40 +163,27 @@ const TopCounterLayout = ({
147
163
  }}
148
164
  >
149
165
  <div className={classes.container}>
150
- <div className={classes.header}>
151
- <div className={classes.indicators}>{renderIndicators()}</div>
152
- <button
153
- aria-controls={`${subMenuId}-menu`}
154
- aria-expanded={toggled}
155
- aria-haspopup="true"
156
- aria-label={title}
157
- className={classes.button}
158
- id={`${subMenuId}-button`}
159
- onClick={(): void => setToggled(!toggled)}
160
- type="button"
161
- >
162
- <span className={classes.iconWrapper}>
163
- <Badge
164
- anchorOrigin={{ horizontal: 'right', vertical: 'top' }}
165
- color="pending"
166
- invisible={!showPendingBadge}
167
- overlap="circular"
168
- variant="dot"
169
- >
170
- <Icon />
171
- </Badge>
172
- </span>
173
- <span className={classes.textWrapper}>{title}</span>
174
- {toggled ? <ExpandLessIcon /> : <ExpandMoreIcon />}
175
- </button>
176
- </div>
166
+ <button
167
+ aria-controls={`${subMenuId}-menu`}
168
+ aria-expanded={toggled}
169
+ aria-haspopup="true"
170
+ aria-label={title}
171
+ className={cx(classes.tile, classes.tileButton)}
172
+ id={`${subMenuId}-button`}
173
+ onClick={(): void => setToggled(!toggled)}
174
+ type="button"
175
+ >
176
+ <span className={classes.icon}>{iconWithTooltip}</span>
177
+ <span className={classes.indicators}>{renderIndicators()}</span>
178
+ {toggled ? <ExpandLessIcon /> : <ExpandMoreIcon />}
179
+ </button>
177
180
  <div
178
181
  aria-labelledby={`${subMenuId}-button`}
179
182
  className={cx(classes.subMenu, { [classes.subMenuOpen]: toggled })}
180
183
  id={`${subMenuId}-menu`}
181
184
  role="menu"
182
185
  >
183
- {renderSubMenu({ closeSubMenu: () => setToggled(false) })}
186
+ {renderSubMenu?.({ closeSubMenu: () => setToggled(false) })}
184
187
  </div>
185
188
  </div>
186
189
  </ClickAwayListener>
@@ -1,6 +1,4 @@
1
1
  export * from './formaters';
2
2
  export type { CounterProps } from './ResourceCounters';
3
3
  export { default as TopCounterResourceCounters } from './ResourceCounters';
4
- export type { SubMenuProps } from './ResourceSubMenu';
5
- export { default as TopCounterResourceSubMenu } from './ResourceSubMenu';
6
4
  export { default as TopCounterLayout } from './TopCounterLayout';
@@ -1,110 +0,0 @@
1
- import { List, ListItem } from '@mui/material';
2
-
3
- import { getStatusColors, type SeverityCode } from '@centreon/ui';
4
- import { ThemeMode } from '@centreon/ui-context';
5
-
6
- import { equals } from 'ramda';
7
- import { Link } from 'react-router';
8
- import { makeStyles } from 'tss-react/mui';
9
-
10
- const useStyles = makeStyles()((theme) => ({
11
- count: {
12
- marginLeft: 'auto'
13
- },
14
- link: {
15
- alignItems: 'center',
16
- color: 'inherit',
17
- display: 'flex',
18
- flex: '100%',
19
- padding: `0 ${theme.spacing(1)}`,
20
- textDecoration: 'none'
21
- },
22
- status: {
23
- alignItems: 'center',
24
- display: 'flex'
25
- },
26
- statusCounter: {
27
- borderRadius: '50%',
28
- height: theme.spacing(1),
29
- marginRight: theme.spacing(1),
30
- width: theme.spacing(1)
31
- },
32
- submenu: {
33
- fontSize: theme.typography.body2.fontSize,
34
- padding: 0
35
- },
36
- submenuItem: {
37
- '&:hover': {
38
- background: equals(theme.palette.mode, ThemeMode.dark)
39
- ? theme.palette.primary.dark
40
- : theme.palette.primary.light,
41
- color: equals(theme.palette.mode, ThemeMode.dark)
42
- ? theme.palette.common.white
43
- : theme.palette.primary.main
44
- },
45
- '&:not(:last-child)': {
46
- borderBottom: `1px solid ${theme.palette.divider}`
47
- }
48
- }
49
- }));
50
-
51
- export interface SubMenuProps {
52
- items: Array<{
53
- countTestId?: string;
54
- onClick: (e: React.MouseEvent) => void;
55
- severityCode: SeverityCode;
56
- submenuCount: string | number;
57
- submenuTitle: string;
58
- to: string;
59
- }>;
60
- onClose?: () => void;
61
- }
62
-
63
- const SubMenu = ({ items, onClose }: SubMenuProps): JSX.Element => {
64
- const { classes, theme } = useStyles();
65
-
66
- return (
67
- <List className={classes.submenu}>
68
- {items.map(
69
- ({
70
- onClick,
71
- severityCode,
72
- submenuTitle,
73
- submenuCount,
74
- countTestId,
75
- to
76
- }) => (
77
- <ListItem
78
- className={classes.submenuItem}
79
- disableGutters
80
- key={to}
81
- onClick={onClose}
82
- >
83
- <Link
84
- className={classes.link}
85
- onClick={onClick}
86
- role="menuitem"
87
- to={to}
88
- >
89
- <span className={classes.status}>
90
- <span
91
- className={classes.statusCounter}
92
- style={{
93
- backgroundColor: getStatusColors({ severityCode, theme })
94
- ?.backgroundColor
95
- }}
96
- />
97
- <span>{submenuTitle}</span>
98
- </span>
99
- <span className={classes.count} data-testid={countTestId}>
100
- {submenuCount}
101
- </span>
102
- </Link>
103
- </ListItem>
104
- )
105
- )}
106
- </List>
107
- );
108
- };
109
-
110
- export default SubMenu;