@centreon/ui 24.6.2 → 24.6.4

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 (54) hide show
  1. package/package.json +1 -1
  2. package/src/Graph/BarChart/BarChart.cypress.spec.tsx +233 -0
  3. package/src/Graph/BarChart/BarChart.stories.tsx +213 -0
  4. package/src/Graph/BarChart/BarChart.tsx +101 -0
  5. package/src/Graph/BarChart/BarGroup.tsx +246 -0
  6. package/src/Graph/BarChart/BarStack.tsx +126 -0
  7. package/src/Graph/BarChart/ResponsiveBarChart.tsx +263 -0
  8. package/src/Graph/BarChart/SingleBar.tsx +62 -0
  9. package/src/Graph/BarChart/Tooltip/BarChartTooltip.tsx +122 -0
  10. package/src/Graph/BarChart/Tooltip/useBarChartTooltipStyles.ts +28 -0
  11. package/src/Graph/BarChart/atoms.ts +5 -0
  12. package/src/Graph/BarChart/models.ts +21 -0
  13. package/src/Graph/BarChart/useBarChartStyles.tsx +5 -0
  14. package/src/Graph/BarChart/useBarStack.ts +114 -0
  15. package/src/Graph/BarChart/useSingleBar.ts +199 -0
  16. package/src/Graph/Gauge/Gauge.cypress.spec.tsx +1 -1
  17. package/src/Graph/Gauge/Gauge.stories.tsx +1 -1
  18. package/src/Graph/LineChart/LineChart.cypress.spec.tsx +4 -4
  19. package/src/Graph/LineChart/LineChart.styles.ts +0 -5
  20. package/src/Graph/LineChart/LineChart.tsx +118 -122
  21. package/src/Graph/LineChart/index.stories.tsx +12 -12
  22. package/src/Graph/SingleBar/SingleBar.cypress.spec.tsx +1 -1
  23. package/src/Graph/SingleBar/SingleBar.stories.tsx +1 -1
  24. package/src/Graph/Text/Text.cypress.spec.tsx +1 -1
  25. package/src/Graph/Text/Text.stories.tsx +1 -1
  26. package/src/Graph/common/Axes/index.tsx +40 -13
  27. package/src/Graph/common/BaseChart/BaseChart.tsx +25 -6
  28. package/src/Graph/common/BaseChart/ChartSvgWrapper.tsx +15 -4
  29. package/src/Graph/{LineChart → common/BaseChart}/Header/index.tsx +3 -3
  30. package/src/Graph/common/BaseChart/Header/models.ts +4 -0
  31. package/src/Graph/common/BaseChart/Header/useHeaderStyles.ts +9 -0
  32. package/src/Graph/common/BaseChart/useBaseChartStyles.ts +6 -0
  33. package/src/Graph/{LineChart/BasicComponents → common/Thresholds}/ThresholdLine.tsx +20 -10
  34. package/src/Graph/{LineChart/BasicComponents → common/Thresholds}/Thresholds.tsx +8 -4
  35. package/src/Graph/common/timeSeries/index.ts +33 -11
  36. package/src/Graph/common/timeSeries/models.ts +1 -0
  37. package/src/Graph/common/useTooltipStyles.ts +1 -0
  38. package/src/Graph/mockedData/pingService.json +203 -0
  39. package/src/Graph/mockedData/pingServiceMixedStacked.json +203 -0
  40. package/src/Graph/mockedData/pingServiceStacked.json +203 -0
  41. /package/src/Graph/{LineChart/mockedData → mockedData}/annotationData.json +0 -0
  42. /package/src/Graph/{LineChart/mockedData → mockedData}/curvesWithSameColor.json +0 -0
  43. /package/src/Graph/{LineChart/mockedData → mockedData}/exclusionPeriodFirstPeriod.json +0 -0
  44. /package/src/Graph/{LineChart/mockedData → mockedData}/exclusionPeriodSecondPeriod.json +0 -0
  45. /package/src/Graph/{LineChart/mockedData → mockedData}/exclusionPeriodThirdPeriod.json +0 -0
  46. /package/src/Graph/{LineChart/mockedData → mockedData}/lastDay.json +0 -0
  47. /package/src/Graph/{LineChart/mockedData → mockedData}/lastDayAreaStack.json +0 -0
  48. /package/src/Graph/{LineChart/mockedData → mockedData}/lastDayForward.json +0 -0
  49. /package/src/Graph/{LineChart/mockedData → mockedData}/lastDayThreshold.json +0 -0
  50. /package/src/Graph/{LineChart/mockedData → mockedData}/lastDayWithIncompleteValues.json +0 -0
  51. /package/src/Graph/{LineChart/mockedData → mockedData}/lastDayWithNullValues.json +0 -0
  52. /package/src/Graph/{LineChart/mockedData → mockedData}/lastMonth.json +0 -0
  53. /package/src/Graph/{LineChart/mockedData → mockedData}/lastWeek.json +0 -0
  54. /package/src/Graph/{LineChart/mockedData → mockedData}/zoomPreview.json +0 -0
@@ -0,0 +1,122 @@
1
+ import { useAtomValue } from 'jotai';
2
+ import {
3
+ always,
4
+ cond,
5
+ equals,
6
+ filter,
7
+ gt,
8
+ has,
9
+ isNil,
10
+ path,
11
+ prop,
12
+ reverse,
13
+ sortBy,
14
+ T
15
+ } from 'ramda';
16
+
17
+ import { Box, Typography } from '@mui/material';
18
+
19
+ import { TimeValue } from '../../common/timeSeries/models';
20
+ import { useLocaleDateTimeFormat } from '../../../utils';
21
+ import { formatMetricValueWithUnit } from '../../common/timeSeries';
22
+ import { tooltipDataAtom } from '../atoms';
23
+ import { Tooltip } from '../../LineChart/models';
24
+
25
+ import { useBarChartTooltipStyles } from './useBarChartTooltipStyles';
26
+
27
+ interface Props extends Partial<Pick<Tooltip, 'mode' | 'sortOrder'>> {
28
+ base: number;
29
+ timeSeries: Array<TimeValue>;
30
+ }
31
+
32
+ const BarChartTooltip = ({
33
+ timeSeries,
34
+ base,
35
+ mode,
36
+ sortOrder
37
+ }: Props): JSX.Element | null => {
38
+ const { classes } = useBarChartTooltipStyles();
39
+ const { toDate, toTime } = useLocaleDateTimeFormat();
40
+ const tooltipData = useAtomValue(tooltipDataAtom);
41
+
42
+ if (isNil(tooltipData)) {
43
+ return null;
44
+ }
45
+
46
+ if (has('thresholdLabel', tooltipData)) {
47
+ return <Typography>{tooltipData.thresholdLabel}</Typography>;
48
+ }
49
+
50
+ const date = timeSeries[tooltipData.index].timeTick;
51
+ const formattedDateTime = `${toDate(date)} / ${toTime(date)}`;
52
+
53
+ const isSingleMode = equals(mode, 'single');
54
+
55
+ const filteredMetrics = isSingleMode
56
+ ? filter(
57
+ ({ metric }) => equals(metric.metric_id, tooltipData.highlightedMetric),
58
+ tooltipData.data
59
+ )
60
+ : tooltipData.data;
61
+
62
+ const displayHighLightedMetric = gt(filteredMetrics.length, 1);
63
+
64
+ const sortedMetrics = cond([
65
+ [equals('name'), always(sortBy(path(['metric', 'name']), filteredMetrics))],
66
+ [equals('ascending'), always(sortBy(prop('value'), filteredMetrics))],
67
+ [
68
+ equals('descending'),
69
+ always(reverse(sortBy(prop('value'), filteredMetrics)))
70
+ ],
71
+ [T, always(filteredMetrics)]
72
+ ])(sortOrder);
73
+
74
+ return (
75
+ <div className={classes.tooltipContainer}>
76
+ <Typography fontWeight="bold" textAlign="center">
77
+ {formattedDateTime}
78
+ </Typography>
79
+ <div className={classes.metrics}>
80
+ {sortedMetrics.map(({ metric, value }) => (
81
+ <div
82
+ className={classes.metric}
83
+ data-metric={metric.name}
84
+ key={metric.metric_id}
85
+ >
86
+ <Box
87
+ className={classes.metricColorBox}
88
+ sx={{ backgroundColor: metric.color }}
89
+ />
90
+ <Typography
91
+ className={classes.metricName}
92
+ fontWeight={
93
+ displayHighLightedMetric &&
94
+ equals(tooltipData.highlightedMetric, metric.metric_id)
95
+ ? 'bold'
96
+ : 'regular'
97
+ }
98
+ >
99
+ {metric.name}
100
+ </Typography>
101
+ <Typography
102
+ fontWeight={
103
+ displayHighLightedMetric &&
104
+ equals(tooltipData.highlightedMetric, metric.metric_id)
105
+ ? 'bold'
106
+ : 'regular'
107
+ }
108
+ >
109
+ {formatMetricValueWithUnit({
110
+ base,
111
+ unit: metric.unit,
112
+ value
113
+ })}
114
+ </Typography>
115
+ </div>
116
+ ))}
117
+ </div>
118
+ </div>
119
+ );
120
+ };
121
+
122
+ export default BarChartTooltip;
@@ -0,0 +1,28 @@
1
+ import { makeStyles } from 'tss-react/mui';
2
+
3
+ export const useBarChartTooltipStyles = makeStyles()((theme) => ({
4
+ metric: {
5
+ alignItems: 'center',
6
+ display: 'flex',
7
+ gap: theme.spacing(1),
8
+ weidth: '100%'
9
+ },
10
+ metricColorBox: {
11
+ borderRadius: theme.shape.borderRadius,
12
+ flexShrink: 0,
13
+ height: theme.spacing(1.5),
14
+ width: theme.spacing(1.5)
15
+ },
16
+ metricName: {
17
+ flexGrow: 1
18
+ },
19
+ metrics: {
20
+ display: 'flex',
21
+ flexDirection: 'column',
22
+ marginTop: theme.spacing(0.5),
23
+ width: '100%'
24
+ },
25
+ tooltipContainer: {
26
+ padding: theme.spacing(1)
27
+ }
28
+ }));
@@ -0,0 +1,5 @@
1
+ import { atom } from 'jotai';
2
+
3
+ import { TooltipData } from './models';
4
+
5
+ export const tooltipDataAtom = atom<TooltipData | null>(null);
@@ -0,0 +1,21 @@
1
+ import { Line } from '../common/timeSeries/models';
2
+
3
+ export type TooltipData =
4
+ | {
5
+ data: Array<{
6
+ metric: Line;
7
+ value: number | null;
8
+ }>;
9
+ highlightedMetric: number;
10
+ index: number;
11
+ }
12
+ | {
13
+ thresholdLabel: string;
14
+ };
15
+
16
+ export interface BarStyle {
17
+ /** The opacity of the bar between 0 and 1. */
18
+ opacity: number;
19
+ /** The radius of a bar between 0 and 0.5. Does not work for stacked bars */
20
+ radius: number;
21
+ }
@@ -0,0 +1,5 @@
1
+ import { makeStyles } from 'tss-react/mui';
2
+
3
+ export const useBarChartStyles = makeStyles()({
4
+ tooltipChildren: { height: '100%', width: '100%' }
5
+ });
@@ -0,0 +1,114 @@
1
+ import { useCallback, useMemo } from 'react';
2
+
3
+ import { BarStack, BarStackHorizontal } from '@visx/shape';
4
+ import { equals, keys, omit } from 'ramda';
5
+ import { scaleOrdinal } from '@visx/scale';
6
+ import { useSetAtom } from 'jotai';
7
+
8
+ import { useDeepMemo } from '../../utils';
9
+ import { Line, TimeValue } from '../common/timeSeries/models';
10
+
11
+ import { tooltipDataAtom } from './atoms';
12
+
13
+ interface HoverBarProps {
14
+ barIndex: number;
15
+ highlightedMetric: number;
16
+ }
17
+
18
+ export interface UseBarStackProps {
19
+ isHorizontal: boolean;
20
+ lines: Array<Line>;
21
+ timeSeries: Array<TimeValue>;
22
+ xScale;
23
+ yScale;
24
+ }
25
+
26
+ interface UseBarStackState {
27
+ BarStackComponent: typeof BarStack | typeof BarStackHorizontal;
28
+ colorScale;
29
+ commonBarStackProps;
30
+ exitBar: () => void;
31
+ hoverBar: (props: HoverBarProps) => () => void;
32
+ lineKeys: Array<number>;
33
+ }
34
+
35
+ export const useBarStack = ({
36
+ timeSeries,
37
+ isHorizontal,
38
+ yScale,
39
+ lines,
40
+ xScale
41
+ }: UseBarStackProps): UseBarStackState => {
42
+ const setTooltipData = useSetAtom(tooltipDataAtom);
43
+
44
+ const BarStackComponent = useMemo(
45
+ () => (isHorizontal ? BarStack : BarStackHorizontal),
46
+ [isHorizontal]
47
+ );
48
+
49
+ const lineKeys = useDeepMemo({
50
+ deps: [timeSeries],
51
+ variable: keys(omit(['timeTick'], timeSeries[0]))
52
+ });
53
+ const colors = useDeepMemo({
54
+ deps: [lineKeys, lines],
55
+ variable: lineKeys.map((key) => {
56
+ const metric = lines.find(({ metric_id }) =>
57
+ equals(metric_id, Number(key))
58
+ );
59
+
60
+ return metric?.lineColor || '';
61
+ })
62
+ });
63
+
64
+ const colorScale = useMemo(
65
+ () =>
66
+ scaleOrdinal<number, string>({
67
+ domain: lineKeys,
68
+ range: colors
69
+ }),
70
+ [...lineKeys, ...colors]
71
+ );
72
+
73
+ const commonBarStackProps = isHorizontal
74
+ ? {
75
+ x: (d) => d.timeTick,
76
+ xScale,
77
+ yScale
78
+ }
79
+ : {
80
+ xScale: yScale,
81
+ y: (d) => d.timeTick,
82
+ yScale: xScale
83
+ };
84
+
85
+ const hoverBar = useCallback(
86
+ ({ highlightedMetric, barIndex }: HoverBarProps) =>
87
+ (): void => {
88
+ setTooltipData({
89
+ data: lines.map((metric) => {
90
+ return {
91
+ metric,
92
+ value: timeSeries[barIndex][metric.metric_id]
93
+ };
94
+ }),
95
+ highlightedMetric,
96
+ index: barIndex
97
+ });
98
+ },
99
+ []
100
+ );
101
+
102
+ const exitBar = useCallback((): void => {
103
+ setTooltipData(null);
104
+ }, []);
105
+
106
+ return {
107
+ BarStackComponent,
108
+ colorScale,
109
+ commonBarStackProps,
110
+ exitBar,
111
+ hoverBar,
112
+ lineKeys
113
+ };
114
+ };
@@ -0,0 +1,199 @@
1
+ import { useMemo } from 'react';
2
+
3
+ import { equals, gt } from 'ramda';
4
+ import { useSetAtom } from 'jotai';
5
+
6
+ import { Line } from '../common/timeSeries/models';
7
+ import { useDeepMemo } from '../..';
8
+
9
+ import { tooltipDataAtom } from './atoms';
10
+
11
+ const getInvertedBarLength = ({
12
+ useRightScale,
13
+ rightScale,
14
+ leftScale,
15
+ value
16
+ }): number | null => {
17
+ const scale = useRightScale ? rightScale : leftScale;
18
+
19
+ return scale(value);
20
+ };
21
+
22
+ const getBarLength = ({
23
+ size,
24
+ value,
25
+ invertedBarLength,
26
+ lengthToMatchZero,
27
+ isCenteredZero,
28
+ isHorizontal
29
+ }): number => {
30
+ if (!value) {
31
+ return 0;
32
+ }
33
+
34
+ if (!isHorizontal && gt(0, value) && isCenteredZero) {
35
+ return size - lengthToMatchZero - invertedBarLength;
36
+ }
37
+
38
+ if (!isHorizontal && gt(0, value) && gt(invertedBarLength, 0)) {
39
+ return invertedBarLength;
40
+ }
41
+
42
+ if (!isHorizontal && gt(0, value)) {
43
+ return invertedBarLength + (size - lengthToMatchZero);
44
+ }
45
+
46
+ if (!isHorizontal) {
47
+ return invertedBarLength - (size - lengthToMatchZero);
48
+ }
49
+
50
+ if (value < 0) {
51
+ return Math.abs(invertedBarLength) - (size - lengthToMatchZero);
52
+ }
53
+
54
+ if (isCenteredZero) {
55
+ const barLength = size - invertedBarLength;
56
+
57
+ return size - invertedBarLength - barLength / 2;
58
+ }
59
+
60
+ return size - invertedBarLength - lengthToMatchZero;
61
+ };
62
+
63
+ export interface UseSingleBarProps {
64
+ bar: {
65
+ color: string;
66
+ height: number;
67
+ index: number;
68
+ key: number;
69
+ value: number | null;
70
+ width: number;
71
+ x: number;
72
+ y: number;
73
+ };
74
+ isCenteredZero?: boolean;
75
+ isHorizontal: boolean;
76
+ isTooltipHidden: boolean;
77
+ leftScale;
78
+ lines: Array<Line>;
79
+ rightScale;
80
+ secondUnit?: string;
81
+ size: number;
82
+ }
83
+
84
+ interface UseSingleBarState {
85
+ barLength: number;
86
+ barPadding: number;
87
+ listeners: {
88
+ onMouseEnter: () => void;
89
+ onMouseLeave: () => void;
90
+ };
91
+ }
92
+
93
+ export const useSingleBar = ({
94
+ lines,
95
+ secondUnit,
96
+ bar,
97
+ leftScale,
98
+ rightScale,
99
+ size,
100
+ isCenteredZero,
101
+ isHorizontal,
102
+ isTooltipHidden
103
+ }: UseSingleBarProps): UseSingleBarState => {
104
+ const setTooltipData = useSetAtom(tooltipDataAtom);
105
+
106
+ const metric = useDeepMemo({
107
+ deps: [lines, bar.key],
108
+ variable: lines.find(({ metric_id }) => equals(metric_id, Number(bar.key)))
109
+ }) as Line;
110
+
111
+ const useRightScale = useMemo(
112
+ () => equals(secondUnit, metric?.unit),
113
+ [secondUnit, metric?.unit]
114
+ );
115
+
116
+ const left0Height = useMemo(() => leftScale(0), [leftScale(0)]);
117
+ const right0Height = useMemo(() => rightScale(0), [rightScale(0)]);
118
+
119
+ const invertedBarLength = useMemo(
120
+ () =>
121
+ getInvertedBarLength({
122
+ leftScale,
123
+ rightScale,
124
+ useRightScale,
125
+ value: bar.value
126
+ }),
127
+ [bar.value, useRightScale, leftScale, rightScale]
128
+ );
129
+
130
+ const lengthToMatchZero = useMemo(
131
+ () => size - (useRightScale ? right0Height : left0Height),
132
+ [useRightScale, rightScale, leftScale]
133
+ );
134
+
135
+ const barLength = useMemo(
136
+ () =>
137
+ getBarLength({
138
+ invertedBarLength,
139
+ isCenteredZero,
140
+ isHorizontal,
141
+ lengthToMatchZero,
142
+ size,
143
+ value: bar.value
144
+ }),
145
+ [
146
+ size,
147
+ invertedBarLength,
148
+ isCenteredZero,
149
+ isHorizontal,
150
+ lengthToMatchZero,
151
+ bar.value
152
+ ]
153
+ );
154
+
155
+ const barPadding = useMemo(
156
+ () =>
157
+ isHorizontal
158
+ ? size -
159
+ barLength -
160
+ lengthToMatchZero +
161
+ ((bar.value ?? 0) < 0 ? barLength : 0)
162
+ : size - lengthToMatchZero - ((bar.value ?? 0) < 0 ? barLength : 0),
163
+ [isHorizontal, barLength, bar.value, barLength]
164
+ );
165
+
166
+ const hoverBar = (): void => {
167
+ setTooltipData({
168
+ data: [
169
+ {
170
+ metric,
171
+ value: bar.value
172
+ }
173
+ ],
174
+ highlightedMetric: metric?.metric_id,
175
+ index: bar.index
176
+ });
177
+ };
178
+
179
+ const exitBar = (): void => {
180
+ setTooltipData(null);
181
+ };
182
+
183
+ const listeners = useMemo(
184
+ () =>
185
+ isTooltipHidden
186
+ ? {}
187
+ : {
188
+ onMouseEnter: hoverBar,
189
+ onMouseLeave: exitBar
190
+ },
191
+ [isTooltipHidden]
192
+ );
193
+
194
+ return {
195
+ barLength,
196
+ barPadding,
197
+ listeners
198
+ };
199
+ };
@@ -1,4 +1,4 @@
1
- import dataLastWeek from '../LineChart/mockedData/lastWeek.json';
1
+ import dataLastWeek from '../mockedData/lastWeek.json';
2
2
  import {
3
3
  criticalThresholds,
4
4
  rangedThresholds,
@@ -1,6 +1,6 @@
1
1
  import { Meta, StoryObj } from '@storybook/react';
2
2
 
3
- import dataLastWeek from '../LineChart/mockedData/lastWeek.json';
3
+ import dataLastWeek from '../mockedData/lastWeek.json';
4
4
 
5
5
  import { Gauge } from './Gauge';
6
6
 
@@ -1,9 +1,9 @@
1
1
  import { LineChartData } from '../common/models';
2
+ import dataLastDay from '../mockedData/lastDay.json';
3
+ import dataLastDayWithNullValues from '../mockedData/lastDayWithNullValues.json';
4
+ import dataLastDayWithIncompleteValues from '../mockedData/lastDayWithIncompleteValues.json';
5
+ import dataCurvesWithSameColor from '../mockedData/curvesWithSameColor.json';
2
6
 
3
- import dataLastDay from './mockedData/lastDay.json';
4
- import dataLastDayWithNullValues from './mockedData/lastDayWithNullValues.json';
5
- import dataLastDayWithIncompleteValues from './mockedData/lastDayWithIncompleteValues.json';
6
- import dataCurvesWithSameColor from './mockedData/curvesWithSameColor.json';
7
7
  import { args as argumentsData } from './helpers/doc';
8
8
  import { LineChartProps } from './models';
9
9
 
@@ -1,10 +1,5 @@
1
1
  import { makeStyles } from 'tss-react/mui';
2
2
 
3
3
  export const useLineChartStyles = makeStyles()({
4
- header: {
5
- display: 'grid',
6
- gridTemplateColumns: '0.4fr 1fr 0.4fr',
7
- width: '100%'
8
- },
9
4
  tooltipChildren: { height: '100%', width: '100%' }
10
5
  });