@centreon/ui 25.10.28 → 25.10.30

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.
Files changed (73) hide show
  1. package/package.json +3 -3
  2. package/public/mockServiceWorker.js +8 -31
  3. package/src/Dashboard/Item.tsx +142 -149
  4. package/src/Form/Inputs/ConnectedAutocomplete.tsx +2 -0
  5. package/src/Form/Inputs/Grid.tsx +2 -3
  6. package/src/Form/Inputs/models.ts +2 -0
  7. package/src/Form/Section/navigateToSection.ts +6 -6
  8. package/src/Graph/BarChart/BarChart.cypress.spec.tsx +55 -102
  9. package/src/Graph/BarChart/BarChart.stories.tsx +1 -51
  10. package/src/Graph/BarChart/BarChart.tsx +1 -1
  11. package/src/Graph/BarChart/BarGroup.tsx +32 -22
  12. package/src/Graph/BarChart/BarStack.tsx +26 -15
  13. package/src/Graph/BarChart/MemoizedGroup.tsx +18 -14
  14. package/src/Graph/BarChart/ResponsiveBarChart.tsx +6 -7
  15. package/src/Graph/BarChart/Tooltip/BarChartTooltip.tsx +2 -2
  16. package/src/Graph/Chart/BasicComponents/Lines/StackedLines/index.tsx +5 -4
  17. package/src/Graph/Chart/BasicComponents/Lines/StackedLines/useStackedLines.ts +45 -18
  18. package/src/Graph/Chart/BasicComponents/Lines/index.tsx +118 -124
  19. package/src/Graph/Chart/Chart.cypress.spec.tsx +6 -25
  20. package/src/Graph/Chart/Chart.stories.tsx +1 -43
  21. package/src/Graph/Chart/Chart.tsx +13 -9
  22. package/src/Graph/Chart/InteractiveComponents/GraphValueTooltip/useGraphValueTooltip.ts +4 -2
  23. package/src/Graph/Chart/Legend/index.tsx +2 -26
  24. package/src/Graph/Chart/index.tsx +45 -31
  25. package/src/Graph/Chart/models.ts +1 -6
  26. package/src/Graph/Chart/translatedLabels.ts +1 -0
  27. package/src/Graph/Chart/useChartData.ts +24 -3
  28. package/src/Graph/common/Axes/index.tsx +1 -1
  29. package/src/Graph/common/BaseChart/BaseChart.tsx +1 -6
  30. package/src/Graph/common/BaseChart/ChartSvgWrapper.tsx +23 -16
  31. package/src/Graph/common/Error/NoData.tsx +18 -0
  32. package/src/Graph/common/Grids/index.tsx +4 -4
  33. package/src/Graph/common/timeSeries/index.test.ts +4 -24
  34. package/src/Graph/common/timeSeries/index.ts +120 -184
  35. package/src/Graph/common/timeSeries/models.ts +1 -1
  36. package/src/Graph/common/utils.ts +4 -10
  37. package/src/Graph/index.ts +20 -20
  38. package/src/InputField/Select/Autocomplete/Connected/index.tsx +9 -9
  39. package/src/InputField/Select/Autocomplete/Multi/Listbox.tsx +11 -3
  40. package/src/InputField/Select/Autocomplete/Multi/Multi.tsx +3 -2
  41. package/src/InputField/Select/Autocomplete/index.tsx +28 -17
  42. package/src/InputField/Select/Option.tsx +3 -3
  43. package/src/InputField/Select/index.tsx +2 -1
  44. package/src/Listing/ActionBar/Pagination.tsx +2 -1
  45. package/src/Listing/ActionBar/index.tsx +113 -8
  46. package/src/Listing/index.tsx +19 -2
  47. package/src/Listing/translatedLabels.ts +4 -0
  48. package/src/Module/index.tsx +2 -8
  49. package/src/ThemeProvider/index.tsx +21 -30
  50. package/src/ThemeProvider/tailwindcss.css +12 -10
  51. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/PickersStartEndDate.tsx +8 -5
  52. package/src/TimePeriods/CustomTimePeriod/PopoverCustomTimePeriod/models.ts +0 -1
  53. package/src/api/useGraphQuery/index.ts +7 -4
  54. package/src/components/Form/AccessRights/ShareInput/ShareInput.tsx +4 -3
  55. package/src/components/Form/AccessRights/ShareInput/useShareInput.tsx +15 -10
  56. package/src/components/Layout/PageLayout/PageLayout.tsx +3 -9
  57. package/src/components/Layout/PageLayout/PageLayoutActions.tsx +3 -5
  58. package/src/components/Layout/PageLayout/PageLayoutBody.tsx +3 -5
  59. package/src/components/Layout/PageLayout/PageLayoutHeader.tsx +3 -5
  60. package/src/components/Layout/PageLayout/PageQuickAccess.tsx +44 -25
  61. package/src/components/Layout/PageLayout/translatedLabels.ts +1 -0
  62. package/src/components/Menu/Button/MenuButton.styles.ts +27 -0
  63. package/src/components/Menu/Button/MenuButton.tsx +5 -8
  64. package/src/components/Menu/Menu.styles.ts +68 -0
  65. package/src/components/Menu/MenuDivider.tsx +5 -1
  66. package/src/components/Menu/MenuItem.tsx +5 -1
  67. package/src/components/Menu/MenuItems.tsx +4 -5
  68. package/src/components/Modal/Modal.styles.ts +2 -1
  69. package/src/components/Modal/ModalHeader.tsx +1 -5
  70. package/src/utils/useInfiniteScrollListing.ts +11 -7
  71. package/src/utils/useViewportIntersection.ts +3 -4
  72. package/src/Graph/mockedData/dataWithMissingPoint.json +0 -74
  73. package/src/Graph/mockedData/pingServiceWithStackedKeys.json +0 -205
@@ -37,6 +37,7 @@ interface UseMetricsQueryProps {
37
37
  timePeriodType: number;
38
38
  };
39
39
  isEnabled?: boolean;
40
+ enforceIsEnabled?: boolean;
40
41
  }
41
42
 
42
43
  interface UseMetricsQueryState {
@@ -103,7 +104,8 @@ const useGraphQuery = ({
103
104
  refreshCount,
104
105
  bypassQueryParams = false,
105
106
  prefix,
106
- isEnabled = true
107
+ isEnabled = true,
108
+ enforceIsEnabled
107
109
  }: UseMetricsQueryProps): UseMetricsQueryState => {
108
110
  const timePeriodToUse = equals(timePeriod?.timePeriodType, -1)
109
111
  ? {
@@ -161,9 +163,10 @@ const useGraphQuery = ({
161
163
  ],
162
164
  queryOptions: {
163
165
  enabled:
164
- areResourcesFullfilled(resources) &&
165
- !isEmpty(definedMetrics) &&
166
- isEnabled,
166
+ enforceIsEnabled ??
167
+ (areResourcesFullfilled(resources) &&
168
+ !isEmpty(definedMetrics) &&
169
+ isEnabled),
167
170
  refetchInterval: refreshInterval,
168
171
  suspense: false
169
172
  },
@@ -8,6 +8,7 @@ import { SelectEntry, SingleConnectedAutocompleteField } from '../../../..';
8
8
  import RoleSelectField from '../common/RoleSelectField';
9
9
  import { Endpoints, Labels } from '../models';
10
10
 
11
+ import { ReactElement } from 'react';
11
12
  import ContactSwitch from './ContactSwitch';
12
13
  import { useShareInputStyles } from './ShareInput.styles';
13
14
  import useShareInput from './useShareInput';
@@ -18,18 +19,18 @@ interface Props {
18
19
  roles: Array<SelectEntry>;
19
20
  }
20
21
 
21
- const ShareInput = ({ labels, endpoints, roles }: Props): JSX.Element => {
22
+ const ShareInput = ({ labels, endpoints, roles }: Props): ReactElement => {
22
23
  const { t } = useTranslation();
23
24
  const { classes } = useShareInputStyles();
24
25
 
25
26
  const {
26
- renderOption,
27
27
  selectedContact,
28
28
  getOptionDisabled,
29
29
  getEndpoint,
30
30
  selectContact,
31
31
  isContactGroup,
32
32
  selectedRole,
33
+ getRenderedOptionText,
33
34
  setSelectedRole,
34
35
  add,
35
36
  changeIdValue
@@ -46,6 +47,7 @@ const ShareInput = ({ labels, endpoints, roles }: Props): JSX.Element => {
46
47
  disableClearable={false}
47
48
  field="name"
48
49
  getEndpoint={getEndpoint}
50
+ getRenderedOptionText={getRenderedOptionText}
49
51
  getOptionDisabled={getOptionDisabled}
50
52
  label={t(
51
53
  isContactGroup
@@ -53,7 +55,6 @@ const ShareInput = ({ labels, endpoints, roles }: Props): JSX.Element => {
53
55
  : t(labels.autocompleteContact)
54
56
  )}
55
57
  queryKey={isContactGroup ? labels.contactGroup : labels.contact}
56
- renderOption={renderOption}
57
58
  value={selectedContact}
58
59
  onChange={selectContact}
59
60
  />
@@ -1,10 +1,15 @@
1
- import { Dispatch, SetStateAction, useEffect, useState } from 'react';
1
+ import {
2
+ Dispatch,
3
+ ReactElement,
4
+ SetStateAction,
5
+ useEffect,
6
+ useState
7
+ } from 'react';
2
8
 
3
9
  import { useAtomValue, useSetAtom } from 'jotai';
4
10
  import { equals, includes, isNil } from 'ramda';
5
11
 
6
12
  import CheckCircleIcon from '@mui/icons-material/CheckCircle';
7
- import { ListItemText, MenuItem } from '@mui/material';
8
13
 
9
14
  import { SelectEntry, buildListingEndpoint } from '../../../..';
10
15
  import {
@@ -20,7 +25,7 @@ interface UseShareInputState {
20
25
  getEndpoint: (parameters) => string;
21
26
  getOptionDisabled: (option) => boolean;
22
27
  isContactGroup: boolean;
23
- renderOption: (attr, option) => JSX.Element;
28
+ getRenderedOptionText: (option: unknown) => ReactElement | string;
24
29
  selectContact: (_, entry) => void;
25
30
  selectedContact: AccessRightInitialValues | null;
26
31
  selectedRole: string;
@@ -40,7 +45,7 @@ const useShareInput = (endpoints: Endpoints): UseShareInputState => {
40
45
 
41
46
  const selectContact = (_, entry): void => {
42
47
  setSelectedContact(entry);
43
- if (equals('editor', entry.most_permissive_role)) {
48
+ if (equals('editor', entry?.most_permissive_role)) {
44
49
  return;
45
50
  }
46
51
  setSelectedRole('viewer');
@@ -71,14 +76,14 @@ const useShareInput = (endpoints: Endpoints): UseShareInputState => {
71
76
  }
72
77
  });
73
78
 
74
- const renderOption = (attr, option): JSX.Element => {
79
+ const getRenderedOptionText = (option): ReactElement => {
75
80
  return (
76
- <MenuItem {...attr}>
77
- <ListItemText>{option.name}</ListItemText>
78
- {includes(option.id, accessRightIds) && (
81
+ <>
82
+ {option?.name}
83
+ {includes(option?.id, accessRightIds) && (
79
84
  <CheckCircleIcon color="success" />
80
85
  )}
81
- </MenuItem>
86
+ </>
82
87
  );
83
88
  };
84
89
 
@@ -102,7 +107,7 @@ const useShareInput = (endpoints: Endpoints): UseShareInputState => {
102
107
  getEndpoint,
103
108
  getOptionDisabled,
104
109
  isContactGroup,
105
- renderOption,
110
+ getRenderedOptionText,
106
111
  selectContact,
107
112
  selectedContact,
108
113
  selectedRole,
@@ -5,22 +5,16 @@ import { useStyles } from './PageLayout.styles';
5
5
  type PageLayoutProps = {
6
6
  children: Array<ReactNode> | ReactNode;
7
7
  variant?: 'default' | 'fixed-header';
8
- className?: string;
9
8
  };
10
9
 
11
10
  export const PageLayout = ({
12
11
  children,
13
- variant = 'default',
14
- className
12
+ variant = 'default'
15
13
  }: PageLayoutProps): ReactElement => {
16
- const { classes, cx } = useStyles();
14
+ const { classes } = useStyles();
17
15
 
18
16
  return (
19
- <section
20
- className={cx(classes.pageLayout, className)}
21
- data-variant={variant}
22
- id="page"
23
- >
17
+ <section className={classes.pageLayout} data-variant={variant} id="page">
24
18
  {children}
25
19
  </section>
26
20
  );
@@ -5,19 +5,17 @@ import { useStyles } from './PageLayout.styles';
5
5
  interface PageLayoutActionsProps {
6
6
  children: Array<ReactNode> | ReactNode;
7
7
  rowReverse?: boolean;
8
- className?: string;
9
8
  }
10
9
 
11
10
  export const PageLayoutActions = ({
12
11
  children,
13
- rowReverse,
14
- className
12
+ rowReverse
15
13
  }: PageLayoutActionsProps): ReactElement => {
16
- const { classes, cx } = useStyles();
14
+ const { classes } = useStyles();
17
15
 
18
16
  return (
19
17
  <section
20
- className={cx(classes.pageLayoutActions, className)}
18
+ className={classes.pageLayoutActions}
21
19
  data-row-reverse={rowReverse}
22
20
  id="actions"
23
21
  >
@@ -5,19 +5,17 @@ import { useStyles } from './PageLayout.styles';
5
5
  type PageLayoutBodyProps = {
6
6
  children: Array<ReactNode> | ReactNode;
7
7
  hasBackground?: boolean;
8
- className?: string;
9
8
  };
10
9
 
11
10
  export const PageLayoutBody = ({
12
11
  children,
13
- hasBackground = false,
14
- className
12
+ hasBackground = false
15
13
  }: PageLayoutBodyProps): ReactElement => {
16
- const { classes, cx } = useStyles();
14
+ const { classes } = useStyles();
17
15
 
18
16
  return (
19
17
  <section
20
- className={cx(classes.pageLayoutBody, className)}
18
+ className={classes.pageLayoutBody}
21
19
  data-has-background={hasBackground}
22
20
  data-has-actions={!!children?.length}
23
21
  id="page-body"
@@ -4,17 +4,15 @@ import { useStyles } from './PageLayout.styles';
4
4
 
5
5
  type PageLayoutHeaderProps = {
6
6
  children: Array<ReactNode> | ReactNode;
7
- className?: string;
8
7
  };
9
8
 
10
9
  export const PageLayoutHeader = ({
11
- children,
12
- className
10
+ children
13
11
  }: PageLayoutHeaderProps): JSX.Element => {
14
- const { classes, cx } = useStyles();
12
+ const { classes } = useStyles();
15
13
 
16
14
  return (
17
- <header className={cx(classes.pageLayoutHeader, className)} id="header">
15
+ <header className={classes.pageLayoutHeader} id="header">
18
16
  {children}
19
17
  </header>
20
18
  );
@@ -2,31 +2,37 @@ import { useTranslation } from 'react-i18next';
2
2
 
3
3
  import AddIcon from '@mui/icons-material/Add';
4
4
  import ArrowBackIcon from '@mui/icons-material/ArrowBack';
5
+ import { CircularProgress } from '@mui/material';
5
6
 
6
7
  import { Button, Menu } from '../..';
7
8
 
9
+ import { labelLoading } from './translatedLabels';
10
+
8
11
  interface NamedEntity {
9
12
  id: number | string;
10
13
  name: string;
11
14
  }
12
15
 
13
16
  type Props = {
14
- create?: () => void;
17
+ create: () => void;
15
18
  elements: Array<NamedEntity>;
16
19
  goBack: () => void;
17
20
  isActive: (id: number | string) => boolean;
18
- isDisabled?: (id: number | string) => boolean;
21
+ isLoading?: boolean;
19
22
  labels: {
20
23
  create: string;
21
24
  goBack: string;
22
25
  };
26
+ // Observed at the bottom of the list to load the next page when it is reached
27
+ loadMoreRef?: (node: Element | null) => void;
23
28
  navigateToElement: (id: number | string) => () => void;
24
29
  };
25
30
 
26
31
  export const PageQuickAccess = ({
27
32
  elements,
28
33
  isActive,
29
- isDisabled,
34
+ isLoading,
35
+ loadMoreRef,
30
36
  navigateToElement,
31
37
  goBack,
32
38
  create,
@@ -38,27 +44,40 @@ export const PageQuickAccess = ({
38
44
  <Menu>
39
45
  <Menu.Button data-testid="quickaccess" />
40
46
  <Menu.Items>
41
- {elements?.map((element) => (
42
- <Menu.Item
43
- key={`${element.id}`}
44
- onClick={navigateToElement(element.id)}
45
- isActive={isActive(element.id)}
46
- isDisabled={isDisabled?.(element.id)}
47
- >
48
- {element.name}
49
- </Menu.Item>
50
- ))}
47
+ <div
48
+ className="max-h-[50vh] overflow-y-auto"
49
+ data-testid="quickaccess-elements"
50
+ >
51
+ {elements?.map((element) => (
52
+ <Menu.Item
53
+ key={`${element.id}`}
54
+ onClick={navigateToElement(element.id)}
55
+ {...(isActive(element.id) && {
56
+ isActive: true,
57
+ isDisabled: true
58
+ })}
59
+ >
60
+ {element.name}
61
+ </Menu.Item>
62
+ ))}
63
+ {loadMoreRef && <div className="h-px" ref={loadMoreRef} />}
64
+ {isLoading && (
65
+ <div className="flex justify-center py-2">
66
+ <CircularProgress aria-label={t(labelLoading)} size={20} />
67
+ </div>
68
+ )}
69
+ </div>
51
70
  <Menu.Divider key="divider" />
52
- <div className="px-2 pb-2 flex gap-4">
53
- <Button
54
- icon={<ArrowBackIcon />}
55
- iconVariant="start"
56
- variant="ghost"
57
- onClick={goBack}
58
- >
59
- {t(labels.goBack)}
60
- </Button>
61
- {create && (
71
+ <Menu.Item key="create">
72
+ <>
73
+ <Button
74
+ icon={<ArrowBackIcon />}
75
+ iconVariant="start"
76
+ variant="ghost"
77
+ onClick={goBack}
78
+ >
79
+ {t(labels.goBack)}
80
+ </Button>
62
81
  <Button
63
82
  icon={<AddIcon />}
64
83
  iconVariant="start"
@@ -67,8 +86,8 @@ export const PageQuickAccess = ({
67
86
  >
68
87
  {t(labels.create)}
69
88
  </Button>
70
- )}
71
- </div>
89
+ </>
90
+ </Menu.Item>
72
91
  </Menu.Items>
73
92
  </Menu>
74
93
  );
@@ -0,0 +1 @@
1
+ export const labelLoading = 'Loading...';
@@ -0,0 +1,27 @@
1
+ import { makeStyles } from 'tss-react/mui';
2
+
3
+ export const useStyles = makeStyles()((theme) => ({
4
+ buttonIcon: {
5
+ '[data-is-active="true"] &': {
6
+ transform: 'rotate(180deg)'
7
+ },
8
+
9
+ fontSize: '1.25rem',
10
+ transition: 'transform 0.15s ease-in-out'
11
+ },
12
+ menuButton: {
13
+ '&:hover:not(&[data-is-active="true"])': {
14
+ backgroundColor: theme.palette.menu.button.background.hover,
15
+ color: theme.palette.menu.button.color.hover
16
+ },
17
+ '&[data-is-active="true"]': {
18
+ backgroundColor: theme.palette.menu.button.background.active,
19
+ color: theme.palette.menu.button.color.active
20
+ },
21
+ backgroundColor: theme.palette.menu.button.background.default,
22
+ color: theme.palette.menu.button.color.default,
23
+ display: 'flex',
24
+ height: 'unset',
25
+ minWidth: 'unset'
26
+ }
27
+ }));
@@ -5,13 +5,12 @@ import {
5
5
  Menu as MenuIcon
6
6
  } from '@mui/icons-material';
7
7
 
8
+ import { useStyles } from './MenuButton.styles';
8
9
  import { AriaLabelingAttributes } from '../../../@types/aria-attributes';
9
10
  import { DataTestAttributes } from '../../../@types/data-attributes';
10
11
  import { Button, ButtonProps } from '../../Button';
11
12
  import { useMenu } from '../useMenu';
12
13
 
13
- import { useStyles } from './MenuButton.styles';
14
-
15
14
  type MenuButtonProps = {
16
15
  ariaLabel?: string;
17
16
  children?: ReactNode;
@@ -33,6 +32,8 @@ const MenuButton = ({
33
32
  className,
34
33
  ...attr
35
34
  }: MenuButtonProps): ReactElement => {
35
+ const { cx, classes } = useStyles();
36
+
36
37
  const { isMenuOpen, setIsMenuOpen, setAnchorEl, onOpen } = useMenu();
37
38
 
38
39
  const onToggle = useCallback(
@@ -50,18 +51,14 @@ const MenuButton = ({
50
51
  <Button
51
52
  {...attr}
52
53
  aria-label={ariaLabel}
53
- className={`${isMenuOpen ? 'bg-primary-main/8 text-text-primary-main' : 'text-text-secondary'} ${className}`}
54
+ className={cx(classes.menuButton, className)}
54
55
  data-is-active={isMenuOpen}
55
56
  size={size}
56
57
  variant={variant}
57
58
  onClick={onToggle}
58
59
  >
59
60
  {children || <MenuIcon />}
60
- {hasArrow && (
61
- <ArrowDropDownIcon
62
- className={`transform-gpu transition-[rotate] ${isMenuOpen ? 'rotate-180' : 'rotate-0'}`}
63
- />
64
- )}
61
+ {hasArrow && <ArrowDropDownIcon className={classes.buttonIcon} />}
65
62
  </Button>
66
63
  );
67
64
  };
@@ -0,0 +1,68 @@
1
+ import { makeStyles } from 'tss-react/mui';
2
+
3
+ export const useStyles = makeStyles()((theme) => ({
4
+ menuDivider: {
5
+ '&, &.MuiDivider-root': {
6
+ borderColor: theme.palette.menu.divider.border
7
+ }
8
+ },
9
+ menuItem: {
10
+ '&, &.MuiMenuItem-root': {
11
+ '& > button': {
12
+ fontSize: '1rem',
13
+ justifyContent: 'flex-start'
14
+ },
15
+
16
+ '&:has(button)': {
17
+ '& > .MuiTouchRipple-root': {
18
+ display: 'none'
19
+ },
20
+ '&:hover:not(&[data-is-active="true"])': {
21
+ backgroundColor: 'unset'
22
+ },
23
+ padding: theme.spacing(0, 2)
24
+ },
25
+
26
+ '&:not(:has(button))': {
27
+ '&:hover:not(&[data-is-active="true"])': {
28
+ backgroundColor: theme.palette.menu.item.background.hover,
29
+ color: theme.palette.menu.item.color.hover
30
+ },
31
+ '&[data-is-active="true"], &.Mui-selected': {
32
+ '&[data-is-disabled="true"], &.Mui-disabled': {
33
+ opacity: 1
34
+ },
35
+ backgroundColor: theme.palette.menu.item.background.active,
36
+ color: theme.palette.menu.item.color.active,
37
+ opacity: 1
38
+ },
39
+ '&[data-is-disabled="true"], &.Mui-disabled': {
40
+ opacity: 0.5
41
+ },
42
+ backgroundColor: theme.palette.menu.item.background.default,
43
+ color: theme.palette.menu.item.color.default,
44
+ padding: theme.spacing(0.75, 2)
45
+ },
46
+
47
+ alignItems: 'center',
48
+ display: 'flex',
49
+ flexDirection: 'row',
50
+ fontSize: '1rem',
51
+ gap: theme.spacing(2),
52
+ justifyContent: 'space-between',
53
+ minHeight: 'unset'
54
+ }
55
+ },
56
+ menuItems: {
57
+ '& > .MuiPaper-root.MuiMenu-paper': {
58
+ backgroundColor: theme.palette.menu.background,
59
+ borderRadius: '4px',
60
+ boxShadow: theme.shadows[8],
61
+ minWidth: '240px',
62
+ transform: `translateY(${theme.spacing(0.5)}) !important`
63
+ },
64
+ '& ul, & ul.MuiMenu-list': {
65
+ padding: theme.spacing(1, 0)
66
+ }
67
+ }
68
+ }));
@@ -2,8 +2,12 @@ import { ReactElement } from 'react';
2
2
 
3
3
  import { Divider as MuiDivider } from '@mui/material';
4
4
 
5
+ import { useStyles } from './Menu.styles';
6
+
5
7
  const MenuDivider = (): ReactElement => {
6
- return <MuiDivider />;
8
+ const { classes } = useStyles();
9
+
10
+ return <MuiDivider className={classes.menuDivider} />;
7
11
  };
8
12
 
9
13
  export { MenuDivider };
@@ -2,6 +2,8 @@ import { ReactElement, ReactNode } from 'react';
2
2
 
3
3
  import { MenuItem as MuiMenuItem } from '@mui/material';
4
4
 
5
+ import { useStyles } from './Menu.styles';
6
+
5
7
  type MenuItemProps = {
6
8
  children?: ReactNode;
7
9
  className?: string;
@@ -17,9 +19,11 @@ const MenuItem = ({
17
19
  isDisabled = false,
18
20
  className
19
21
  }: MenuItemProps): ReactElement => {
22
+ const { cx, classes } = useStyles();
23
+
20
24
  return (
21
25
  <MuiMenuItem
22
- className={className}
26
+ className={cx(classes.menuItem, className)}
23
27
  data-is-active={isActive}
24
28
  data-is-disabled={isDisabled}
25
29
  disabled={isDisabled}
@@ -2,6 +2,7 @@ import { ReactElement, ReactNode } from 'react';
2
2
 
3
3
  import { Menu as MuiMenu } from '@mui/material';
4
4
 
5
+ import { useStyles } from './Menu.styles';
5
6
  import { useMenu } from './useMenu';
6
7
 
7
8
  type MenuItemsProps = {
@@ -9,6 +10,8 @@ type MenuItemsProps = {
9
10
  className?: string;
10
11
  };
11
12
  const MenuItems = ({ children, className }: MenuItemsProps): ReactElement => {
13
+ const { cx, classes } = useStyles();
14
+
12
15
  const { isMenuOpen, setIsMenuOpen, anchorEl, onClose } = useMenu();
13
16
 
14
17
  const onCloseMenu = (): void => {
@@ -19,11 +22,7 @@ const MenuItems = ({ children, className }: MenuItemsProps): ReactElement => {
19
22
  return (
20
23
  <MuiMenu
21
24
  anchorEl={anchorEl}
22
- className={className}
23
- classes={{
24
- paper:
25
- 'rounded-sm shadow-lg min-w-[240px] bg-background-paper border-1 border-divider'
26
- }}
25
+ className={cx(classes.menuItems, className)}
27
26
  open={isMenuOpen}
28
27
  variant="menu"
29
28
  onClick={onCloseMenu}
@@ -72,7 +72,8 @@ const useStyles = makeStyles<{
72
72
  opacity: 0.6
73
73
  },
74
74
  top: theme.spacing(1)
75
- }
75
+ },
76
+
76
77
  }));
77
78
 
78
79
  export { useStyles };
@@ -16,11 +16,7 @@ const ModalHeader = ({
16
16
  }: ModalHeaderProps & DialogTitleProps): ReactElement => {
17
17
  return (
18
18
  <div className={modalHeader}>
19
- <MuiDialogTitle
20
- className="p-0 font-bold text-2xl"
21
- color="primary"
22
- {...rest}
23
- >
19
+ <MuiDialogTitle className="p-0 font-bold text-2xl" color="primary" {...rest}>
24
20
  {children}
25
21
  </MuiDialogTitle>
26
22
  </div>
@@ -50,6 +50,15 @@ export const useInfiniteScrollListing = <T>({
50
50
 
51
51
  const [page, setPage] = useAtom(pageAtom);
52
52
 
53
+ // Shared by the active query and the next page prefetch, so that the
54
+ // prefetched page is reused instead of being requested again
55
+ const getQueryKey = (queryPage: number): Array<string | number> => [
56
+ queryKeyName,
57
+ queryPage,
58
+ JSON.stringify(parameters),
59
+ JSON.stringify(customQueryParameters)
60
+ ];
61
+
53
62
  const { data, isLoading, prefetchNextPage, fetchStatus } = useFetchQuery<
54
63
  Listing<T>
55
64
  >({
@@ -60,12 +69,7 @@ export const useInfiniteScrollListing = <T>({
60
69
  customQueryParameters,
61
70
  parameters: { limit, page: params?.page || page, ...parameters }
62
71
  }),
63
- getQueryKey: () => [
64
- queryKeyName,
65
- page,
66
- JSON.stringify(parameters),
67
- JSON.stringify(customQueryParameters)
68
- ],
72
+ getQueryKey: () => getQueryKey(page),
69
73
  isPaginated: true,
70
74
  queryOptions: {
71
75
  enabled,
@@ -111,7 +115,7 @@ export const useInfiniteScrollListing = <T>({
111
115
  }
112
116
 
113
117
  prefetchNextPage({
114
- getPrefetchQueryKey: (newPage) => ['dashboards', newPage],
118
+ getPrefetchQueryKey: getQueryKey,
115
119
  page
116
120
  });
117
121
  }, [data]);
@@ -18,10 +18,9 @@ export const useViewportIntersection = (
18
18
  observer.current.disconnect();
19
19
  }
20
20
 
21
- observer.current = new window.IntersectionObserver(
22
- ([newEntry]) => setEntry(newEntry),
23
- options
24
- );
21
+ observer.current = new window.IntersectionObserver(([newEntry]) => {
22
+ setEntry(newEntry);
23
+ }, options);
25
24
 
26
25
  if (element) {
27
26
  observer.current.observe(element);