@centreon/ui 23.10.92 → 23.10.93

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": "23.10.92",
3
+ "version": "23.10.93",
4
4
  "description": "Centreon UI Components",
5
5
  "scripts": {
6
6
  "eslint": "eslint ./src --ext .js,.jsx,.ts,.tsx --max-warnings 0",
@@ -0,0 +1,116 @@
1
+ import { makeStyles } from 'tss-react/mui';
2
+ import { equals } from 'ramda';
3
+
4
+ import { Theme, alpha } from '@mui/material';
5
+
6
+ interface GetBackgroundAndColorProps {
7
+ status: string;
8
+ theme: Theme;
9
+ }
10
+
11
+ const getColor = ({ theme, status }: GetBackgroundAndColorProps): string =>
12
+ equals(theme.palette.mode, 'dark')
13
+ ? theme.palette.common.white
14
+ : theme.palette[status].main;
15
+
16
+ export const useStyles = makeStyles()((theme) => ({
17
+ item: {
18
+ '& .MuiListItemText-secondary': {
19
+ textWrap: 'pretty'
20
+ },
21
+ '&[data-variant="error"]': {
22
+ '& .MuiListItemIcon-root': {
23
+ color: getColor({ status: 'error', theme })
24
+ },
25
+ '&:hover': {
26
+ background: alpha(
27
+ theme.palette.error.main,
28
+ theme.palette.action.focusOpacity
29
+ ),
30
+ color: getColor({ status: 'error', theme })
31
+ },
32
+ color: theme.palette.error.main
33
+ },
34
+ '&[data-variant="info"]': {
35
+ '& .MuiListItemIcon-root': {
36
+ color: getColor({ status: 'info', theme })
37
+ },
38
+ '&:hover': {
39
+ background: alpha(
40
+ theme.palette.info.main,
41
+ theme.palette.action.focusOpacity
42
+ ),
43
+ color: getColor({ status: 'info', theme })
44
+ },
45
+ color: theme.palette.info.main
46
+ },
47
+ '&[data-variant="pending"]': {
48
+ '& .MuiListItemIcon-root': {
49
+ color: getColor({ status: 'pending', theme })
50
+ },
51
+ '&:hover': {
52
+ background: alpha(
53
+ theme.palette.pending.main,
54
+ theme.palette.action.focusOpacity
55
+ ),
56
+ color: getColor({ status: 'pending', theme })
57
+ },
58
+ color: theme.palette.pending.main
59
+ },
60
+ '&[data-variant="primary"]': {
61
+ '& .MuiListItemIcon-root': {
62
+ color: getColor({ status: 'primary', theme })
63
+ },
64
+ '&:hover': {
65
+ background: alpha(
66
+ theme.palette.primary.main,
67
+ theme.palette.action.focusOpacity
68
+ ),
69
+ color: getColor({ status: 'primary', theme })
70
+ },
71
+ color: theme.palette.primary.main
72
+ },
73
+ '&[data-variant="secondary"]': {
74
+ '& .MuiListItemIcon-root': {
75
+ color: getColor({ status: 'secondary', theme })
76
+ },
77
+ '&:hover': {
78
+ background: alpha(
79
+ theme.palette.secondary.main,
80
+ theme.palette.action.focusOpacity
81
+ ),
82
+ color: getColor({ status: 'secondary', theme })
83
+ },
84
+ color: theme.palette.secondary.main
85
+ },
86
+ '&[data-variant="success"]': {
87
+ '& .MuiListItemIcon-root': {
88
+ color: getColor({ status: 'success', theme })
89
+ },
90
+ '&:hover': {
91
+ background: alpha(
92
+ theme.palette.success.main,
93
+ theme.palette.action.focusOpacity
94
+ ),
95
+ color: getColor({ status: 'success', theme })
96
+ },
97
+ color: theme.palette.success.main
98
+ },
99
+ '&[data-variant="warning"]': {
100
+ '& .MuiListItemIcon-root': {
101
+ color: getColor({ status: 'warning', theme })
102
+ },
103
+ '&:hover': {
104
+ background: alpha(
105
+ theme.palette.warning.main,
106
+ theme.palette.action.focusOpacity
107
+ ),
108
+ color: getColor({ status: 'warning', theme })
109
+ },
110
+ color: theme.palette.warning.main
111
+ }
112
+ },
113
+ list: {
114
+ width: '100%'
115
+ }
116
+ }));
@@ -24,6 +24,68 @@ const actions = [
24
24
  }
25
25
  ];
26
26
 
27
+ const actionsWithVariants = [
28
+ {
29
+ label: 'No variant',
30
+ onClick: (): void => undefined
31
+ },
32
+ {
33
+ label: 'Primary',
34
+ onClick: (): void => undefined,
35
+ variant: 'primary'
36
+ },
37
+ {
38
+ label: 'Secondary',
39
+ onClick: (): void => undefined,
40
+ variant: 'secondary'
41
+ },
42
+ {
43
+ label: 'Success',
44
+ onClick: (): void => undefined,
45
+ variant: 'success'
46
+ },
47
+ {
48
+ label: 'Warning',
49
+ onClick: (): void => undefined,
50
+ variant: 'warning'
51
+ },
52
+ {
53
+ label: 'Error',
54
+ onClick: (): void => undefined,
55
+ variant: 'error'
56
+ },
57
+ {
58
+ label: 'Info',
59
+ onClick: (): void => undefined,
60
+ variant: 'info'
61
+ },
62
+ {
63
+ label: 'Pending',
64
+ onClick: (): void => undefined,
65
+ variant: 'pending'
66
+ }
67
+ ];
68
+
69
+ const actionsWithSecondaryLabel = [
70
+ {
71
+ Icon: EditIcon,
72
+ label: 'Edit',
73
+ onClick: (): void => undefined
74
+ },
75
+ {
76
+ Icon: CopyIcon,
77
+ label: 'Duplicate',
78
+ onClick: (): void => undefined,
79
+ secondaryLabel:
80
+ 'This is a secondary label that the purpose is to give a small description about an action'
81
+ },
82
+ {
83
+ Icon: DeleteIcon,
84
+ label: 'Delete',
85
+ onClick: (): void => undefined
86
+ }
87
+ ];
88
+
27
89
  export default {
28
90
  argTypes: {},
29
91
  component: ActionsList,
@@ -39,3 +101,15 @@ export const Playground = Template.bind({});
39
101
  Playground.args = {
40
102
  actions
41
103
  };
104
+
105
+ export const Variants = Template.bind({});
106
+
107
+ Variants.args = {
108
+ actions: actionsWithVariants
109
+ };
110
+
111
+ export const SecondaryLabel = Template.bind({});
112
+
113
+ SecondaryLabel.args = {
114
+ actions: actionsWithSecondaryLabel
115
+ };
@@ -1,5 +1,4 @@
1
1
  /* eslint-disable react/no-array-index-key */
2
- import { makeStyles } from 'tss-react/mui';
3
2
  import { useTranslation } from 'react-i18next';
4
3
  import { equals } from 'ramda';
5
4
 
@@ -12,10 +11,15 @@ import {
12
11
  Divider
13
12
  } from '@mui/material';
14
13
 
14
+ import { useStyles } from './ActionsList.styles';
15
+ import { ActionVariants } from './models';
16
+
15
17
  interface ActionsType {
16
18
  Icon?: (props: SvgIconProps) => JSX.Element;
17
19
  label: string;
18
20
  onClick?: (e?) => void;
21
+ secondaryLabel?: string;
22
+ variant?: ActionVariants;
19
23
  }
20
24
 
21
25
  interface Props {
@@ -24,12 +28,6 @@ interface Props {
24
28
  listItemClassName?: string;
25
29
  }
26
30
 
27
- const useStyles = makeStyles()({
28
- list: {
29
- maxWidth: '100%'
30
- }
31
- });
32
-
33
31
  const ActionsList = ({
34
32
  className,
35
33
  listItemClassName,
@@ -45,18 +43,28 @@ const ActionsList = ({
45
43
  return <Divider key={`divider_${idx}`} />;
46
44
  }
47
45
 
48
- const { label, Icon, onClick } = action as ActionsType;
46
+ const { label, Icon, onClick, variant, secondaryLabel } =
47
+ action as ActionsType;
49
48
 
50
49
  return (
51
- <MenuItem aria-label={label} id={label} key={label} onClick={onClick}>
50
+ <MenuItem
51
+ aria-label={label}
52
+ className={classes.item}
53
+ data-variant={variant}
54
+ id={label}
55
+ key={label}
56
+ onClick={onClick}
57
+ >
52
58
  {Icon && (
53
59
  <ListItemIcon>
54
60
  <Icon fontSize="small" />
55
61
  </ListItemIcon>
56
62
  )}
57
- <ListItemText className={listItemClassName}>
58
- {t(label)}
59
- </ListItemText>
63
+ <ListItemText
64
+ className={listItemClassName}
65
+ primary={t(label)}
66
+ secondary={secondaryLabel && t(secondaryLabel)}
67
+ />
60
68
  </MenuItem>
61
69
  );
62
70
  })}
@@ -0,0 +1,8 @@
1
+ export type ActionVariants =
2
+ | 'primary'
3
+ | 'secondary'
4
+ | 'success'
5
+ | 'warning'
6
+ | 'error'
7
+ | 'info'
8
+ | 'pending';
@@ -12,7 +12,6 @@ import {
12
12
  export type LabelPlacement = 'bottom' | 'top' | 'end' | 'start' | undefined;
13
13
 
14
14
  interface StyleProps {
15
- disabled: boolean;
16
15
  hasIcon: boolean;
17
16
  labelPlacement: LabelPlacement;
18
17
  }
@@ -26,7 +25,13 @@ const getLabelSpacing = (labelPlacement, theme): string => {
26
25
  };
27
26
 
28
27
  const useStyles = makeStyles<StyleProps>()(
29
- (theme, { disabled, hasIcon, labelPlacement }) => ({
28
+ (theme, { hasIcon, labelPlacement }) => ({
29
+ checkbox: {
30
+ '&.Mui-checked': {
31
+ color: theme.palette.primary.main
32
+ },
33
+ color: theme.palette.primary.main
34
+ },
30
35
  container: hasIcon
31
36
  ? {
32
37
  alignItems: 'center',
@@ -40,13 +45,8 @@ const useStyles = makeStyles<StyleProps>()(
40
45
  fontSize: theme.spacing(10)
41
46
  },
42
47
  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,
48
+ fontSize: theme.typography.body2.fontSize,
49
+ fontWeight: theme.typography.fontWeightMedium,
50
50
  padding: getLabelSpacing(labelPlacement, theme)
51
51
  }
52
52
  })
@@ -74,7 +74,6 @@ const Checkbox = ({
74
74
  dataTestId
75
75
  }: Props): JSX.Element => {
76
76
  const { classes, cx } = useStyles({
77
- disabled,
78
77
  hasIcon: !!Icon,
79
78
  labelPlacement
80
79
  });
@@ -86,6 +85,7 @@ const Checkbox = ({
86
85
  control={
87
86
  <MuiCheckbox
88
87
  checked={checked}
88
+ className={classes.checkbox}
89
89
  color="primary"
90
90
  disabled={disabled}
91
91
  id={label}
@@ -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
- }));