@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.
- package/package.json +3 -1
- package/src/Dashboard/Layout.tsx +4 -2
- package/src/Graph/BasicComponents/Axes/index.tsx +1 -1
- package/src/Graph/BasicComponents/Lines/RegularLines/index.tsx +6 -3
- package/src/Graph/BasicComponents/Lines/StackedLines/index.tsx +6 -3
- package/src/Graph/BasicComponents/Lines/Threshold/BasicThreshold.tsx +5 -3
- package/src/Graph/BasicComponents/Lines/Threshold/ThresholdWithPatternLines.tsx +5 -1
- package/src/Graph/BasicComponents/Lines/Threshold/ThresholdWithVariation.tsx +5 -1
- package/src/Graph/BasicComponents/Lines/Threshold/index.tsx +5 -1
- package/src/Graph/BasicComponents/Lines/index.tsx +6 -1
- package/src/Graph/BasicComponents/Lines/models.ts +3 -0
- package/src/Graph/Graph.tsx +40 -20
- package/src/Graph/Header/index.tsx +7 -7
- package/src/Graph/InteractiveComponents/AnchorPoint/TooltipAnchorPoint.tsx +48 -22
- package/src/Graph/InteractiveComponents/AnchorPoint/index.tsx +14 -12
- package/src/Graph/InteractiveComponents/TimeShiftZones/TimeShiftZone.tsx +1 -1
- package/src/Graph/index.stories.tsx +10 -0
- package/src/Graph/index.tsx +35 -23
- package/src/Graph/models.ts +1 -1
- package/src/InputField/Select/Autocomplete/Connected/index.tsx +9 -1
- package/src/InputField/Select/Autocomplete/Multi/index.tsx +7 -2
- package/src/RichTextEditor/ContentEditable.tsx +12 -9
- package/src/RichTextEditor/RichTextEditor.cypress.spec.tsx +109 -1
- package/src/RichTextEditor/RichTextEditor.tsx +19 -5
- package/src/RichTextEditor/plugins/AutoLinkPlugin/index.tsx +14 -5
- package/src/RichTextEditor/plugins/FloatingLinkEditorPlugin.tsx +14 -7
- package/src/RichTextEditor/plugins/ToolbarPlugin/BlockButtons.tsx +197 -0
- package/src/RichTextEditor/plugins/ToolbarPlugin/FormatButtons.tsx +3 -10
- package/src/RichTextEditor/plugins/ToolbarPlugin/MacrosButton.tsx +6 -1
- package/src/RichTextEditor/plugins/ToolbarPlugin/ToolbarPlugin.styles.ts +7 -0
- package/src/RichTextEditor/plugins/ToolbarPlugin/UndoRedoButtons.tsx +7 -3
- package/src/RichTextEditor/plugins/ToolbarPlugin/index.tsx +9 -7
- package/src/api/useGraphQuery/index.ts +92 -0
- package/src/api/useGraphQuery/models.ts +12 -0
- package/src/components/ItemComposition/Item.tsx +37 -0
- package/src/components/ItemComposition/ItemComposition.stories.tsx +78 -0
- package/src/components/ItemComposition/ItemComposition.styles.ts +26 -0
- package/src/components/ItemComposition/ItemComposition.tsx +41 -0
- package/src/components/ItemComposition/index.ts +6 -0
- package/src/components/Modal/Modal.styles.ts +1 -1
- package/src/components/index.ts +1 -0
- package/src/index.ts +3 -0
- 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
|
-
|
|
63
|
+
disabled: boolean;
|
|
65
64
|
}
|
|
66
65
|
|
|
67
|
-
const FormatButtons = ({
|
|
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}
|
|
@@ -18,7 +18,11 @@ import { labelRedo, labelUndo } from '../../translatedLabels';
|
|
|
18
18
|
|
|
19
19
|
const LowPriority = 1;
|
|
20
20
|
|
|
21
|
-
|
|
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
|
-
<
|
|
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,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
|
+
};
|
package/src/components/index.ts
CHANGED
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';
|