@centreon/ui 23.10.26-MON-19396-update-dashboard-rights.0 → 23.10.26

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 (44) hide show
  1. package/package.json +1 -1
  2. package/src/Button/Save/index.stories.tsx +10 -5
  3. package/src/Checkbox/Checkbox.tsx +107 -0
  4. package/src/Checkbox/CheckboxGroup/index.stories.tsx +41 -0
  5. package/src/Checkbox/CheckboxGroup/index.tsx +66 -0
  6. package/src/Checkbox/index.stories.tsx +39 -0
  7. package/src/Checkbox/index.tsx +2 -0
  8. package/src/Form/CollapsibleGroup.tsx +11 -5
  9. package/src/Form/Form.stories.tsx +1 -1
  10. package/src/Form/Form.tsx +24 -9
  11. package/src/Form/Inputs/Checkbox.tsx +57 -0
  12. package/src/Form/Inputs/CheckboxGroup.tsx +76 -0
  13. package/src/Form/Inputs/Grid.tsx +4 -2
  14. package/src/Form/Inputs/index.tsx +29 -7
  15. package/src/Form/Inputs/models.ts +13 -1
  16. package/src/Form/storiesData.tsx +45 -0
  17. package/src/InputField/Text/index.tsx +3 -0
  18. package/src/Panel/index.tsx +6 -4
  19. package/src/RichTextEditor/ContentEditable.tsx +27 -7
  20. package/src/RichTextEditor/RichTextEditor.tsx +86 -45
  21. package/src/ThemeProvider/index.tsx +4 -1
  22. package/src/ThemeProvider/palettes.ts +7 -0
  23. package/src/api/useMutationQuery/index.ts +2 -10
  24. package/src/components/Button/Button.tsx +1 -1
  25. package/src/components/DataTable/EmptyState/DataTableEmptyState.tsx +9 -13
  26. package/src/components/DataTable/Item/DataTableItem.tsx +0 -1
  27. package/src/components/Form/Dashboard/Dashboard.resource.ts +1 -1
  28. package/src/components/Form/Dashboard/DashboardForm.tsx +10 -6
  29. package/src/components/Form/Dashboard/translatedLabels.ts +1 -0
  30. package/src/components/Modal/Modal.styles.ts +1 -3
  31. package/src/components/Modal/ModalActions.tsx +1 -4
  32. package/src/components/index.ts +0 -1
  33. package/src/index.ts +2 -0
  34. package/src/utils/index.ts +1 -1
  35. package/src/utils/panelSize.ts +15 -0
  36. package/src/components/List/Item/Avatar.tsx +0 -14
  37. package/src/components/List/Item/Item.tsx +0 -18
  38. package/src/components/List/Item/Text.tsx +0 -13
  39. package/src/components/List/Item/index.ts +0 -8
  40. package/src/components/List/List.stories.tsx +0 -70
  41. package/src/components/List/List.styles.ts +0 -20
  42. package/src/components/List/List.tsx +0 -19
  43. package/src/components/List/index.ts +0 -6
  44. package/src/utils/useInfiniteScrollListing.ts +0 -106
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@centreon/ui",
3
- "version": "23.10.26-MON-19396-update-dashboard-rights.0",
3
+ "version": "23.10.26",
4
4
  "description": "Centreon UI Components",
5
5
  "scripts": {
6
6
  "eslint": "eslint ./src --ext .js,.jsx,.ts,.tsx --max-warnings 0",
@@ -1,4 +1,4 @@
1
- import { ComponentMeta, ComponentStory } from '@storybook/react';
1
+ import { ComponentStory } from '@storybook/react';
2
2
  import { makeStyles } from 'tss-react/mui';
3
3
 
4
4
  import { Theme } from '@mui/material';
@@ -13,7 +13,7 @@ const useStyles = makeStyles()((theme: Theme) => ({
13
13
  }
14
14
  }));
15
15
 
16
- export default {
16
+ const meta = {
17
17
  argTypes: {
18
18
  labelLoading: { control: 'text' },
19
19
  labelSave: { control: 'text' },
@@ -26,8 +26,13 @@ export default {
26
26
 
27
27
  component: ButtonSave,
28
28
 
29
+ parameters: {
30
+ chromatic: { delay: 300 }
31
+ },
29
32
  title: 'Button/Save'
30
- } as ComponentMeta<typeof ButtonSave>;
33
+ };
34
+
35
+ export default meta;
31
36
 
32
37
  const TemplateButtonSave: ComponentStory<typeof ButtonSave> = (args) => (
33
38
  <ButtonSave {...args} />
@@ -42,7 +47,7 @@ export const loading = (): JSX.Element => <ButtonSave loading />;
42
47
  export const succeeded = (): JSX.Element => <ButtonSave succeeded />;
43
48
 
44
49
  export const normalWithText = (): JSX.Element => (
45
- <ButtonSave labelSave="Save" />
50
+ <ButtonSave labelSave="Save" size="medium" />
46
51
  );
47
52
 
48
53
  export const largeWithText = (): JSX.Element => (
@@ -50,7 +55,7 @@ export const largeWithText = (): JSX.Element => (
50
55
  );
51
56
 
52
57
  export const loadingWithTextAndMediumSize = (): JSX.Element => (
53
- <ButtonSave loading labelLoading="Loading" />
58
+ <ButtonSave loading labelLoading="Loading" size="medium" />
54
59
  );
55
60
 
56
61
  export const succeededWithText = (): JSX.Element => (
@@ -0,0 +1,107 @@
1
+ import { makeStyles } from 'tss-react/mui';
2
+ import { T, always, cond, equals } from 'ramda';
3
+
4
+ import { SvgIconComponent } from '@mui/icons-material';
5
+ import {
6
+ FormControlLabel,
7
+ Checkbox as MuiCheckbox,
8
+ Box,
9
+ Typography
10
+ } from '@mui/material';
11
+
12
+ export type LabelPlacement = 'bottom' | 'top' | 'end' | 'start' | undefined;
13
+
14
+ interface StyleProps {
15
+ disabled: boolean;
16
+ hasIcon: boolean;
17
+ labelPlacement: LabelPlacement;
18
+ }
19
+
20
+ const getLabelSpacing = (labelPlacement, theme): string => {
21
+ return cond([
22
+ [equals('top'), always(theme.spacing(0, 0, 0.5))],
23
+ [equals('end'), always(theme.spacing(0, 0, 0, 0.5))],
24
+ [T, always(0)]
25
+ ])(labelPlacement);
26
+ };
27
+
28
+ const useStyles = makeStyles<StyleProps>()(
29
+ (theme, { disabled, hasIcon, labelPlacement }) => ({
30
+ container: hasIcon
31
+ ? {
32
+ alignItems: 'center',
33
+ display: 'flex',
34
+ flexDirection: 'column',
35
+ justifyContent: 'space-between',
36
+ minHeight: theme.spacing(11)
37
+ }
38
+ : {},
39
+ icon: {
40
+ fontSize: theme.spacing(10)
41
+ },
42
+ label: {
43
+ color: disabled
44
+ ? theme.palette.action.disabled
45
+ : theme.palette.text.secondary,
46
+ fontSize: theme.typography.body1.fontSize,
47
+ fontWeight: equals(labelPlacement, 'top')
48
+ ? theme.typography.fontWeightBold
49
+ : theme.typography.fontWeightMedium,
50
+ padding: getLabelSpacing(labelPlacement, theme)
51
+ }
52
+ })
53
+ );
54
+
55
+ interface Props {
56
+ Icon?: SvgIconComponent;
57
+ checked: boolean;
58
+ className?: string;
59
+ dataTestId?: string;
60
+ disabled?: boolean;
61
+ label: string;
62
+ labelPlacement?: LabelPlacement;
63
+ onChange?: (e) => void;
64
+ }
65
+
66
+ const Checkbox = ({
67
+ Icon,
68
+ checked,
69
+ label,
70
+ onChange,
71
+ className,
72
+ disabled = false,
73
+ labelPlacement = 'end',
74
+ dataTestId
75
+ }: Props): JSX.Element => {
76
+ const { classes, cx } = useStyles({
77
+ disabled,
78
+ hasIcon: !!Icon,
79
+ labelPlacement
80
+ });
81
+
82
+ return (
83
+ <Box className={cx(classes.container, className)}>
84
+ {Icon && <Icon className={classes.icon} />}
85
+ <FormControlLabel
86
+ control={
87
+ <MuiCheckbox
88
+ checked={checked}
89
+ color="primary"
90
+ disabled={disabled}
91
+ id={label}
92
+ size="small"
93
+ sx={{ padding: 0 }}
94
+ onChange={onChange}
95
+ />
96
+ }
97
+ data-testid={dataTestId || ''}
98
+ key={label}
99
+ label={<Typography className={classes.label}>{label}</Typography>}
100
+ labelPlacement={labelPlacement}
101
+ sx={{ margin: 0, padding: 0 }}
102
+ />
103
+ </Box>
104
+ );
105
+ };
106
+
107
+ export default Checkbox;
@@ -0,0 +1,41 @@
1
+ import { ComponentMeta, ComponentStory } from '@storybook/react';
2
+
3
+ import CheckboxGroup from '.';
4
+
5
+ const options = ['ok', 'warning', 'critical', 'unknown'];
6
+
7
+ const values = ['ok', 'critical'];
8
+
9
+ export default {
10
+ argTypes: {},
11
+ component: CheckboxGroup,
12
+ title: 'Checkbox/Multi'
13
+ } as ComponentMeta<typeof CheckboxGroup>;
14
+
15
+ const Template: ComponentStory<typeof CheckboxGroup> = (args) => (
16
+ <CheckboxGroup {...args} />
17
+ );
18
+
19
+ export const Playground = Template.bind({});
20
+
21
+ Playground.args = {
22
+ options,
23
+ values
24
+ };
25
+
26
+ export const Horizontal = Template.bind({});
27
+
28
+ Horizontal.args = {
29
+ direction: 'horizontal',
30
+ options,
31
+ values
32
+ };
33
+
34
+ export const LabelOnTop = Template.bind({});
35
+
36
+ LabelOnTop.args = {
37
+ direction: 'horizontal',
38
+ labelPlacement: 'top',
39
+ options,
40
+ values
41
+ };
@@ -0,0 +1,66 @@
1
+ import { equals, includes } from 'ramda';
2
+ import { makeStyles } from 'tss-react/mui';
3
+
4
+ import { FormGroup } from '@mui/material';
5
+
6
+ import Checkbox, { LabelPlacement } from '../Checkbox';
7
+
8
+ interface Props {
9
+ dataTestId?: string;
10
+ direction?: 'horizontal' | 'vertical';
11
+ disabled?: boolean;
12
+ labelPlacement?: LabelPlacement;
13
+ onChange?: (e) => void;
14
+ options: Array<string>;
15
+ values: Array<string>;
16
+ }
17
+
18
+ const useStyles = makeStyles()((theme) => ({
19
+ checkbox: {
20
+ display: 'flex',
21
+ justifyContent: 'center',
22
+ minWidth: theme.spacing(10)
23
+ },
24
+ container: {
25
+ display: 'flex',
26
+ gap: 1
27
+ }
28
+ }));
29
+
30
+ const CheckboxGroup = ({
31
+ options,
32
+ values,
33
+ direction = 'vertical',
34
+ onChange,
35
+ labelPlacement = 'end',
36
+ disabled = false,
37
+ dataTestId
38
+ }: Props): JSX.Element => {
39
+ const { classes } = useStyles();
40
+
41
+ const row = !equals(direction, 'vertical');
42
+
43
+ return (
44
+ <FormGroup
45
+ className={classes.container}
46
+ data-testid={dataTestId || ''}
47
+ row={row}
48
+ >
49
+ {options.map((value) => {
50
+ return (
51
+ <Checkbox
52
+ checked={includes(value, values)}
53
+ className={classes.checkbox}
54
+ disabled={disabled}
55
+ key={value}
56
+ label={value}
57
+ labelPlacement={labelPlacement}
58
+ onChange={onChange}
59
+ />
60
+ );
61
+ })}
62
+ </FormGroup>
63
+ );
64
+ };
65
+
66
+ export default CheckboxGroup;
@@ -0,0 +1,39 @@
1
+ import { ComponentMeta, ComponentStory } from '@storybook/react';
2
+
3
+ import MailIcon from '@mui/icons-material/MailOutline';
4
+
5
+ import Checkbox from './Checkbox';
6
+
7
+ export default {
8
+ argTypes: {},
9
+ component: Checkbox,
10
+ title: 'Checkbox/Single'
11
+ } as ComponentMeta<typeof Checkbox>;
12
+
13
+ const Template: ComponentStory<typeof Checkbox> = (args) => (
14
+ <Checkbox {...args} />
15
+ );
16
+
17
+ export const Playground = Template.bind({});
18
+
19
+ Playground.args = {
20
+ checked: true,
21
+ label: 'Up'
22
+ };
23
+
24
+ export const WithIcon = Template.bind({});
25
+
26
+ WithIcon.args = {
27
+ Icon: MailIcon,
28
+ checked: true,
29
+ label: 'Mail',
30
+ labelPlacement: 'end'
31
+ };
32
+
33
+ export const LabelOnTop = Template.bind({});
34
+
35
+ LabelOnTop.args = {
36
+ checked: true,
37
+ label: 'Down',
38
+ labelPlacement: 'top'
39
+ };
@@ -0,0 +1,2 @@
1
+ export { default as CheckboxGroup } from './CheckboxGroup';
2
+ export { default as Checkbox } from './Checkbox';
@@ -18,6 +18,7 @@ import { Group } from './Inputs/models';
18
18
 
19
19
  interface Props {
20
20
  children: React.ReactNode;
21
+ className?: string;
21
22
  defaultIsOpen?: boolean;
22
23
  group?: Group;
23
24
  hasGroupTitle: boolean;
@@ -50,9 +51,10 @@ const CollapsibleGroup = ({
50
51
  isCollapsible,
51
52
  group,
52
53
  hasGroupTitle,
53
- defaultIsOpen = false
54
+ defaultIsOpen = false,
55
+ className
54
56
  }: Props): JSX.Element => {
55
- const { classes } = useStyles();
57
+ const { classes, cx } = useStyles();
56
58
  const { t } = useTranslation();
57
59
 
58
60
  const [isOpen, setIsOpen] = useState(isCollapsible && defaultIsOpen);
@@ -61,6 +63,8 @@ const CollapsibleGroup = ({
61
63
  setIsOpen((currentIsOpen) => !currentIsOpen);
62
64
  };
63
65
 
66
+ const containerClassName = className || '';
67
+
64
68
  const CollapseIcon = isOpen ? ExpandMore : ChevronRightIcon;
65
69
  const ContainerComponent = useCallback(
66
70
  ({
@@ -72,13 +76,13 @@ const CollapsibleGroup = ({
72
76
  disableGutters
73
77
  disableRipple
74
78
  aria-label={group?.name}
75
- className={classes.groupTitleContainer}
79
+ className={cx(classes.groupTitleContainer, containerClassName)}
76
80
  onClick={toggle}
77
81
  >
78
82
  {containerComponentChildren}
79
83
  </ListItemButton>
80
84
  ) : (
81
- <Box className={classes.groupTitleContainer}>
85
+ <Box className={cx(classes.groupTitleContainer, containerClassName)}>
82
86
  {containerComponentChildren}
83
87
  </Box>
84
88
  ),
@@ -91,7 +95,9 @@ const CollapsibleGroup = ({
91
95
  <ContainerComponent>
92
96
  {isCollapsible && <CollapseIcon />}
93
97
  <div className={classes.groupTitleIcon}>
94
- <Typography variant="h5">{t(group?.name as string)}</Typography>
98
+ <Typography className="groupText" variant="h5">
99
+ {t(group?.name as string)}
100
+ </Typography>
95
101
  {group?.EndIcon && (
96
102
  <Tooltip
97
103
  classes={{
@@ -71,7 +71,7 @@ export const basicFormWithHorizontalGroups = (): JSX.Element => (
71
71
  <Form<BasicForm>
72
72
  {...mandatoryProps}
73
73
  groupDirection={GroupDirection.Horizontal}
74
- groups={basicFormGroups}
74
+ groups={basicFormGroups.filter((group) => group.order !== 3)}
75
75
  />
76
76
  </Paper>
77
77
  );
package/src/Form/Form.tsx CHANGED
@@ -18,8 +18,12 @@ export enum GroupDirection {
18
18
 
19
19
  export type FormProps<T> = {
20
20
  Buttons?: React.ComponentType;
21
+ areGroupsOpen?: boolean;
22
+ children?: JSX.Element;
23
+ className?: string;
21
24
  groupDirection?: GroupDirection;
22
25
  groups?: Array<Group>;
26
+ groupsClassName?: string;
23
27
  initialValues: T;
24
28
  inputs: Array<InputProps>;
25
29
  isCollapsible?: boolean;
@@ -30,6 +34,10 @@ export type FormProps<T> = {
30
34
  } & Omit<FormikSharedConfig<T>, 'isInitialValid'>;
31
35
 
32
36
  const Form = <T extends object>({
37
+ children,
38
+ className,
39
+ groupsClassName,
40
+ areGroupsOpen,
33
41
  initialValues,
34
42
  validate,
35
43
  validationSchema,
@@ -42,13 +50,15 @@ const Form = <T extends object>({
42
50
  groupDirection = GroupDirection.Vertical,
43
51
  ...formikSharedConfig
44
52
  }: FormProps<T>): JSX.Element => {
45
- const { classes } = useStyles();
53
+ const { cx, classes } = useStyles();
46
54
 
47
55
  if (isLoading) {
48
56
  return (
49
57
  <Inputs
50
58
  isLoading
59
+ areGroupsOpen={areGroupsOpen}
51
60
  groups={groups}
61
+ groupsClassName={groupsClassName}
52
62
  inputs={inputs}
53
63
  isCollapsible={isCollapsible}
54
64
  />
@@ -63,14 +73,19 @@ const Form = <T extends object>({
63
73
  onSubmit={submit}
64
74
  {...formikSharedConfig}
65
75
  >
66
- <div className={classes.form}>
67
- <Inputs
68
- groupDirection={groupDirection}
69
- groups={groups}
70
- inputs={inputs}
71
- isCollapsible={isCollapsible}
72
- />
73
- <Buttons />
76
+ <div>
77
+ {children}
78
+ <div className={cx(className, classes.form)}>
79
+ <Inputs
80
+ areGroupsOpen={areGroupsOpen}
81
+ groupDirection={groupDirection}
82
+ groups={groups}
83
+ groupsClassName={groupsClassName}
84
+ inputs={inputs}
85
+ isCollapsible={isCollapsible}
86
+ />
87
+ <Buttons />
88
+ </div>
74
89
  </div>
75
90
  </Formik>
76
91
  );
@@ -0,0 +1,57 @@
1
+ import { ChangeEvent } from 'react';
2
+
3
+ import { FormikValues, useFormikContext } from 'formik';
4
+ import { path, split } from 'ramda';
5
+
6
+ import { Box } from '@mui/material';
7
+
8
+ import { Checkbox as CheckboxComponent } from '../../Checkbox';
9
+ import { useMemoComponent } from '../..';
10
+
11
+ import { InputPropsWithoutGroup } from './models';
12
+
13
+ const Checkbox = ({
14
+ checkbox,
15
+ fieldName,
16
+ getDisabled,
17
+ hideInput,
18
+ dataTestId
19
+ }: InputPropsWithoutGroup): JSX.Element => {
20
+ const { values, setFieldValue } = useFormikContext<FormikValues>();
21
+
22
+ const fieldNamePath = split('.', fieldName);
23
+
24
+ const value = path(fieldNamePath, values);
25
+
26
+ const disabled = getDisabled?.(values) || false;
27
+ const hideCheckbox = hideInput?.(values) || false;
28
+
29
+ const handleChange = (event: ChangeEvent<HTMLInputElement>): void => {
30
+ const newValue = {
31
+ ...value,
32
+ checked: event.target.checked,
33
+ label: event.target.id
34
+ };
35
+
36
+ setFieldValue(fieldName, newValue);
37
+ };
38
+
39
+ return useMemoComponent({
40
+ Component: hideCheckbox ? (
41
+ <Box />
42
+ ) : (
43
+ <CheckboxComponent
44
+ Icon={value?.Icon}
45
+ checked={value?.checked}
46
+ dataTestId={dataTestId || ''}
47
+ disabled={disabled}
48
+ label={value?.label}
49
+ labelPlacement={checkbox?.labelPlacement || 'end'}
50
+ onChange={handleChange}
51
+ />
52
+ ),
53
+ memoProps: [value, disabled, hideCheckbox]
54
+ });
55
+ };
56
+
57
+ export default Checkbox;
@@ -0,0 +1,76 @@
1
+ import { ChangeEvent, useEffect } from 'react';
2
+
3
+ import { FormikValues, useFormikContext } from 'formik';
4
+ import { equals, includes, path, split } from 'ramda';
5
+
6
+ import { Box, Typography } from '@mui/material';
7
+
8
+ import { CheckboxGroup as CheckboxGroupComponent } from '../../Checkbox';
9
+ import { useMemoComponent } from '../..';
10
+
11
+ import { InputPropsWithoutGroup } from './models';
12
+
13
+ const CheckboxGroup = ({
14
+ checkbox,
15
+ fieldName,
16
+ additionalLabel,
17
+ getDisabled,
18
+ hideInput,
19
+ dataTestId
20
+ }: InputPropsWithoutGroup): JSX.Element => {
21
+ const { values, setFieldValue } = useFormikContext<FormikValues>();
22
+
23
+ const fieldNamePath = split('.', fieldName);
24
+
25
+ const value = path(fieldNamePath, values);
26
+
27
+ const disabled = getDisabled?.(values) || false;
28
+ const hideCheckbox = hideInput?.(values) || false;
29
+
30
+ useEffect(() => {
31
+ if (!disabled && !hideCheckbox) {
32
+ return;
33
+ }
34
+ const resetedValue = value?.map((element) => ({
35
+ ...element,
36
+ checked: false
37
+ }));
38
+ setFieldValue(fieldName, resetedValue);
39
+ }, [disabled, hideCheckbox]);
40
+
41
+ const handleChange = (event: ChangeEvent<HTMLInputElement>): void => {
42
+ const label = event.target.id;
43
+ if (!includes(label, value)) {
44
+ setFieldValue(fieldName, [...value, label]);
45
+
46
+ return;
47
+ }
48
+
49
+ setFieldValue(
50
+ fieldName,
51
+ value?.filter((elm) => !equals(elm, label))
52
+ );
53
+ };
54
+
55
+ return useMemoComponent({
56
+ Component: hideCheckbox ? (
57
+ <Box />
58
+ ) : (
59
+ <Box>
60
+ {additionalLabel && <Typography>{additionalLabel}</Typography>}
61
+ <CheckboxGroupComponent
62
+ dataTestId={dataTestId || ''}
63
+ direction={checkbox?.direction}
64
+ disabled={disabled}
65
+ labelPlacement={checkbox?.labelPlacement || 'end'}
66
+ options={checkbox?.options as Array<string>}
67
+ values={value}
68
+ onChange={handleChange}
69
+ />
70
+ </Box>
71
+ ),
72
+ memoProps: [value, disabled, hideCheckbox]
73
+ });
74
+ };
75
+
76
+ export default CheckboxGroup;
@@ -23,14 +23,16 @@ const useStyles = makeStyles<StylesProps>()(
23
23
  );
24
24
 
25
25
  const Grid = ({ grid }: InputPropsWithoutGroup): JSX.Element => {
26
- const { classes } = useStyles({
26
+ const { classes, cx } = useStyles({
27
27
  alignItems: grid?.alignItems,
28
28
  columns: grid?.columns.length,
29
29
  gridTemplateColumns: grid?.gridTemplateColumns
30
30
  });
31
31
 
32
+ const className = grid?.className || '';
33
+
32
34
  return (
33
- <div className={classes.gridFields}>
35
+ <div className={cx(classes.gridFields, className)}>
34
36
  {grid?.columns.map((field) => {
35
37
  const Input = getInput(field.type);
36
38