@centreon/ui 23.10.51 → 23.10.53

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 (43) hide show
  1. package/package.json +3 -1
  2. package/src/Dashboard/Layout.tsx +4 -2
  3. package/src/Graph/BasicComponents/Axes/index.tsx +1 -1
  4. package/src/Graph/BasicComponents/Lines/RegularLines/index.tsx +6 -3
  5. package/src/Graph/BasicComponents/Lines/StackedLines/index.tsx +6 -3
  6. package/src/Graph/BasicComponents/Lines/Threshold/BasicThreshold.tsx +5 -3
  7. package/src/Graph/BasicComponents/Lines/Threshold/ThresholdWithPatternLines.tsx +5 -1
  8. package/src/Graph/BasicComponents/Lines/Threshold/ThresholdWithVariation.tsx +5 -1
  9. package/src/Graph/BasicComponents/Lines/Threshold/index.tsx +5 -1
  10. package/src/Graph/BasicComponents/Lines/index.tsx +6 -1
  11. package/src/Graph/BasicComponents/Lines/models.ts +3 -0
  12. package/src/Graph/Graph.tsx +40 -20
  13. package/src/Graph/Header/index.tsx +7 -7
  14. package/src/Graph/InteractiveComponents/AnchorPoint/TooltipAnchorPoint.tsx +48 -22
  15. package/src/Graph/InteractiveComponents/AnchorPoint/index.tsx +14 -12
  16. package/src/Graph/InteractiveComponents/TimeShiftZones/TimeShiftZone.tsx +1 -1
  17. package/src/Graph/index.stories.tsx +10 -0
  18. package/src/Graph/index.tsx +35 -23
  19. package/src/Graph/models.ts +1 -1
  20. package/src/InputField/Select/Autocomplete/Connected/index.tsx +9 -1
  21. package/src/InputField/Select/Autocomplete/Multi/index.tsx +7 -2
  22. package/src/RichTextEditor/ContentEditable.tsx +12 -9
  23. package/src/RichTextEditor/RichTextEditor.cypress.spec.tsx +109 -1
  24. package/src/RichTextEditor/RichTextEditor.tsx +19 -5
  25. package/src/RichTextEditor/plugins/AutoLinkPlugin/index.tsx +14 -5
  26. package/src/RichTextEditor/plugins/FloatingLinkEditorPlugin.tsx +14 -7
  27. package/src/RichTextEditor/plugins/ToolbarPlugin/BlockButtons.tsx +197 -0
  28. package/src/RichTextEditor/plugins/ToolbarPlugin/FormatButtons.tsx +3 -10
  29. package/src/RichTextEditor/plugins/ToolbarPlugin/MacrosButton.tsx +6 -1
  30. package/src/RichTextEditor/plugins/ToolbarPlugin/ToolbarPlugin.styles.ts +7 -0
  31. package/src/RichTextEditor/plugins/ToolbarPlugin/UndoRedoButtons.tsx +7 -3
  32. package/src/RichTextEditor/plugins/ToolbarPlugin/index.tsx +9 -7
  33. package/src/api/useGraphQuery/index.ts +92 -0
  34. package/src/api/useGraphQuery/models.ts +12 -0
  35. package/src/components/ItemComposition/Item.tsx +37 -0
  36. package/src/components/ItemComposition/ItemComposition.stories.tsx +78 -0
  37. package/src/components/ItemComposition/ItemComposition.styles.ts +26 -0
  38. package/src/components/ItemComposition/ItemComposition.tsx +41 -0
  39. package/src/components/ItemComposition/index.ts +6 -0
  40. package/src/components/Modal/Modal.styles.ts +1 -1
  41. package/src/components/index.ts +1 -0
  42. package/src/index.ts +3 -0
  43. package/src/Graph/InteractiveComponents/AnchorPoint/AnchorPoint.styles.ts +0 -58
@@ -0,0 +1,197 @@
1
+ import { useCallback, useEffect, useState } from 'react';
2
+
3
+ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
4
+ import {
5
+ $isListNode,
6
+ INSERT_ORDERED_LIST_COMMAND,
7
+ INSERT_UNORDERED_LIST_COMMAND,
8
+ ListNode,
9
+ REMOVE_LIST_COMMAND
10
+ } from '@lexical/list';
11
+ import {
12
+ $createParagraphNode,
13
+ $getSelection,
14
+ $isRangeSelection,
15
+ $isRootOrShadowRoot,
16
+ COMMAND_PRIORITY_CRITICAL,
17
+ SELECTION_CHANGE_COMMAND
18
+ } from 'lexical';
19
+ import {
20
+ $createHeadingNode,
21
+ $isHeadingNode,
22
+ HeadingTagType
23
+ } from '@lexical/rich-text';
24
+ import { $setBlocksType } from '@lexical/selection';
25
+ import { T, always, cond, equals, isNil } from 'ramda';
26
+ import {
27
+ $findMatchingParent,
28
+ $getNearestNodeOfType,
29
+ mergeRegister
30
+ } from '@lexical/utils';
31
+
32
+ import { SingleAutocompleteField } from '../../..';
33
+
34
+ import { useBlockButtonsStyles } from './ToolbarPlugin.styles';
35
+
36
+ interface Props {
37
+ disabled: boolean;
38
+ }
39
+
40
+ const blockTypeToBlockName = {
41
+ bullet: 'Bullet List',
42
+ h1: 'Heading 1',
43
+ h2: 'Heading 2',
44
+ h3: 'Heading 3',
45
+ h4: 'Heading 4',
46
+ h5: 'Heading 5',
47
+ h6: 'Heading 6',
48
+ number: 'Number List',
49
+ paragraph: 'Normal'
50
+ };
51
+
52
+ const blockTypes = [
53
+ 'h1',
54
+ 'h2',
55
+ 'h3',
56
+ 'h4',
57
+ 'h5',
58
+ 'h6',
59
+ 'bullet',
60
+ 'number',
61
+ 'paragraph'
62
+ ];
63
+
64
+ const blockTypeOptions = blockTypes.map((blockType) => ({
65
+ id: blockType,
66
+ name: blockTypeToBlockName[blockType]
67
+ }));
68
+
69
+ const BlockButtons = ({ disabled }: Props): JSX.Element => {
70
+ const { classes } = useBlockButtonsStyles();
71
+
72
+ const [blockType, setBlockType] =
73
+ useState<keyof typeof blockTypeToBlockName>('paragraph');
74
+
75
+ const [editor] = useLexicalComposerContext();
76
+
77
+ const formatParagraph = (): void => {
78
+ editor.update(() => {
79
+ const selection = $getSelection();
80
+ $setBlocksType(selection, () => $createParagraphNode());
81
+ });
82
+ };
83
+
84
+ const formatBulletList = (): void => {
85
+ if (blockType !== 'bullet') {
86
+ editor.dispatchCommand(INSERT_UNORDERED_LIST_COMMAND, undefined);
87
+ } else {
88
+ editor.dispatchCommand(REMOVE_LIST_COMMAND, undefined);
89
+ }
90
+ };
91
+
92
+ const formatNumberedList = (): void => {
93
+ if (blockType !== 'number') {
94
+ editor.dispatchCommand(INSERT_ORDERED_LIST_COMMAND, undefined);
95
+ } else {
96
+ editor.dispatchCommand(REMOVE_LIST_COMMAND, undefined);
97
+ }
98
+ };
99
+
100
+ const formatHeading = (headingSize: HeadingTagType): void => {
101
+ if (blockType !== headingSize) {
102
+ editor.update(() => {
103
+ const selection = $getSelection();
104
+ if ($isRangeSelection(selection)) {
105
+ $setBlocksType(selection, () => $createHeadingNode(headingSize));
106
+ }
107
+ });
108
+ }
109
+ };
110
+
111
+ const changeBlockType = (_, newBlockType): void => {
112
+ const formatFunction = cond<Array<string>, (value?) => void>([
113
+ [equals('bullet'), always(formatBulletList)],
114
+ [equals('number'), always(formatNumberedList)],
115
+ [equals('h1'), always(() => formatHeading('h1'))],
116
+ [equals('h2'), always(() => formatHeading('h2'))],
117
+ [equals('h3'), always(() => formatHeading('h3'))],
118
+ [equals('h4'), always(() => formatHeading('h4'))],
119
+ [equals('h5'), always(() => formatHeading('h5'))],
120
+ [equals('h6'), always(() => formatHeading('h6'))],
121
+ [T, always(formatParagraph)]
122
+ ])(newBlockType?.id || '');
123
+
124
+ formatFunction();
125
+ };
126
+
127
+ const updateToolbar = useCallback(() => {
128
+ const selection = $getSelection();
129
+ const anchorNode = selection?.anchor.getNode();
130
+ const element = equals(anchorNode?.getKey(), 'root')
131
+ ? anchorNode
132
+ : $findMatchingParent(anchorNode, (e) => {
133
+ const parent = e.getParent();
134
+
135
+ return parent !== null && $isRootOrShadowRoot(parent);
136
+ }) || anchorNode?.getTopLevelElementOrThrow();
137
+
138
+ const elementKey = element?.getKey();
139
+ const elementDOM = editor.getElementByKey(elementKey);
140
+
141
+ if (isNil(elementDOM)) {
142
+ return;
143
+ }
144
+
145
+ if ($isListNode(element)) {
146
+ const parentList = $getNearestNodeOfType(anchorNode, ListNode);
147
+ const type = parentList
148
+ ? parentList.getListType()
149
+ : element.getListType();
150
+ setBlockType(type);
151
+
152
+ return;
153
+ }
154
+ const type = $isHeadingNode(element) ? element.getTag() : element.getType();
155
+ if (type in blockTypeToBlockName) {
156
+ setBlockType(type as keyof typeof blockTypeToBlockName);
157
+ }
158
+ }, [editor]);
159
+
160
+ const value = blockTypeOptions.find((option) => option.id === blockType);
161
+
162
+ useEffect(() => {
163
+ return editor.registerCommand(
164
+ SELECTION_CHANGE_COMMAND,
165
+ () => {
166
+ updateToolbar();
167
+
168
+ return false;
169
+ },
170
+ COMMAND_PRIORITY_CRITICAL
171
+ );
172
+ }, [editor, updateToolbar]);
173
+
174
+ useEffect(() => {
175
+ return mergeRegister(
176
+ editor.registerUpdateListener(({ editorState }) => {
177
+ editorState.read(() => {
178
+ updateToolbar();
179
+ });
180
+ })
181
+ );
182
+ }, [editor, updateToolbar]);
183
+
184
+ return (
185
+ <SingleAutocompleteField
186
+ className={classes.autocomplete}
187
+ dataTestId="Block type"
188
+ disabled={disabled}
189
+ label=""
190
+ options={blockTypeOptions}
191
+ value={value}
192
+ onChange={changeBlockType}
193
+ />
194
+ );
195
+ };
196
+
197
+ export default BlockButtons;
@@ -5,7 +5,6 @@ import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext
5
5
  import {
6
6
  $getSelection,
7
7
  $isRangeSelection,
8
- EditorState,
9
8
  FORMAT_TEXT_COMMAND,
10
9
  RangeSelection,
11
10
  SELECTION_CHANGE_COMMAND,
@@ -61,10 +60,10 @@ const getSelectedNode = (selection: RangeSelection): ElementNode | TextNode => {
61
60
  };
62
61
 
63
62
  interface Props {
64
- getEditorState?: (editorState: EditorState) => void;
63
+ disabled: boolean;
65
64
  }
66
65
 
67
- const FormatButtons = ({ getEditorState }: Props): JSX.Element => {
66
+ const FormatButtons = ({ disabled }: Props): JSX.Element => {
68
67
  const { classes, cx } = useStyles();
69
68
 
70
69
  const [isBold, setIsBold] = useState(false);
@@ -104,13 +103,6 @@ const FormatButtons = ({ getEditorState }: Props): JSX.Element => {
104
103
 
105
104
  useEffect(() => {
106
105
  return mergeRegister(
107
- editor.registerUpdateListener(({ editorState }) => {
108
- editorState.read(() => {
109
- updateToolbar();
110
- });
111
-
112
- getEditorState?.(editorState);
113
- }),
114
106
  editor.registerCommand(
115
107
  SELECTION_CHANGE_COMMAND,
116
108
  () => {
@@ -169,6 +161,7 @@ const FormatButtons = ({ getEditorState }: Props): JSX.Element => {
169
161
  <IconButton
170
162
  ariaLabel={type}
171
163
  className={cx(isSelected && classes.buttonSelected)}
164
+ disabled={disabled}
172
165
  key={type}
173
166
  size="medium"
174
167
  title={type}
@@ -11,6 +11,10 @@ import { useStyles } from '.';
11
11
 
12
12
  const LowPriority = 1;
13
13
 
14
+ interface Props {
15
+ disabled: boolean;
16
+ }
17
+
14
18
  export const standardMacros = [
15
19
  '{{SHORTDATETIME}}',
16
20
  '{{LONGDATETIME}}',
@@ -22,7 +26,7 @@ export const standardMacros = [
22
26
  '{{STATETYPE}}'
23
27
  ];
24
28
 
25
- const MacrosButton = (): JSX.Element => {
29
+ const MacrosButton = ({ disabled }: Props): JSX.Element => {
26
30
  const { classes } = useStyles();
27
31
  const [arMacrosDiplayed, setAreMacrosDiplayed] = useState(false);
28
32
 
@@ -65,6 +69,7 @@ const MacrosButton = (): JSX.Element => {
65
69
  <IconButton
66
70
  ariaLabel="Macros"
67
71
  className={classes.macrosButton}
72
+ disabled={disabled}
68
73
  title="Macros"
69
74
  tooltipPlacement="top"
70
75
  onClick={displayMacros}
@@ -0,0 +1,7 @@
1
+ import { makeStyles } from 'tss-react/mui';
2
+
3
+ export const useBlockButtonsStyles = makeStyles()((theme) => ({
4
+ autocomplete: {
5
+ width: theme.spacing(17)
6
+ }
7
+ }));
@@ -18,7 +18,11 @@ import { labelRedo, labelUndo } from '../../translatedLabels';
18
18
 
19
19
  const LowPriority = 1;
20
20
 
21
- const UndoRedoButtons = (): JSX.Element => {
21
+ interface Props {
22
+ disabled: boolean;
23
+ }
24
+
25
+ const UndoRedoButtons = ({ disabled }: Props): JSX.Element => {
22
26
  const { t } = useTranslation();
23
27
 
24
28
  const [canUndo, setCanUndo] = useState(false);
@@ -64,7 +68,7 @@ const UndoRedoButtons = (): JSX.Element => {
64
68
  <>
65
69
  <IconButton
66
70
  ariaLabel={translatedUndo}
67
- disabled={!canUndo}
71
+ disabled={!canUndo || disabled}
68
72
  title={translatedUndo}
69
73
  tooltipPlacement="top"
70
74
  onClick={undo}
@@ -73,7 +77,7 @@ const UndoRedoButtons = (): JSX.Element => {
73
77
  </IconButton>
74
78
  <IconButton
75
79
  ariaLabel={translatedRedo}
76
- disabled={!canRedo}
80
+ disabled={!canRedo || disabled}
77
81
  title={translatedRedo}
78
82
  tooltipPlacement="top"
79
83
  onClick={redo}
@@ -1,4 +1,3 @@
1
- import { EditorState } from 'lexical';
2
1
  import { makeStyles } from 'tss-react/mui';
3
2
 
4
3
  import { Divider } from '@mui/material';
@@ -6,11 +5,12 @@ import { Divider } from '@mui/material';
6
5
  import FormatButtons from './FormatButtons';
7
6
  import UndoRedoButtons from './UndoRedoButtons';
8
7
  import MacrosButton from './MacrosButton';
8
+ import BlockButtons from './BlockButtons';
9
9
 
10
10
  interface Props {
11
+ disabled: boolean;
11
12
  displayMacrosButton?: boolean;
12
13
  editable: boolean;
13
- getEditorState?: (editorState: EditorState) => void;
14
14
  }
15
15
 
16
16
  export const useStyles = makeStyles()((theme) => ({
@@ -31,21 +31,23 @@ export const useStyles = makeStyles()((theme) => ({
31
31
  }));
32
32
 
33
33
  const ToolbarPlugin = ({
34
- getEditorState,
35
34
  editable,
36
- displayMacrosButton
35
+ displayMacrosButton,
36
+ disabled
37
37
  }: Props): JSX.Element | null => {
38
38
  const { classes } = useStyles();
39
39
 
40
40
  return editable ? (
41
41
  <div className={classes.container}>
42
- <UndoRedoButtons />
42
+ <UndoRedoButtons disabled={disabled} />
43
43
  <Divider flexItem orientation="vertical" />
44
- <FormatButtons getEditorState={getEditorState} />
44
+ <BlockButtons disabled={disabled} />
45
+ <Divider flexItem orientation="vertical" />
46
+ <FormatButtons disabled={disabled} />
45
47
  {displayMacrosButton && (
46
48
  <>
47
49
  <Divider flexItem orientation="vertical" />
48
- <MacrosButton />
50
+ <MacrosButton disabled={disabled} />
49
51
  </>
50
52
  )}
51
53
  </div>
@@ -0,0 +1,92 @@
1
+ import { T, always, cond, flatten, isEmpty, join, pipe, pluck } from 'ramda';
2
+ import dayjs from 'dayjs';
3
+
4
+ import { GraphData, useFetchQuery } from '../..';
5
+
6
+ import { ServiceMetric } from './models';
7
+
8
+ interface UseMetricsQueryProps {
9
+ baseEndpoint: string;
10
+ metrics: Array<ServiceMetric>;
11
+ timePeriod?: TimePeriod;
12
+ }
13
+
14
+ interface UseMetricsQueryState {
15
+ end: string;
16
+ graphData: GraphData | undefined;
17
+ isGraphLoading: boolean;
18
+ isMetricIdsEmpty: boolean;
19
+ start: string;
20
+ }
21
+
22
+ enum TimePeriod {
23
+ lastDay = 'last_day'
24
+ }
25
+
26
+ const getStartEndFromTimePeriod = (
27
+ timePeriod: TimePeriod
28
+ ): { end: string; start: string } => {
29
+ return cond([
30
+ [
31
+ T,
32
+ always({
33
+ end: dayjs().toISOString(),
34
+ start: dayjs().subtract(1, 'day').toISOString()
35
+ })
36
+ ]
37
+ ])(timePeriod);
38
+ };
39
+
40
+ interface PerformanceGraphData extends Omit<GraphData, 'global'> {
41
+ base: number;
42
+ }
43
+
44
+ const useGraphQuery = ({
45
+ metrics,
46
+ baseEndpoint,
47
+ timePeriod = TimePeriod.lastDay
48
+ }: UseMetricsQueryProps): UseMetricsQueryState => {
49
+ const metricIds = pipe(
50
+ pluck('metrics'),
51
+ flatten,
52
+ pluck('id'),
53
+ join(',')
54
+ )(metrics);
55
+
56
+ const { end, start } = getStartEndFromTimePeriod(timePeriod);
57
+
58
+ const {
59
+ data: graphData,
60
+ isFetching,
61
+ isLoading
62
+ } = useFetchQuery<PerformanceGraphData>({
63
+ getEndpoint: () =>
64
+ `${baseEndpoint}?metricIds=[${metricIds}]&start=${start}&end=${end}`,
65
+ getQueryKey: () => ['graph', metricIds],
66
+ queryOptions: {
67
+ enabled: !isEmpty(metricIds),
68
+ suspense: false
69
+ }
70
+ });
71
+
72
+ const formattedGraphData = graphData
73
+ ? {
74
+ global: {
75
+ base: graphData.base,
76
+ title: ''
77
+ },
78
+ metrics: graphData.metrics,
79
+ times: graphData.times
80
+ }
81
+ : undefined;
82
+
83
+ return {
84
+ end,
85
+ graphData: formattedGraphData,
86
+ isGraphLoading: isFetching || (isLoading && !isEmpty(metricIds)),
87
+ isMetricIdsEmpty: isEmpty(metricIds),
88
+ start
89
+ };
90
+ };
91
+
92
+ export default useGraphQuery;
@@ -0,0 +1,12 @@
1
+ interface NamedEntity {
2
+ id: number;
3
+ name: string;
4
+ }
5
+
6
+ interface Metric extends NamedEntity {
7
+ unit: string;
8
+ }
9
+
10
+ export interface ServiceMetric extends NamedEntity {
11
+ metrics: Array<Metric>;
12
+ }
@@ -0,0 +1,37 @@
1
+ import { ReactElement } from 'react';
2
+
3
+ import CloseIcon from '@mui/icons-material/Close';
4
+
5
+ import { IconButton } from '..';
6
+
7
+ import { useItemStyles } from './ItemComposition.styles';
8
+
9
+ type Props = {
10
+ children: Array<ReactElement>;
11
+ className?: string;
12
+ labelDelete: string;
13
+ onDeleteItem: () => void;
14
+ };
15
+
16
+ export const Item = ({
17
+ onDeleteItem,
18
+ children,
19
+ labelDelete,
20
+ className
21
+ }: Props): JSX.Element => {
22
+ const { classes, cx } = useItemStyles();
23
+
24
+ return (
25
+ <div className={classes.itemContainer}>
26
+ <div className={cx(classes.itemContent, className)}>{children}</div>
27
+ <IconButton
28
+ aria-label={labelDelete}
29
+ data-testid={labelDelete}
30
+ icon={<CloseIcon />}
31
+ size="small"
32
+ variant="ghost"
33
+ onClick={onDeleteItem}
34
+ />
35
+ </div>
36
+ );
37
+ };
@@ -0,0 +1,78 @@
1
+ /* eslint-disable react/no-array-index-key */
2
+ import { Meta, StoryObj } from '@storybook/react';
3
+
4
+ import { Typography } from '@mui/material';
5
+
6
+ import { SelectField } from '../..';
7
+
8
+ import { ItemComposition } from '.';
9
+
10
+ const meta: Meta<typeof ItemComposition> = {
11
+ component: ItemComposition
12
+ };
13
+
14
+ export default meta;
15
+ type Story = StoryObj<typeof ItemComposition>;
16
+
17
+ const stub = (): void => undefined;
18
+
19
+ export const Default: Story = {
20
+ args: {
21
+ children: [...Array(5)].map((_, i) => (
22
+ <ItemComposition.Item key={i} labelDelete="Delete" onDeleteItem={stub}>
23
+ <Typography>Item 1</Typography>
24
+ <Typography>Item 2</Typography>
25
+ </ItemComposition.Item>
26
+ )),
27
+ labelAdd: 'Add',
28
+ onAddItem: stub
29
+ }
30
+ };
31
+
32
+ export const Empty: Story = {
33
+ args: {
34
+ children: [],
35
+ labelAdd: 'Add',
36
+ onAddItem: stub
37
+ }
38
+ };
39
+
40
+ const options = [
41
+ {
42
+ id: 1,
43
+ name: 'Test select 1'
44
+ },
45
+ {
46
+ id: 2,
47
+ name: 'Test select 2'
48
+ },
49
+ {
50
+ id: 3,
51
+ name: 'Test select 3'
52
+ }
53
+ ];
54
+
55
+ export const WithSelectInputs: Story = {
56
+ args: {
57
+ children: [...Array(5)].map((_, i) => (
58
+ <ItemComposition.Item key={i} labelDelete="Delete" onDeleteItem={stub}>
59
+ <SelectField
60
+ dataTestId="select 1"
61
+ label="select 1"
62
+ options={options}
63
+ selectedOptionId={1}
64
+ onChange={stub}
65
+ />
66
+ <SelectField
67
+ dataTestId="select 2"
68
+ label="select 2"
69
+ options={options}
70
+ selectedOptionId={2}
71
+ onChange={stub}
72
+ />
73
+ </ItemComposition.Item>
74
+ )),
75
+ labelAdd: 'Add',
76
+ onAddItem: stub
77
+ }
78
+ };
@@ -0,0 +1,26 @@
1
+ import { makeStyles } from 'tss-react/mui';
2
+
3
+ export const useItemCompositionStyles = makeStyles()((theme) => ({
4
+ itemCompositionContainer: {
5
+ alignItems: 'flex-start',
6
+ display: 'flex',
7
+ flexDirection: 'column',
8
+ gap: theme.spacing(2),
9
+ width: '100%'
10
+ }
11
+ }));
12
+
13
+ export const useItemStyles = makeStyles()((theme) => ({
14
+ itemContainer: {
15
+ alignItems: 'center',
16
+ display: 'flex',
17
+ flexDirection: 'row',
18
+ gap: theme.spacing(1),
19
+ width: '100%'
20
+ },
21
+ itemContent: {
22
+ display: 'flex',
23
+ flexDirection: 'row',
24
+ width: '100%'
25
+ }
26
+ }));
@@ -0,0 +1,41 @@
1
+ import { ReactElement } from 'react';
2
+
3
+ import AddIcon from '@mui/icons-material/Add';
4
+
5
+ import { Button } from '..';
6
+
7
+ import { useItemCompositionStyles } from './ItemComposition.styles';
8
+
9
+ type Props = {
10
+ addbuttonDisabled?: boolean;
11
+ children: Array<ReactElement>;
12
+ labelAdd: string;
13
+ onAddItem: () => void;
14
+ };
15
+
16
+ export const ItemComposition = ({
17
+ onAddItem,
18
+ children,
19
+ labelAdd,
20
+ addbuttonDisabled
21
+ }: Props): JSX.Element => {
22
+ const { classes } = useItemCompositionStyles();
23
+
24
+ return (
25
+ <div className={classes.itemCompositionContainer}>
26
+ {children}
27
+ <Button
28
+ aria-label={labelAdd}
29
+ data-testid={labelAdd}
30
+ disabled={addbuttonDisabled}
31
+ icon={<AddIcon />}
32
+ iconVariant="start"
33
+ size="small"
34
+ variant="ghost"
35
+ onClick={onAddItem}
36
+ >
37
+ {labelAdd}
38
+ </Button>
39
+ </div>
40
+ );
41
+ };
@@ -0,0 +1,6 @@
1
+ import { ItemComposition as ItemCompositionRoot } from './ItemComposition';
2
+ import { Item } from './Item';
3
+
4
+ export const ItemComposition = Object.assign(ItemCompositionRoot, {
5
+ Item
6
+ });
@@ -17,7 +17,7 @@ const useStyles = makeStyles()((theme) => ({
17
17
  width: '400px'
18
18
  },
19
19
  '&[data-size="xlarge"] .MuiDialog-paper': {
20
- maxWidth: '1200px',
20
+ maxWidth: '1400px',
21
21
  width: 'calc(100% - 64px)'
22
22
  }
23
23
  },
@@ -7,3 +7,4 @@ export * from './Layout';
7
7
  export * from './Tooltip';
8
8
  export * from './Menu';
9
9
  export * from './List';
10
+ export * from './ItemComposition';
package/src/index.ts CHANGED
@@ -123,6 +123,7 @@ export type {
123
123
  export { default as Responsive } from './Responsive';
124
124
  export { default as useFetchQuery } from './api/useFetchQuery';
125
125
  export { default as useMutationQuery, Method } from './api/useMutationQuery';
126
+ export { default as useGraphQuery } from './api/useGraphQuery';
126
127
  export { default as QueryProvider } from './api/QueryProvider';
127
128
  export {
128
129
  default as FileDropZone,
@@ -143,3 +144,5 @@ export type { ResponseError, CatchErrorProps } from './api/customFetch';
143
144
  export * from './utils';
144
145
  export { default as FluidTypography } from './Typography/FluidTypography';
145
146
  export * from './Dashboard';
147
+ export { default as Graph } from './Graph';
148
+ export type { GraphData } from './Graph/models';