@centreon/ui 23.10.1-MON-20803-RS-add-basic-filter.26 → 23.10.1-MON-20803-RS-add-basic-filter.27

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 (26) hide show
  1. package/package.json +1 -1
  2. package/src/ActionsList/ActionsList.styles.ts +116 -0
  3. package/src/ActionsList/index.stories.tsx +74 -0
  4. package/src/ActionsList/index.tsx +20 -12
  5. package/src/ActionsList/models.ts +8 -0
  6. package/src/Checkbox/Checkbox.tsx +10 -10
  7. package/src/Listing/Listing.cypress.spec.tsx +17 -15
  8. package/src/Listing/Listing.styles.ts +5 -0
  9. package/src/Listing/index.stories.tsx +2 -1
  10. package/src/Listing/index.tsx +140 -151
  11. package/src/ListingPage/index.stories.tsx +21 -8
  12. package/src/ListingPage/index.tsx +19 -37
  13. package/src/RichTextEditor/RichTextEditor.cypress.spec.tsx +23 -0
  14. package/src/RichTextEditor/index.stories.tsx +4 -2
  15. package/src/RichTextEditor/plugins/ToolbarPlugin/AlignPicker.tsx +115 -0
  16. package/src/RichTextEditor/plugins/ToolbarPlugin/ToolbarPlugin.styles.ts +15 -0
  17. package/src/RichTextEditor/plugins/ToolbarPlugin/index.tsx +2 -0
  18. package/src/components/Menu/Button/MenuButton.tsx +3 -0
  19. package/src/components/Tooltip/ConfirmationTooltip/ConfirmationTooltip.stories.tsx +62 -0
  20. package/src/components/Tooltip/ConfirmationTooltip/ConfirmationTooltip.styles.tsx +19 -0
  21. package/src/components/Tooltip/ConfirmationTooltip/ConfirmationTooltip.tsx +67 -0
  22. package/src/components/Tooltip/ConfirmationTooltip/TooltipContent.tsx +31 -0
  23. package/src/components/Tooltip/ConfirmationTooltip/index.ts +0 -0
  24. package/src/components/Tooltip/ConfirmationTooltip/models.ts +18 -0
  25. package/src/components/Tooltip/Tooltip.tsx +19 -8
  26. package/src/components/Tooltip/Tooltip.styles.ts +0 -5
@@ -0,0 +1,115 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+
3
+ import {
4
+ $getSelection,
5
+ $isElementNode,
6
+ $isRangeSelection,
7
+ ElementFormatType,
8
+ FORMAT_ELEMENT_COMMAND
9
+ } from 'lexical';
10
+ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
11
+ import { equals } from 'ramda';
12
+
13
+ import FormatAlignLeftIcon from '@mui/icons-material/FormatAlignLeft';
14
+ import FormatAlignCenterIcon from '@mui/icons-material/FormatAlignCenter';
15
+ import FormatAlignRightIcon from '@mui/icons-material/FormatAlignRight';
16
+ import { OverridableComponent } from '@mui/material/OverridableComponent';
17
+ import { SvgIconTypeMap } from '@mui/material';
18
+
19
+ import { Menu } from '../../../components';
20
+ import { getSelectedNode } from '../../utils/getSelectedNode';
21
+
22
+ import { useAlignPickerStyles } from './ToolbarPlugin.styles';
23
+
24
+ const formatOptions: Array<{
25
+ Icon: OverridableComponent<SvgIconTypeMap<object, 'svg'>>;
26
+ label: string;
27
+ value: ElementFormatType;
28
+ }> = [
29
+ {
30
+ Icon: FormatAlignLeftIcon,
31
+ label: 'Left',
32
+ value: 'left'
33
+ },
34
+ {
35
+ Icon: FormatAlignCenterIcon,
36
+ label: 'Center',
37
+ value: 'center'
38
+ },
39
+ {
40
+ Icon: FormatAlignRightIcon,
41
+ label: 'Right',
42
+ value: 'right'
43
+ }
44
+ ];
45
+
46
+ interface Props {
47
+ disabled: boolean;
48
+ }
49
+
50
+ const AlignPicker = ({ disabled }: Props): JSX.Element => {
51
+ const { classes } = useAlignPickerStyles();
52
+
53
+ const [elementFormat, setElementFormat] = useState<ElementFormatType>('left');
54
+
55
+ const [editor] = useLexicalComposerContext();
56
+
57
+ const dispatchAlignment = (alignment: ElementFormatType) => () => {
58
+ editor.dispatchCommand(FORMAT_ELEMENT_COMMAND, alignment);
59
+ };
60
+
61
+ const updateElementFormat = useCallback(() => {
62
+ const selection = $getSelection();
63
+
64
+ if (!$isRangeSelection(selection)) {
65
+ return;
66
+ }
67
+
68
+ const node = getSelectedNode(selection);
69
+ const parent = node.getParent();
70
+
71
+ setElementFormat(
72
+ ($isElementNode(node) ? node.getFormatType() : parent?.getFormatType()) ||
73
+ 'left'
74
+ );
75
+ }, [editor]);
76
+
77
+ const selectedFormat = formatOptions.find(({ value }) =>
78
+ equals(value, elementFormat)
79
+ );
80
+
81
+ useEffect(() => {
82
+ return editor.registerUpdateListener(({ editorState }) => {
83
+ editorState.read(() => {
84
+ updateElementFormat();
85
+ });
86
+ });
87
+ }, [editor, updateElementFormat]);
88
+
89
+ return (
90
+ <Menu>
91
+ <Menu.Button ariaLabel={elementFormat} disabled={disabled}>
92
+ {selectedFormat && (
93
+ <div className={classes.button}>
94
+ <selectedFormat.Icon /> {selectedFormat.label}
95
+ </div>
96
+ )}
97
+ </Menu.Button>
98
+ <Menu.Items>
99
+ {formatOptions.map(({ Icon, label, value }) => (
100
+ <Menu.Item
101
+ isActive={equals(value, elementFormat)}
102
+ key={value}
103
+ onClick={dispatchAlignment(value)}
104
+ >
105
+ <div className={classes.option}>
106
+ <Icon /> {label}
107
+ </div>
108
+ </Menu.Item>
109
+ ))}
110
+ </Menu.Items>
111
+ </Menu>
112
+ );
113
+ };
114
+
115
+ export default AlignPicker;
@@ -5,3 +5,18 @@ export const useBlockButtonsStyles = makeStyles()((theme) => ({
5
5
  width: theme.spacing(17)
6
6
  }
7
7
  }));
8
+
9
+ export const useAlignPickerStyles = makeStyles()((theme) => ({
10
+ button: {
11
+ alignItems: 'center',
12
+ display: 'flex',
13
+ flexDirection: 'row',
14
+ gap: theme.spacing(1)
15
+ },
16
+ option: {
17
+ alignItems: 'center',
18
+ display: 'flex',
19
+ flexDirection: 'row',
20
+ gap: theme.spacing(2)
21
+ }
22
+ }));
@@ -6,6 +6,7 @@ import FormatButtons from './FormatButtons';
6
6
  import UndoRedoButtons from './UndoRedoButtons';
7
7
  import MacrosButton from './MacrosButton';
8
8
  import BlockButtons from './BlockButtons';
9
+ import AlignPicker from './AlignPicker';
9
10
 
10
11
  interface Props {
11
12
  disabled: boolean;
@@ -56,6 +57,7 @@ const ToolbarPlugin = ({
56
57
  <MacrosButton disabled={disabled} />
57
58
  </>
58
59
  )}
60
+ <AlignPicker disabled={disabled} />
59
61
  </div>
60
62
  ) : null;
61
63
  };
@@ -13,6 +13,7 @@ import { useMenu } from '../useMenu';
13
13
  import { useStyles } from './MenuButton.styles';
14
14
 
15
15
  type MenuButtonProps = {
16
+ ariaLabel?: string;
16
17
  children?: ReactNode;
17
18
  hasArrow?: boolean;
18
19
  isOpen?: boolean;
@@ -27,6 +28,7 @@ const MenuButton = ({
27
28
  hasArrow = true,
28
29
  size = 'small',
29
30
  variant = 'ghost',
31
+ ariaLabel,
30
32
  ...attr
31
33
  }: MenuButtonProps): ReactElement => {
32
34
  const { classes } = useStyles();
@@ -47,6 +49,7 @@ const MenuButton = ({
47
49
  return (
48
50
  <Button
49
51
  {...attr}
52
+ aria-label={ariaLabel}
50
53
  className={classes.menuButton}
51
54
  data-is-active={isMenuOpen}
52
55
  size={size}
@@ -0,0 +1,62 @@
1
+ import { Meta, StoryObj } from '@storybook/react';
2
+
3
+ import { Delete as DeleteIcon, Save as SaveIcon } from '@mui/icons-material';
4
+
5
+ import { IconButton } from '../..';
6
+
7
+ import { ConfirmationTooltip } from './ConfirmationTooltip';
8
+
9
+ const meta: Meta<typeof ConfirmationTooltip> = {
10
+ component: ConfirmationTooltip
11
+ };
12
+
13
+ export default meta;
14
+ type Story = StoryObj<typeof ConfirmationTooltip>;
15
+
16
+ export const Default: Story = {
17
+ args: {
18
+ children: (toggleTooltip) => (
19
+ <IconButton icon={<SaveIcon />} onClick={toggleTooltip} />
20
+ ),
21
+ labels: {
22
+ cancel: 'Cancel',
23
+ confirm: {
24
+ label: 'Save'
25
+ }
26
+ },
27
+ onConfirm: () => undefined
28
+ }
29
+ };
30
+
31
+ export const WithConfirmVariant: Story = {
32
+ args: {
33
+ children: (toggleTooltip) => (
34
+ <IconButton icon={<DeleteIcon color="error" />} onClick={toggleTooltip} />
35
+ ),
36
+ confirmVariant: 'error',
37
+ labels: {
38
+ cancel: 'Cancel',
39
+ confirm: {
40
+ label: 'Delete'
41
+ }
42
+ },
43
+ onConfirm: () => undefined
44
+ }
45
+ };
46
+
47
+ export const WithSecondaryLabel: Story = {
48
+ args: {
49
+ children: (toggleTooltip) => (
50
+ <IconButton icon={<DeleteIcon color="error" />} onClick={toggleTooltip} />
51
+ ),
52
+ confirmVariant: 'error',
53
+ labels: {
54
+ cancel: 'Cancel',
55
+ confirm: {
56
+ label: 'Delete',
57
+ secondaryLabel: 'This action will delete the current item'
58
+ }
59
+ },
60
+ onConfirm: () => undefined
61
+ }
62
+ };
@@ -0,0 +1,19 @@
1
+ import { equals } from 'ramda';
2
+ import { makeStyles } from 'tss-react/mui';
3
+
4
+ import { ThemeMode } from '@centreon/ui-context';
5
+
6
+ export const useStyles = makeStyles()((theme) => ({
7
+ list: {
8
+ padding: 0
9
+ },
10
+ paper: {
11
+ backgroundColor: equals(theme.palette.mode, ThemeMode.dark)
12
+ ? theme.palette.background.widget
13
+ : theme.palette.background.default,
14
+ borderRadius: theme.shape.borderRadius,
15
+ boxShadow: theme.shadows[3],
16
+ marginLeft: theme.spacing(2),
17
+ maxWidth: '350px'
18
+ }
19
+ }));
@@ -0,0 +1,67 @@
1
+ import { MouseEvent, useState } from 'react';
2
+
3
+ import { ClickAwayListener, Box, Popper } from '@mui/material';
4
+
5
+ import TooltipContent from './TooltipContent';
6
+ import { Props } from './models';
7
+ import { useStyles } from './ConfirmationTooltip.styles';
8
+
9
+ export const ConfirmationTooltip = ({
10
+ children,
11
+ labels,
12
+ onConfirm,
13
+ confirmVariant
14
+ }: Props): JSX.Element => {
15
+ const { classes } = useStyles();
16
+
17
+ const [anchorElement, setAnchorElement] = useState<
18
+ HTMLButtonElement | undefined
19
+ >(undefined);
20
+
21
+ const close = (): void => {
22
+ setAnchorElement(undefined);
23
+ };
24
+
25
+ const open = (event: MouseEvent<HTMLButtonElement>): void => {
26
+ setAnchorElement(event.currentTarget);
27
+ };
28
+
29
+ const confirm = (): void => {
30
+ close();
31
+ onConfirm();
32
+ };
33
+
34
+ const actions = [
35
+ {
36
+ action: close,
37
+ label: labels.cancel
38
+ },
39
+ {
40
+ action: confirm,
41
+ label: labels.confirm.label,
42
+ secondaryLabel: labels.confirm.secondaryLabel,
43
+ variant: confirmVariant
44
+ }
45
+ ];
46
+
47
+ const isOpen = Boolean(anchorElement);
48
+
49
+ return (
50
+ <ClickAwayListener onClickAway={close}>
51
+ <div>
52
+ {children(isOpen ? close : open)}
53
+ <Popper
54
+ anchorEl={anchorElement}
55
+ open={isOpen}
56
+ popperOptions={{
57
+ placement: 'bottom-start'
58
+ }}
59
+ >
60
+ <Box className={classes.paper}>
61
+ <TooltipContent actions={actions} />
62
+ </Box>
63
+ </Popper>
64
+ </div>
65
+ </ClickAwayListener>
66
+ );
67
+ };
@@ -0,0 +1,31 @@
1
+ import { ActionVariants } from '../../../ActionsList/models';
2
+ import { ActionsList } from '../../..';
3
+
4
+ import { useStyles } from './ConfirmationTooltip.styles';
5
+
6
+ interface Props {
7
+ actions: Array<{
8
+ action: () => void;
9
+ label: string;
10
+ secondaryLabel?: string;
11
+ variant?: ActionVariants;
12
+ }>;
13
+ }
14
+
15
+ const TooltipContent = ({ actions }: Props): JSX.Element => {
16
+ const { classes } = useStyles();
17
+
18
+ return (
19
+ <ActionsList
20
+ actions={actions.map(({ label, action, variant, secondaryLabel }) => ({
21
+ label,
22
+ onClick: action,
23
+ secondaryLabel,
24
+ variant
25
+ }))}
26
+ className={classes.list}
27
+ />
28
+ );
29
+ };
30
+
31
+ export default TooltipContent;
@@ -0,0 +1,18 @@
1
+ import { ReactElement } from 'react';
2
+
3
+ import { ActionVariants } from '../../../ActionsList/models';
4
+
5
+ interface Labels {
6
+ cancel: string;
7
+ confirm: {
8
+ label: string;
9
+ secondaryLabel?: string;
10
+ };
11
+ }
12
+
13
+ export interface Props {
14
+ children: (toggleTooltip) => ReactElement;
15
+ confirmVariant?: ActionVariants;
16
+ labels: Labels;
17
+ onConfirm: () => void;
18
+ }
@@ -1,21 +1,35 @@
1
1
  import { ReactElement, ReactNode } from 'react';
2
2
 
3
- import { Tooltip as MuiTooltip } from '@mui/material';
3
+ import {
4
+ Tooltip as MuiTooltip,
5
+ TooltipProps as MuiTooltipProps
6
+ } from '@mui/material';
4
7
 
5
8
  import { AriaLabelingAttributes } from '../../@types/aria-attributes';
6
9
  import { DataTestAttributes } from '../../@types/data-attributes';
7
10
 
8
- import { useStyles } from './Tooltip.styles';
9
-
10
11
  export type TooltipProps = {
11
12
  children: ReactElement;
12
13
  followCursor?: boolean;
13
14
  hasCaret?: boolean;
14
15
  isOpen?: boolean;
15
16
  label: ReactNode;
16
- position?: 'top' | 'bottom' | 'left' | 'right';
17
+ position?:
18
+ | 'top'
19
+ | 'bottom'
20
+ | 'left'
21
+ | 'right'
22
+ | 'bottom-end'
23
+ | 'bottom-start'
24
+ | 'left-end'
25
+ | 'left-start'
26
+ | 'right-end'
27
+ | 'right-start'
28
+ | 'top-end'
29
+ | 'top-start';
17
30
  } & AriaLabelingAttributes &
18
- DataTestAttributes;
31
+ DataTestAttributes &
32
+ Omit<MuiTooltipProps, 'title'>;
19
33
 
20
34
  const Tooltip = ({
21
35
  children,
@@ -26,12 +40,9 @@ const Tooltip = ({
26
40
  hasCaret = false,
27
41
  ...attr
28
42
  }: TooltipProps): ReactElement => {
29
- const { classes } = useStyles();
30
-
31
43
  return (
32
44
  <MuiTooltip
33
45
  arrow={hasCaret}
34
- className={classes.tooltip}
35
46
  followCursor={followCursor}
36
47
  open={isOpen}
37
48
  placement={position}
@@ -1,5 +0,0 @@
1
- import { makeStyles } from 'tss-react/mui';
2
-
3
- export const useStyles = makeStyles()(() => ({
4
- tooltip: {}
5
- }));