@centreon/ui 23.10.32 → 23.10.34-MON-19940-Add-testIds-cloud-notifications.0

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 (90) hide show
  1. package/package.json +12 -2
  2. package/src/Dashboard/DashboardLayout.stories.tsx +3 -5
  3. package/src/Dashboard/Layout.tsx +4 -4
  4. package/src/Dashboard/utils.ts +28 -28
  5. package/src/Dialog/index.tsx +20 -1
  6. package/src/ThemeProvider/index.tsx +6 -5
  7. package/src/ThemeProvider/palettes.ts +389 -215
  8. package/src/api/TestQueryProvider.tsx +2 -1
  9. package/src/api/useFetchQuery/index.ts +8 -7
  10. package/src/api/useMutationQuery/index.ts +11 -10
  11. package/src/base/tokens/README.md +3 -0
  12. package/src/base/tokens/source/$metadata.json +5 -0
  13. package/src/base/tokens/source/base.json +142 -0
  14. package/src/base/tokens/themes/base.tokens.d.ts +38 -0
  15. package/src/base/tokens/themes/base.tokens.json +695 -0
  16. package/src/base/tokens/themes/base.tokens.ts +38 -0
  17. package/src/base/tokens/themes/ui-dark.tokens.d.ts +38 -0
  18. package/src/base/tokens/themes/ui-dark.tokens.json +695 -0
  19. package/src/base/tokens/themes/ui-dark.tokens.ts +38 -0
  20. package/src/base/tokens/themes/ui-light.tokens.d.ts +38 -0
  21. package/src/base/tokens/themes/ui-light.tokens.json +695 -0
  22. package/src/base/tokens/themes/ui-light.tokens.ts +38 -0
  23. package/src/components/Button/Button.styles.ts +17 -3
  24. package/src/components/Button/Button.tsx +6 -3
  25. package/src/components/Button/Icon/IconButton.styles.ts +9 -2
  26. package/src/components/DataTable/DataTable.stories.tsx +9 -0
  27. package/src/components/DataTable/DataTable.styles.ts +5 -0
  28. package/src/components/DataTable/DataTable.tsx +8 -6
  29. package/src/components/DataTable/EmptyState/DataTableEmptyState.tsx +4 -1
  30. package/src/components/DataTable/Item/DataTableItem.styles.ts +5 -2
  31. package/src/components/DataTable/Item/DataTableItem.tsx +36 -18
  32. package/src/components/Header/Header.styles.ts +0 -27
  33. package/src/components/Header/PageHeader/PageHeader.stories.tsx +88 -0
  34. package/src/components/Header/PageHeader/PageHeader.styles.ts +99 -0
  35. package/src/components/Header/PageHeader/PageHeader.tsx +15 -0
  36. package/src/components/Header/PageHeader/PageHeaderActions.tsx +17 -0
  37. package/src/components/Header/PageHeader/PageHeaderMain.tsx +15 -0
  38. package/src/components/Header/PageHeader/PageHeaderMenu.tsx +15 -0
  39. package/src/components/Header/PageHeader/PageHeaderTitle.tsx +47 -0
  40. package/src/components/Header/PageHeader/index.ts +12 -0
  41. package/src/components/Header/index.ts +1 -1
  42. package/src/components/Layout/AreaIndicator.styles.ts +33 -0
  43. package/src/components/Layout/AreaIndicator.tsx +35 -0
  44. package/src/components/Layout/PageLayout/PageLayout.stories.tsx +81 -0
  45. package/src/components/Layout/PageLayout/PageLayout.styles.ts +40 -0
  46. package/src/components/Layout/PageLayout/PageLayout.tsx +21 -0
  47. package/src/components/Layout/PageLayout/PageLayoutActions.tsx +15 -0
  48. package/src/components/Layout/PageLayout/PageLayoutBody.tsx +24 -0
  49. package/src/components/Layout/PageLayout/PageLayoutHeader.tsx +15 -0
  50. package/src/components/Layout/PageLayout/index.ts +10 -0
  51. package/src/components/Layout/index.ts +1 -0
  52. package/src/components/Menu/Button/MenuButton.stories.tsx +16 -0
  53. package/src/components/Menu/Button/MenuButton.styles.ts +27 -0
  54. package/src/components/Menu/Button/MenuButton.tsx +62 -0
  55. package/src/components/Menu/Button/index.ts +1 -0
  56. package/src/components/Menu/Menu.stories.tsx +71 -0
  57. package/src/components/Menu/Menu.styles.ts +70 -0
  58. package/src/components/Menu/Menu.tsx +25 -0
  59. package/src/components/Menu/MenuDivider.tsx +13 -0
  60. package/src/components/Menu/MenuItem.tsx +36 -0
  61. package/src/components/Menu/MenuItems.tsx +35 -0
  62. package/src/components/Menu/index.ts +12 -0
  63. package/src/components/Menu/useMenu.tsx +79 -0
  64. package/src/components/Modal/index.ts +2 -0
  65. package/src/components/Tooltip/TextOverflowTooltip/TextOverflowTooltip.stories.tsx +70 -0
  66. package/src/components/Tooltip/TextOverflowTooltip/TextOverflowTooltip.styles.ts +5 -0
  67. package/src/components/Tooltip/TextOverflowTooltip/TextOverflowTooltip.tsx +115 -0
  68. package/src/components/Tooltip/TextOverflowTooltip/index.tsx +1 -0
  69. package/src/components/Tooltip/Tooltip.stories.tsx +41 -0
  70. package/src/components/Tooltip/Tooltip.styles.ts +5 -0
  71. package/src/components/Tooltip/Tooltip.tsx +46 -0
  72. package/src/components/Tooltip/index.ts +1 -0
  73. package/src/components/index.ts +3 -0
  74. package/src/index.ts +0 -2
  75. package/src/screens/dashboard/DashboardsDetail.stories.tsx +108 -0
  76. package/src/screens/dashboard/DashboardsOverview.stories.tsx +110 -102
  77. package/src/styles/tokens/themes/_ui-dark.tokens.json +695 -0
  78. package/src/styles/tokens/themes/_ui-light.tokens.json +695 -0
  79. package/src/utils/index.ts +1 -1
  80. package/src/utils/sanitizedHTML.ts +1 -1
  81. package/src/utils/useResizeObserver.ts +6 -0
  82. package/test/setupTests.js +1 -1
  83. package/src/components/Header/Header.stories.tsx +0 -27
  84. package/src/components/Header/Header.tsx +0 -23
  85. package/src/layout/TiledListingPage/TiledListingActions.tsx +0 -15
  86. package/src/layout/TiledListingPage/TiledListingContent.tsx +0 -21
  87. package/src/layout/TiledListingPage/TiledListingList.tsx +0 -11
  88. package/src/layout/TiledListingPage/TiledListingPage.styles.ts +0 -16
  89. package/src/layout/TiledListingPage/TiledListingPage.tsx +0 -15
  90. package/src/layout/TiledListingPage/index.ts +0 -4
@@ -0,0 +1,35 @@
1
+ import { ReactElement, ReactNode } from 'react';
2
+
3
+ import { useStyles } from './AreaIndicator.styles';
4
+
5
+ type AreaIndicatorProps = {
6
+ children?: ReactNode | Array<ReactNode>;
7
+ depth?: number;
8
+ height?: number | string;
9
+ name: string;
10
+ width?: number | string;
11
+ };
12
+
13
+ const AreaIndicator = ({
14
+ children,
15
+ name = 'area',
16
+ width = '100%',
17
+ height = '100%',
18
+ depth = 0
19
+ }: AreaIndicatorProps): ReactElement => {
20
+ const { classes } = useStyles();
21
+
22
+ return (
23
+ <div
24
+ className={classes.areaIndicator}
25
+ data-depth={depth}
26
+ style={{ height, width }}
27
+ >
28
+ {/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
29
+ <label>{name}</label>
30
+ {children}
31
+ </div>
32
+ );
33
+ };
34
+
35
+ export { AreaIndicator };
@@ -0,0 +1,81 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+
3
+ import { AreaIndicator } from '../AreaIndicator';
4
+ import { Default as DefaultPageHeaderStory } from '../../Header/PageHeader/PageHeader.stories';
5
+
6
+ import { PageLayout } from './index';
7
+
8
+ const meta: Meta<typeof PageLayout> = {
9
+ argTypes: {
10
+ variant: {
11
+ control: {
12
+ options: ['default', 'fixed-header']
13
+ }
14
+ }
15
+ },
16
+ component: PageLayout,
17
+ parameters: {
18
+ layout: 'fullscreen'
19
+ }
20
+ };
21
+
22
+ export default meta;
23
+ type Story = StoryObj<typeof PageLayout>;
24
+
25
+ export const Default: Story = {
26
+ args: {
27
+ children: (
28
+ <>
29
+ <PageLayout.Header>
30
+ <AreaIndicator name="header" />
31
+ </PageLayout.Header>
32
+ <PageLayout.Body>
33
+ <AreaIndicator height="100vh" name="body">
34
+ <PageLayout.Actions>
35
+ <AreaIndicator depth={1} name="actions" />
36
+ </PageLayout.Actions>
37
+ </AreaIndicator>
38
+ </PageLayout.Body>
39
+ </>
40
+ ),
41
+ variant: 'default'
42
+ }
43
+ };
44
+
45
+ export const FixedHeader: Story = {
46
+ args: {
47
+ children: (
48
+ <>
49
+ <PageLayout.Header>
50
+ <AreaIndicator name="header" />
51
+ </PageLayout.Header>
52
+ <PageLayout.Body hasBackground>
53
+ <AreaIndicator height="100vh" name="body">
54
+ <PageLayout.Actions>
55
+ <AreaIndicator depth={1} name="actions" />
56
+ </PageLayout.Actions>
57
+ </AreaIndicator>
58
+ </PageLayout.Body>
59
+ </>
60
+ ),
61
+ variant: 'fixed-header'
62
+ }
63
+ };
64
+
65
+ export const DefaultWithPageHeader: Story = {
66
+ args: {
67
+ children: (
68
+ <>
69
+ <PageLayout.Header>
70
+ {/* eslint-disable react/jsx-pascal-case */}
71
+ {/* @ts-expect-error storybook story render method not being recognised */}
72
+ <DefaultPageHeaderStory.render {...DefaultPageHeaderStory.args} />
73
+ </PageLayout.Header>
74
+ <PageLayout.Body>
75
+ <AreaIndicator height="100vh" name="body" />
76
+ </PageLayout.Body>
77
+ </>
78
+ ),
79
+ variant: 'default'
80
+ }
81
+ };
@@ -0,0 +1,40 @@
1
+ import { makeStyles } from 'tss-react/mui';
2
+
3
+ export const useStyles = makeStyles()((theme) => ({
4
+ pageLayout: {
5
+ display: 'grid',
6
+ gridTemplateRows: 'min-content',
7
+ overflow: 'hidden'
8
+ },
9
+ pageLayoutActions: {
10
+ '& > span': {
11
+ display: 'flex',
12
+ gap: theme.spacing(0.5)
13
+ },
14
+
15
+ display: 'flex',
16
+ justifyContent: 'space-between',
17
+ paddingBottom: theme.spacing(2.5)
18
+ },
19
+ pageLayoutBody: {
20
+ '&[data-has-background="true"]': {
21
+ backgroundColor: theme.palette.layout.body.background
22
+ },
23
+ display: 'grid',
24
+ gridTemplateRows: 'min-content',
25
+ overflow: 'hidden',
26
+ padding: theme.spacing(3, 3, 5)
27
+ },
28
+ pageLayoutHeader: {
29
+ '[data-variant="fixed-header"] &': {
30
+ backgroundColor: theme.palette.layout.header.background,
31
+ borderBottom: `1px solid ${theme.palette.layout.header.border}`,
32
+ padding: theme.spacing(5, 4, 2),
33
+ position: 'sticky',
34
+ top: 0,
35
+ zIndex: `calc(${theme.zIndex.mobileStepper} - 100)`
36
+ },
37
+
38
+ padding: theme.spacing(5, 3, 0)
39
+ }
40
+ }));
@@ -0,0 +1,21 @@
1
+ import { ReactElement, ReactNode } from 'react';
2
+
3
+ import { useStyles } from './PageLayout.styles';
4
+
5
+ type PageLayoutProps = {
6
+ children: Array<ReactNode> | ReactNode;
7
+ variant?: 'default' | 'fixed-header';
8
+ };
9
+
10
+ export const PageLayout = ({
11
+ children,
12
+ variant = 'default'
13
+ }: PageLayoutProps): ReactElement => {
14
+ const { classes } = useStyles();
15
+
16
+ return (
17
+ <section className={classes.pageLayout} data-variant={variant}>
18
+ {children}
19
+ </section>
20
+ );
21
+ };
@@ -0,0 +1,15 @@
1
+ import { ReactElement, ReactNode } from 'react';
2
+
3
+ import { useStyles } from './PageLayout.styles';
4
+
5
+ type PageLayoutActionsProps = {
6
+ children: Array<ReactNode> | ReactNode;
7
+ };
8
+
9
+ export const PageLayoutActions = ({
10
+ children
11
+ }: PageLayoutActionsProps): ReactElement => {
12
+ const { classes } = useStyles();
13
+
14
+ return <section className={classes.pageLayoutActions}>{children}</section>;
15
+ };
@@ -0,0 +1,24 @@
1
+ import { ReactElement, ReactNode } from 'react';
2
+
3
+ import { useStyles } from './PageLayout.styles';
4
+
5
+ type PageLayoutBodyProps = {
6
+ children: Array<ReactNode> | ReactNode;
7
+ hasBackground?: boolean;
8
+ };
9
+
10
+ export const PageLayoutBody = ({
11
+ children,
12
+ hasBackground = false
13
+ }: PageLayoutBodyProps): ReactElement => {
14
+ const { classes } = useStyles();
15
+
16
+ return (
17
+ <section
18
+ className={classes.pageLayoutBody}
19
+ data-has-background={hasBackground}
20
+ >
21
+ {children}
22
+ </section>
23
+ );
24
+ };
@@ -0,0 +1,15 @@
1
+ import { ReactNode } from 'react';
2
+
3
+ import { useStyles } from './PageLayout.styles';
4
+
5
+ type PageLayoutHeaderProps = {
6
+ children: Array<ReactNode> | ReactNode;
7
+ };
8
+
9
+ export const PageLayoutHeader = ({
10
+ children
11
+ }: PageLayoutHeaderProps): JSX.Element => {
12
+ const { classes } = useStyles();
13
+
14
+ return <header className={classes.pageLayoutHeader}>{children}</header>;
15
+ };
@@ -0,0 +1,10 @@
1
+ import { PageLayout as PageLayoutRoot } from './PageLayout';
2
+ import { PageLayoutHeader } from './PageLayoutHeader';
3
+ import { PageLayoutBody } from './PageLayoutBody';
4
+ import { PageLayoutActions } from './PageLayoutActions';
5
+
6
+ export const PageLayout = Object.assign(PageLayoutRoot, {
7
+ Actions: PageLayoutActions,
8
+ Body: PageLayoutBody,
9
+ Header: PageLayoutHeader
10
+ });
@@ -0,0 +1 @@
1
+ export * from './PageLayout';
@@ -0,0 +1,16 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+
3
+ import { MenuButton } from './MenuButton';
4
+
5
+ const meta: Meta<typeof MenuButton> = {
6
+ component: MenuButton
7
+ };
8
+
9
+ export default meta;
10
+ type Story = StoryObj<typeof MenuButton>;
11
+
12
+ export const Default: Story = {
13
+ args: {
14
+ 'aria-label': 'menu button'
15
+ }
16
+ };
@@ -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
+ }));
@@ -0,0 +1,62 @@
1
+ import { ReactElement, ReactNode, useCallback } from 'react';
2
+
3
+ import {
4
+ ArrowDropDown as ArrowDropDownIcon,
5
+ Menu as MenuIcon
6
+ } from '@mui/icons-material';
7
+
8
+ import { Button, ButtonProps } from '../../Button';
9
+ import { AriaLabelingAttributes } from '../../../@types/aria-attributes';
10
+ import { DataTestAttributes } from '../../../@types/data-attributes';
11
+ import { useMenu } from '../useMenu';
12
+
13
+ import { useStyles } from './MenuButton.styles';
14
+
15
+ type MenuButtonProps = {
16
+ children?: ReactNode;
17
+ hasArrow?: boolean;
18
+ isOpen?: boolean;
19
+ onClick?: (args: { isOpen: boolean }) => void;
20
+ } & Pick<ButtonProps, 'disabled' | 'size' | 'variant'> &
21
+ AriaLabelingAttributes &
22
+ DataTestAttributes;
23
+
24
+ const MenuButton = ({
25
+ children,
26
+ onClick,
27
+ hasArrow = true,
28
+ size = 'small',
29
+ variant = 'ghost',
30
+ ...attr
31
+ }: MenuButtonProps): ReactElement => {
32
+ const { classes } = useStyles();
33
+
34
+ const { isMenuOpen, setIsMenuOpen, setAnchorEl, onOpen } = useMenu();
35
+
36
+ const onToggle = useCallback(
37
+ (e): void => {
38
+ setAnchorEl(e.currentTarget);
39
+
40
+ setIsMenuOpen(!isMenuOpen);
41
+ onClick?.({ isOpen: !isMenuOpen });
42
+ if (!isMenuOpen) onOpen?.();
43
+ },
44
+ [isMenuOpen, onClick, onOpen]
45
+ );
46
+
47
+ return (
48
+ <Button
49
+ {...attr}
50
+ className={classes.menuButton}
51
+ data-is-active={isMenuOpen}
52
+ size={size}
53
+ variant={variant}
54
+ onClick={onToggle}
55
+ >
56
+ {children || <MenuIcon />}
57
+ {hasArrow && <ArrowDropDownIcon className={classes.buttonIcon} />}
58
+ </Button>
59
+ );
60
+ };
61
+
62
+ export { MenuButton };
@@ -0,0 +1 @@
1
+ export * from './MenuButton';
@@ -0,0 +1,71 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+
3
+ import { Add as AddIcon } from '@mui/icons-material';
4
+
5
+ import { Button } from '../Button';
6
+
7
+ import { Menu } from '.';
8
+
9
+ const meta: Meta<typeof Menu> = {
10
+ component: Menu
11
+ };
12
+
13
+ export default meta;
14
+ type Story = StoryObj<typeof Menu>;
15
+
16
+ export const Default: Story = {
17
+ args: {
18
+ children: (
19
+ <Menu.Items>
20
+ <Menu.Item>Menu Item</Menu.Item>
21
+ <Menu.Item>Menu Item</Menu.Item>
22
+ <Menu.Item>Menu Item</Menu.Item>
23
+ <Menu.Item>Menu Item</Menu.Item>
24
+ <Menu.Item isActive isDisabled>
25
+ Menu Item
26
+ </Menu.Item>
27
+ <Menu.Item isDisabled>Menu Item</Menu.Item>
28
+ <Menu.Item>Menu Item</Menu.Item>
29
+ <Menu.Divider />
30
+ <Menu.Item>Add item...</Menu.Item>
31
+ </Menu.Items>
32
+ ),
33
+ isOpen: true
34
+ }
35
+ };
36
+
37
+ export const WithMenuButton: Story = {
38
+ args: {
39
+ children: (
40
+ <>
41
+ <Menu.Button />
42
+ {Default.args?.children}
43
+ </>
44
+ )
45
+ }
46
+ };
47
+
48
+ export const WithCustomActionButton: Story = {
49
+ args: {
50
+ children: (
51
+ <Menu.Items>
52
+ <Menu.Item>Menu Item</Menu.Item>
53
+ <Menu.Item>Menu Item</Menu.Item>
54
+ <Menu.Item>Menu Item</Menu.Item>
55
+ <Menu.Item>Menu Item</Menu.Item>
56
+ <Menu.Item isActive isDisabled>
57
+ Menu Item
58
+ </Menu.Item>
59
+ <Menu.Item isDisabled>Menu Item</Menu.Item>
60
+ <Menu.Item>Menu Item</Menu.Item>
61
+ <Menu.Divider />
62
+ <Menu.Item>
63
+ <Button icon={<AddIcon />} iconVariant="start" variant="ghost">
64
+ Add item
65
+ </Button>
66
+ </Menu.Item>
67
+ </Menu.Items>
68
+ ),
69
+ isOpen: true
70
+ }
71
+ };
@@ -0,0 +1,70 @@
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
+ '&:hover:not(&[data-is-active="true"])': {
13
+ backgroundColor: theme.palette.menu.item.background.hover,
14
+ color: theme.palette.menu.item.color.hover
15
+ },
16
+ backgroundColor: theme.palette.menu.item.background.default,
17
+ color: theme.palette.menu.item.color.default,
18
+ fontSize: '1rem',
19
+ justifyContent: 'flex-start',
20
+ width: '100%'
21
+ },
22
+
23
+ '&:has(button)': {
24
+ '& > .MuiTouchRipple-root': {
25
+ display: 'none'
26
+ },
27
+ '&:hover:not(&[data-is-active="true"])': {
28
+ backgroundColor: 'unset'
29
+ },
30
+ padding: theme.spacing(0, 2)
31
+ },
32
+
33
+ '&:not(:has(button))': {
34
+ '&:hover:not(&[data-is-active="true"])': {
35
+ backgroundColor: theme.palette.menu.item.background.hover,
36
+ color: theme.palette.menu.item.color.hover
37
+ },
38
+ '&[data-is-active="true"], &.Mui-selected': {
39
+ '&[data-is-disabled="true"], &.Mui-disabled': {
40
+ opacity: 1
41
+ },
42
+ backgroundColor: theme.palette.menu.item.background.active,
43
+ color: theme.palette.menu.item.color.active,
44
+ opacity: 1
45
+ },
46
+ '&[data-is-disabled="true"], &.Mui-disabled': {
47
+ opacity: 0.5
48
+ },
49
+ backgroundColor: theme.palette.menu.item.background.default,
50
+ color: theme.palette.menu.item.color.default,
51
+ padding: theme.spacing(0.75, 2)
52
+ },
53
+
54
+ fontSize: '1rem',
55
+ minHeight: 'unset'
56
+ }
57
+ },
58
+ menuItems: {
59
+ '& > .MuiPaper-root.MuiMenu-paper': {
60
+ backgroundColor: theme.palette.menu.background,
61
+ borderRadius: '4px',
62
+ boxShadow: theme.shadows[8],
63
+ minWidth: '240px',
64
+ transform: `translateY(${theme.spacing(0.5)}) !important`
65
+ },
66
+ '& ul, & ul.MuiMenu-list': {
67
+ padding: theme.spacing(1, 0)
68
+ }
69
+ }
70
+ }));
@@ -0,0 +1,25 @@
1
+ import { ReactElement, ReactNode } from 'react';
2
+
3
+ import { MenuProvider } from './useMenu';
4
+
5
+ type MenuProps = {
6
+ children?: ReactNode | Array<ReactNode>;
7
+ isOpen?: boolean;
8
+ onClose?: () => void;
9
+ onOpen?: () => void;
10
+ };
11
+
12
+ const Menu = ({
13
+ children,
14
+ isOpen = false,
15
+ onOpen,
16
+ onClose
17
+ }: MenuProps): ReactElement => {
18
+ return (
19
+ <MenuProvider initialIsOpen={isOpen} onClose={onClose} onOpen={onOpen}>
20
+ {children}
21
+ </MenuProvider>
22
+ );
23
+ };
24
+
25
+ export { Menu };
@@ -0,0 +1,13 @@
1
+ import { ReactElement } from 'react';
2
+
3
+ import { Divider as MuiDivider } from '@mui/material';
4
+
5
+ import { useStyles } from './Menu.styles';
6
+
7
+ const MenuDivider = (): ReactElement => {
8
+ const { classes } = useStyles();
9
+
10
+ return <MuiDivider className={classes.menuDivider} />;
11
+ };
12
+
13
+ export { MenuDivider };
@@ -0,0 +1,36 @@
1
+ import { ReactElement, ReactNode } from 'react';
2
+
3
+ import { MenuItem as MuiMenuItem } from '@mui/material';
4
+
5
+ import { useStyles } from './Menu.styles';
6
+
7
+ type MenuItemProps = {
8
+ children?: ReactNode;
9
+ isActive?: boolean;
10
+ isDisabled?: boolean;
11
+ onClick?: () => void;
12
+ };
13
+
14
+ const MenuItem = ({
15
+ children,
16
+ onClick,
17
+ isActive = false,
18
+ isDisabled = false
19
+ }: MenuItemProps): ReactElement => {
20
+ const { classes } = useStyles();
21
+
22
+ return (
23
+ <MuiMenuItem
24
+ className={classes.menuItem}
25
+ data-is-active={isActive}
26
+ data-is-disabled={isDisabled}
27
+ disabled={isDisabled}
28
+ selected={isActive}
29
+ onClick={() => onClick?.()}
30
+ >
31
+ {children}
32
+ </MuiMenuItem>
33
+ );
34
+ };
35
+
36
+ export { MenuItem };
@@ -0,0 +1,35 @@
1
+ import { ReactElement, ReactNode } from 'react';
2
+
3
+ import { Menu as MuiMenu } from '@mui/material';
4
+
5
+ import { useStyles } from './Menu.styles';
6
+ import { useMenu } from './useMenu';
7
+
8
+ type MenuItemsProps = {
9
+ children?: ReactNode | Array<ReactNode>;
10
+ };
11
+ const MenuItems = ({ children }: MenuItemsProps): ReactElement => {
12
+ const { classes } = useStyles();
13
+
14
+ const { isMenuOpen, setIsMenuOpen, anchorEl, onClose } = useMenu();
15
+
16
+ const onCloseMenu = (): void => {
17
+ setIsMenuOpen(false);
18
+ onClose?.();
19
+ };
20
+
21
+ return (
22
+ <MuiMenu
23
+ anchorEl={anchorEl}
24
+ className={classes.menuItems}
25
+ open={isMenuOpen}
26
+ variant="menu"
27
+ onClick={onCloseMenu}
28
+ onClose={onCloseMenu}
29
+ >
30
+ {children}
31
+ </MuiMenu>
32
+ );
33
+ };
34
+
35
+ export { MenuItems };
@@ -0,0 +1,12 @@
1
+ import { Menu as MenuRoot } from './Menu';
2
+ import { MenuButton } from './Button';
3
+ import { MenuItem } from './MenuItem';
4
+ import { MenuDivider } from './MenuDivider';
5
+ import { MenuItems } from './MenuItems';
6
+
7
+ export const Menu = Object.assign(MenuRoot, {
8
+ Button: MenuButton,
9
+ Divider: MenuDivider,
10
+ Item: MenuItem,
11
+ Items: MenuItems
12
+ });