@centreon/ui 23.10.1-MON-20799-fe-implement-new-visualization.12 → 23.10.1-MON-21149-implement-time-periods-thresholds-refresh-interval.16
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 +1 -1
- package/src/Graph/LineChart/BasicComponents/Thresholds.tsx +119 -0
- package/src/Graph/LineChart/BasicComponents/useFilterLines.ts +9 -7
- package/src/Graph/LineChart/InteractiveComponents/AnchorPoint/index.tsx +2 -4
- package/src/Graph/LineChart/InteractiveComponents/interactionWithGraphAtoms.ts +1 -0
- package/src/Graph/LineChart/Legend/index.tsx +9 -4
- package/src/Graph/LineChart/Legend/useInteractiveValues.ts +13 -4
- package/src/Graph/LineChart/Legend/useLegend.ts +10 -13
- package/src/Graph/LineChart/LineChart.tsx +70 -6
- package/src/Graph/LineChart/graphAtoms.ts +0 -3
- package/src/Graph/LineChart/index.stories.tsx +37 -6
- package/src/Graph/LineChart/index.tsx +18 -2
- package/src/Graph/common/timeSeries/index.ts +41 -8
- package/src/Graph/common/timeSeries/models.ts +3 -1
- package/src/Listing/ActionBar/index.tsx +7 -14
- package/src/Listing/Cell/DataCell.tsx +7 -7
- package/src/Listing/Cell/index.tsx +5 -5
- package/src/Listing/Header/Cell/ListingHeaderCell.styles.ts +3 -3
- package/src/Listing/Header/Cell/ListingHeaderCell.tsx +2 -2
- package/src/Listing/Header/ListingHeader.tsx +4 -4
- package/src/Listing/Listing.styles.ts +1 -1
- package/src/Listing/Row/Row.tsx +3 -3
- package/src/Listing/index.stories.tsx +5 -5
- package/src/Listing/index.tsx +12 -15
- package/src/Listing/tableAtoms.ts +11 -14
- package/src/Listing/useStyleTable.ts +8 -8
- package/src/RichTextEditor/ContentEditable.tsx +1 -7
- package/src/RichTextEditor/RichTextEditor.tsx +3 -6
- package/src/SortableItems/SortableItem.tsx +1 -2
- package/src/api/useGraphQuery/index.ts +52 -21
- package/src/components/Modal/Modal.styles.ts +1 -2
- package/src/utils/index.ts +1 -0
- package/src/utils/useRefreshInterval.ts +21 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@centreon/ui",
|
|
3
|
-
"version": "23.10.1-MON-
|
|
3
|
+
"version": "23.10.1-MON-21149-implement-time-periods-thresholds-refresh-interval.16",
|
|
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,119 @@
|
|
|
1
|
+
import { always, cond, equals, isNil } from 'ramda';
|
|
2
|
+
|
|
3
|
+
import { Theme, useTheme } from '@mui/material';
|
|
4
|
+
|
|
5
|
+
import { getUnits, getYScale } from '../../common/timeSeries';
|
|
6
|
+
import { Line } from '../../common/timeSeries/models';
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
displayedLines: Array<Line>;
|
|
10
|
+
hideTooltip: () => void;
|
|
11
|
+
leftScale: (value: number) => number;
|
|
12
|
+
rightScale: (value: number) => number;
|
|
13
|
+
showTooltip: (props) => void;
|
|
14
|
+
thresholdLabels?: Array<string>;
|
|
15
|
+
thresholdUnit?: string;
|
|
16
|
+
thresholds?: Array<number>;
|
|
17
|
+
width: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface GetColorProps {
|
|
21
|
+
index: number;
|
|
22
|
+
theme: Theme;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const getColor = ({ index, theme }: GetColorProps): string => {
|
|
26
|
+
return cond([
|
|
27
|
+
[equals(0), always(theme.palette.warning.main)],
|
|
28
|
+
[equals(1), always(theme.palette.error.main)]
|
|
29
|
+
])(index);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const Thresholds = ({
|
|
33
|
+
thresholds,
|
|
34
|
+
leftScale,
|
|
35
|
+
rightScale,
|
|
36
|
+
width,
|
|
37
|
+
displayedLines,
|
|
38
|
+
thresholdUnit,
|
|
39
|
+
showTooltip,
|
|
40
|
+
hideTooltip,
|
|
41
|
+
thresholdLabels
|
|
42
|
+
}: Props): JSX.Element | null => {
|
|
43
|
+
const theme = useTheme();
|
|
44
|
+
|
|
45
|
+
if (!thresholds) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const [firstUnit, secondUnit, thirdUnit] = getUnits(
|
|
50
|
+
displayedLines as Array<Line>
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const shouldUseRightScale = equals(thresholdUnit, secondUnit);
|
|
54
|
+
|
|
55
|
+
const yScale = shouldUseRightScale
|
|
56
|
+
? rightScale
|
|
57
|
+
: getYScale({
|
|
58
|
+
hasMoreThanTwoUnits: !isNil(thirdUnit),
|
|
59
|
+
invert: null,
|
|
60
|
+
leftScale,
|
|
61
|
+
rightScale,
|
|
62
|
+
secondUnit,
|
|
63
|
+
unit: firstUnit
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const thresholdScaledValues = thresholds
|
|
67
|
+
.sort()
|
|
68
|
+
.map((threshold) => yScale(threshold));
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<>
|
|
72
|
+
{thresholdScaledValues.map((threshold, index) => {
|
|
73
|
+
return (
|
|
74
|
+
<line
|
|
75
|
+
data-testid={`threshold-${threshold}`}
|
|
76
|
+
key={`threshold-${thresholdLabels?.[index]}-${threshold}`}
|
|
77
|
+
stroke={getColor({
|
|
78
|
+
index,
|
|
79
|
+
theme
|
|
80
|
+
})}
|
|
81
|
+
strokeDasharray="5,5"
|
|
82
|
+
strokeWidth={2}
|
|
83
|
+
x1={0}
|
|
84
|
+
x2={width}
|
|
85
|
+
y1={threshold}
|
|
86
|
+
y2={threshold}
|
|
87
|
+
/>
|
|
88
|
+
);
|
|
89
|
+
})}
|
|
90
|
+
{thresholdScaledValues.map((threshold, index) => {
|
|
91
|
+
return (
|
|
92
|
+
<line
|
|
93
|
+
data-testid={`threshold-${threshold}-tooltip`}
|
|
94
|
+
key={`threshold-${thresholdLabels?.[index]}-${threshold}-tooltip`}
|
|
95
|
+
stroke="transparent"
|
|
96
|
+
strokeWidth={4}
|
|
97
|
+
x1={0}
|
|
98
|
+
x2={width}
|
|
99
|
+
y1={threshold}
|
|
100
|
+
y2={threshold}
|
|
101
|
+
onMouseEnter={(): void => {
|
|
102
|
+
if (!thresholdLabels?.[index]) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
showTooltip({
|
|
106
|
+
tooltipData: thresholdLabels?.[index],
|
|
107
|
+
tooltipLeft: 0,
|
|
108
|
+
tooltipTop: threshold
|
|
109
|
+
});
|
|
110
|
+
}}
|
|
111
|
+
onMouseLeave={hideTooltip}
|
|
112
|
+
/>
|
|
113
|
+
);
|
|
114
|
+
})}
|
|
115
|
+
</>
|
|
116
|
+
);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export default Thresholds;
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { useEffect } from 'react';
|
|
1
|
+
import { Dispatch, SetStateAction, useEffect } from 'react';
|
|
2
2
|
|
|
3
|
-
import { useAtom } from 'jotai';
|
|
4
3
|
import { compose, equals, prop, propEq, reject, sortBy, toLower } from 'ramda';
|
|
5
4
|
|
|
6
|
-
import { linesGraphAtom } from '../graphAtoms';
|
|
7
5
|
import { Line } from '../../common/timeSeries/models';
|
|
8
6
|
|
|
9
7
|
import {
|
|
@@ -15,6 +13,8 @@ import {
|
|
|
15
13
|
interface UseFilterLines {
|
|
16
14
|
displayThreshold?: boolean;
|
|
17
15
|
lines: Array<Line>;
|
|
16
|
+
linesGraph: Array<Line> | null;
|
|
17
|
+
setLinesGraph: Dispatch<SetStateAction<Array<Line> | null>>;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
interface Result {
|
|
@@ -24,10 +24,10 @@ interface Result {
|
|
|
24
24
|
|
|
25
25
|
const useFilterLines = ({
|
|
26
26
|
displayThreshold = false,
|
|
27
|
-
lines
|
|
27
|
+
lines,
|
|
28
|
+
linesGraph,
|
|
29
|
+
setLinesGraph
|
|
28
30
|
}: UseFilterLines): Result => {
|
|
29
|
-
const [linesGraph, setLinesGraph] = useAtom(linesGraphAtom);
|
|
30
|
-
|
|
31
31
|
const displayedLines = reject(propEq('display', false), linesGraph ?? lines);
|
|
32
32
|
const filterLines = (): Array<Line> => {
|
|
33
33
|
const lineOriginMetric = findLineOfOriginMetricThreshold(lines);
|
|
@@ -45,7 +45,9 @@ const useFilterLines = ({
|
|
|
45
45
|
|
|
46
46
|
useEffect(() => {
|
|
47
47
|
const filteredLines = filterLines();
|
|
48
|
-
if (!lines || !displayThreshold
|
|
48
|
+
if (!lines || !displayThreshold) {
|
|
49
|
+
setLinesGraph(lines);
|
|
50
|
+
|
|
49
51
|
return;
|
|
50
52
|
}
|
|
51
53
|
|
|
@@ -8,14 +8,12 @@ interface Props {
|
|
|
8
8
|
|
|
9
9
|
const Point = ({ lineColor, x, y }: Props): JSX.Element => (
|
|
10
10
|
<circle
|
|
11
|
+
cx={x}
|
|
12
|
+
cy={y}
|
|
11
13
|
fill={alpha(lineColor, 0.5)}
|
|
12
14
|
r={4}
|
|
13
15
|
stroke={lineColor}
|
|
14
16
|
strokeWidth={2}
|
|
15
|
-
style={{
|
|
16
|
-
transform: `translate(${x}px, ${y}px)`,
|
|
17
|
-
transition: 'transform 0.08s ease-out'
|
|
18
|
-
}}
|
|
19
17
|
/>
|
|
20
18
|
);
|
|
21
19
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ReactNode } from 'react';
|
|
1
|
+
import { Dispatch, ReactNode, SetStateAction } from 'react';
|
|
2
2
|
|
|
3
3
|
import { slice } from 'ramda';
|
|
4
4
|
import { useAtomValue } from 'jotai';
|
|
@@ -28,8 +28,10 @@ interface Props {
|
|
|
28
28
|
limitLegendRows?: boolean;
|
|
29
29
|
lines: Array<Line>;
|
|
30
30
|
renderExtraComponent?: ReactNode;
|
|
31
|
+
setLinesGraph: Dispatch<SetStateAction<Array<Line> | null>>;
|
|
31
32
|
timeSeries: Array<TimeValue>;
|
|
32
33
|
toggable?: boolean;
|
|
34
|
+
xScale;
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
const MainLegend = ({
|
|
@@ -39,18 +41,21 @@ const MainLegend = ({
|
|
|
39
41
|
toggable = true,
|
|
40
42
|
limitLegendRows = true,
|
|
41
43
|
renderExtraComponent,
|
|
42
|
-
displayAnchor = true
|
|
44
|
+
displayAnchor = true,
|
|
45
|
+
setLinesGraph,
|
|
46
|
+
xScale
|
|
43
47
|
}: Props): JSX.Element => {
|
|
44
48
|
const { classes, cx } = useStyles({ limitLegendRows });
|
|
45
49
|
const theme = useTheme();
|
|
46
50
|
|
|
47
51
|
const { selectMetricLine, clearHighlight, highlightLine, toggleMetricLine } =
|
|
48
|
-
useLegend({ lines });
|
|
52
|
+
useLegend({ lines, setLinesGraph });
|
|
49
53
|
|
|
50
54
|
const { getFormattedValue } = useInteractiveValues({
|
|
51
55
|
base,
|
|
52
56
|
lines,
|
|
53
|
-
timeSeries
|
|
57
|
+
timeSeries,
|
|
58
|
+
xScale
|
|
54
59
|
});
|
|
55
60
|
|
|
56
61
|
const displayedLines = limitLegendRows
|
|
@@ -3,11 +3,12 @@ import { useMemo } from 'react';
|
|
|
3
3
|
import { useAtomValue } from 'jotai';
|
|
4
4
|
import { equals, find, isNil } from 'ramda';
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import { mousePositionAtom } from '../InteractiveComponents/interactionWithGraphAtoms';
|
|
7
7
|
import {
|
|
8
8
|
formatMetricValue,
|
|
9
9
|
getLineForMetric,
|
|
10
|
-
getMetrics
|
|
10
|
+
getMetrics,
|
|
11
|
+
getTimeValue
|
|
11
12
|
} from '../../common/timeSeries';
|
|
12
13
|
import { Line, TimeValue } from '../../common/timeSeries/models';
|
|
13
14
|
|
|
@@ -21,14 +22,22 @@ interface Props {
|
|
|
21
22
|
base: number;
|
|
22
23
|
lines: Array<Line>;
|
|
23
24
|
timeSeries: Array<TimeValue>;
|
|
25
|
+
xScale;
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
const useInteractiveValues = ({
|
|
27
29
|
timeSeries,
|
|
28
30
|
lines,
|
|
29
|
-
base
|
|
31
|
+
base,
|
|
32
|
+
xScale
|
|
30
33
|
}: Props): InteractiveValues => {
|
|
31
|
-
const
|
|
34
|
+
const mousePosition = useAtomValue(mousePositionAtom);
|
|
35
|
+
|
|
36
|
+
const timeValue = getTimeValue({
|
|
37
|
+
timeSeries,
|
|
38
|
+
x: mousePosition?.[0],
|
|
39
|
+
xScale
|
|
40
|
+
});
|
|
32
41
|
|
|
33
42
|
const graphTimeValue = timeSeries?.find((item) =>
|
|
34
43
|
equals(item.timeTick, timeValue?.timeTick)
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { useEffect } from 'react';
|
|
1
|
+
import { Dispatch, SetStateAction, useEffect } from 'react';
|
|
2
2
|
|
|
3
|
-
import { useSetAtom } from 'jotai';
|
|
4
3
|
import {
|
|
5
4
|
equals,
|
|
6
5
|
find,
|
|
@@ -17,7 +16,6 @@ import {
|
|
|
17
16
|
toLower
|
|
18
17
|
} from 'ramda';
|
|
19
18
|
|
|
20
|
-
import { linesGraphAtom } from '../graphAtoms';
|
|
21
19
|
import { Line } from '../../common/timeSeries/models';
|
|
22
20
|
|
|
23
21
|
interface LegendActions {
|
|
@@ -29,11 +27,10 @@ interface LegendActions {
|
|
|
29
27
|
|
|
30
28
|
interface Props {
|
|
31
29
|
lines: Array<Line>;
|
|
30
|
+
setLinesGraph: Dispatch<SetStateAction<Array<Line> | null>>;
|
|
32
31
|
}
|
|
33
32
|
|
|
34
|
-
const useLegend = ({ lines }: Props): LegendActions => {
|
|
35
|
-
const setLines = useSetAtom(linesGraphAtom);
|
|
36
|
-
|
|
33
|
+
const useLegend = ({ lines, setLinesGraph }: Props): LegendActions => {
|
|
37
34
|
const displayedLines = reject(propEq('display', false), lines);
|
|
38
35
|
const getLineByMetric = (metric: string): Line =>
|
|
39
36
|
find(propEq('metric', metric), lines) as Line;
|
|
@@ -41,7 +38,7 @@ const useLegend = ({ lines }: Props): LegendActions => {
|
|
|
41
38
|
const toggleMetricLine = (metric): void => {
|
|
42
39
|
const line = getLineByMetric(metric);
|
|
43
40
|
|
|
44
|
-
|
|
41
|
+
setLinesGraph([
|
|
45
42
|
...reject(propEq('metric', metric), lines),
|
|
46
43
|
{ ...line, display: !line.display }
|
|
47
44
|
]);
|
|
@@ -56,11 +53,11 @@ const useLegend = ({ lines }: Props): LegendActions => {
|
|
|
56
53
|
|
|
57
54
|
const sortedData = sortBy(compose(toLower, prop('name')), data);
|
|
58
55
|
|
|
59
|
-
|
|
56
|
+
setLinesGraph(sortedData);
|
|
60
57
|
};
|
|
61
58
|
|
|
62
59
|
const clearHighlight = (): void => {
|
|
63
|
-
|
|
60
|
+
setLinesGraph(map((line) => ({ ...line, highlight: undefined }), lines));
|
|
64
61
|
};
|
|
65
62
|
|
|
66
63
|
const selectMetricLine = (metric: string): void => {
|
|
@@ -71,7 +68,7 @@ const useLegend = ({ lines }: Props): LegendActions => {
|
|
|
71
68
|
equals(displayedLines.length, 1) && isLineDisplayed;
|
|
72
69
|
|
|
73
70
|
if (isOnlyLineDisplayed || isEmpty(displayedLines)) {
|
|
74
|
-
|
|
71
|
+
setLinesGraph(
|
|
75
72
|
map(
|
|
76
73
|
(line) => ({
|
|
77
74
|
...line,
|
|
@@ -84,7 +81,7 @@ const useLegend = ({ lines }: Props): LegendActions => {
|
|
|
84
81
|
return;
|
|
85
82
|
}
|
|
86
83
|
|
|
87
|
-
|
|
84
|
+
setLinesGraph(
|
|
88
85
|
map(
|
|
89
86
|
(line) => ({
|
|
90
87
|
...line,
|
|
@@ -105,8 +102,8 @@ const useLegend = ({ lines }: Props): LegendActions => {
|
|
|
105
102
|
display: find(propEq('name', line.name), lines)?.display ?? true
|
|
106
103
|
}));
|
|
107
104
|
|
|
108
|
-
|
|
109
|
-
}, []);
|
|
105
|
+
setLinesGraph(newLines);
|
|
106
|
+
}, [lines]);
|
|
110
107
|
|
|
111
108
|
return { clearHighlight, highlightLine, selectMetricLine, toggleMetricLine };
|
|
112
109
|
};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { MutableRefObject, useMemo, useRef } from 'react';
|
|
1
|
+
import { MutableRefObject, useMemo, useRef, useState } from 'react';
|
|
2
2
|
|
|
3
|
-
import { Group } from '@visx/visx';
|
|
3
|
+
import { Group, Tooltip } from '@visx/visx';
|
|
4
4
|
import { isNil } from 'ramda';
|
|
5
5
|
|
|
6
|
-
import { ClickAwayListener, Skeleton } from '@mui/material';
|
|
6
|
+
import { ClickAwayListener, Fade, Skeleton, useTheme } from '@mui/material';
|
|
7
7
|
|
|
8
8
|
import { getLeftScale, getRightScale, getXScale } from '../common/timeSeries';
|
|
9
|
+
import { Line } from '../common/timeSeries/models';
|
|
9
10
|
|
|
10
11
|
import Axes from './BasicComponents/Axes';
|
|
11
12
|
import Grids from './BasicComponents/Grids';
|
|
@@ -30,17 +31,27 @@ import {
|
|
|
30
31
|
} from './models';
|
|
31
32
|
import { useIntersection } from './useLineChartIntersection';
|
|
32
33
|
import { CurveType } from './BasicComponents/Lines/models';
|
|
34
|
+
import Thresholds from './BasicComponents/Thresholds';
|
|
33
35
|
|
|
34
36
|
interface Props extends LineChartProps {
|
|
35
37
|
curve: CurveType;
|
|
38
|
+
disabledThresholds?: boolean;
|
|
36
39
|
graphData: Data;
|
|
37
40
|
graphInterval: GraphInterval;
|
|
38
41
|
graphRef: MutableRefObject<HTMLDivElement | null>;
|
|
39
42
|
legend?: LegendModel;
|
|
40
43
|
marginBottom: number;
|
|
41
44
|
shapeLines?: GlobalAreaLines;
|
|
45
|
+
thresholdLabels?: Array<string>;
|
|
46
|
+
thresholdUnit?: string;
|
|
47
|
+
thresholds?: Array<number>;
|
|
42
48
|
}
|
|
43
49
|
|
|
50
|
+
const baseStyles = {
|
|
51
|
+
...Tooltip.defaultStyles,
|
|
52
|
+
textAlign: 'center'
|
|
53
|
+
};
|
|
54
|
+
|
|
44
55
|
const LineChart = ({
|
|
45
56
|
graphData,
|
|
46
57
|
height = 500,
|
|
@@ -58,15 +69,32 @@ const LineChart = ({
|
|
|
58
69
|
graphRef,
|
|
59
70
|
header,
|
|
60
71
|
curve,
|
|
61
|
-
marginBottom
|
|
72
|
+
marginBottom,
|
|
73
|
+
thresholds,
|
|
74
|
+
thresholdUnit,
|
|
75
|
+
thresholdLabels,
|
|
76
|
+
disabledThresholds
|
|
62
77
|
}: Props): JSX.Element => {
|
|
78
|
+
const { classes } = useStyles();
|
|
79
|
+
|
|
80
|
+
const theme = useTheme();
|
|
81
|
+
|
|
82
|
+
const [linesGraph, setLinesGraph] = useState<Array<Line> | null>(null);
|
|
63
83
|
const graphSvgRef = useRef<SVGSVGElement | null>(null);
|
|
64
84
|
|
|
65
|
-
const { classes } = useStyles();
|
|
66
85
|
const { isInViewport } = useIntersection({ element: graphRef?.current });
|
|
67
86
|
|
|
68
87
|
const legendRef = useRef<HTMLDivElement | null>(null);
|
|
69
88
|
|
|
89
|
+
const {
|
|
90
|
+
tooltipOpen: thresholdTooltipOpen,
|
|
91
|
+
tooltipLeft: thresholdTooltipLeft,
|
|
92
|
+
tooltipTop: thresholdTooltipTop,
|
|
93
|
+
tooltipData: thresholdTooltipData,
|
|
94
|
+
hideTooltip: hideThresholdTooltip,
|
|
95
|
+
showTooltip: showThresholdTooltip
|
|
96
|
+
} = Tooltip.useTooltip();
|
|
97
|
+
|
|
70
98
|
const graphWidth = width > 0 ? width - margin.left - margin.right : 0;
|
|
71
99
|
const graphHeight =
|
|
72
100
|
(height || 0) > 0
|
|
@@ -81,7 +109,9 @@ const LineChart = ({
|
|
|
81
109
|
|
|
82
110
|
const { displayedLines, newLines } = useFilterLines({
|
|
83
111
|
displayThreshold: canDisplayThreshold(shapeLines?.areaThresholdLines),
|
|
84
|
-
lines
|
|
112
|
+
lines,
|
|
113
|
+
linesGraph,
|
|
114
|
+
setLinesGraph
|
|
85
115
|
});
|
|
86
116
|
|
|
87
117
|
const xScale = useMemo(
|
|
@@ -98,6 +128,8 @@ const LineChart = ({
|
|
|
98
128
|
getLeftScale({
|
|
99
129
|
dataLines: displayedLines,
|
|
100
130
|
dataTimeSeries: timeSeries,
|
|
131
|
+
thresholdUnit,
|
|
132
|
+
thresholds: (!disabledThresholds && thresholds) || [],
|
|
101
133
|
valueGraphHeight: graphHeight - 35
|
|
102
134
|
}),
|
|
103
135
|
[displayedLines, timeSeries, graphHeight]
|
|
@@ -108,6 +140,8 @@ const LineChart = ({
|
|
|
108
140
|
getRightScale({
|
|
109
141
|
dataLines: displayedLines,
|
|
110
142
|
dataTimeSeries: timeSeries,
|
|
143
|
+
thresholdUnit,
|
|
144
|
+
thresholds: (!disabledThresholds && thresholds) || [],
|
|
111
145
|
valueGraphHeight: graphHeight - 35
|
|
112
146
|
}),
|
|
113
147
|
[timeSeries, displayedLines, graphHeight]
|
|
@@ -201,6 +235,20 @@ const LineChart = ({
|
|
|
201
235
|
}}
|
|
202
236
|
zoomData={{ ...zoomPreview }}
|
|
203
237
|
/>
|
|
238
|
+
|
|
239
|
+
{!disabledThresholds && (
|
|
240
|
+
<Thresholds
|
|
241
|
+
displayedLines={displayedLines}
|
|
242
|
+
hideTooltip={hideThresholdTooltip}
|
|
243
|
+
leftScale={leftScale}
|
|
244
|
+
rightScale={rightScale}
|
|
245
|
+
showTooltip={showThresholdTooltip}
|
|
246
|
+
thresholdLabels={thresholdLabels}
|
|
247
|
+
thresholdUnit={thresholdUnit}
|
|
248
|
+
thresholds={thresholds}
|
|
249
|
+
width={graphWidth}
|
|
250
|
+
/>
|
|
251
|
+
)}
|
|
204
252
|
</Group.Group>
|
|
205
253
|
</svg>
|
|
206
254
|
{displayTooltip && (
|
|
@@ -218,6 +266,20 @@ const LineChart = ({
|
|
|
218
266
|
xScale={xScale}
|
|
219
267
|
/>
|
|
220
268
|
)}
|
|
269
|
+
<Fade in={thresholdTooltipOpen}>
|
|
270
|
+
<Tooltip.Tooltip
|
|
271
|
+
left={thresholdTooltipLeft}
|
|
272
|
+
style={{
|
|
273
|
+
...baseStyles,
|
|
274
|
+
backgroundColor: theme.palette.background.paper,
|
|
275
|
+
color: theme.palette.text.primary,
|
|
276
|
+
transform: `translate(${graphWidth / 2}px, -10px)`
|
|
277
|
+
}}
|
|
278
|
+
top={thresholdTooltipTop}
|
|
279
|
+
>
|
|
280
|
+
{thresholdTooltipData}
|
|
281
|
+
</Tooltip.Tooltip>
|
|
282
|
+
</Fade>
|
|
221
283
|
</div>
|
|
222
284
|
</ClickAwayListener>
|
|
223
285
|
{displayLegend && (
|
|
@@ -227,7 +289,9 @@ const LineChart = ({
|
|
|
227
289
|
displayAnchor={displayAnchor?.displayGuidingLines ?? true}
|
|
228
290
|
lines={newLines}
|
|
229
291
|
renderExtraComponent={legend?.renderExtraComponent}
|
|
292
|
+
setLinesGraph={setLinesGraph}
|
|
230
293
|
timeSeries={timeSeries}
|
|
294
|
+
xScale={xScale}
|
|
231
295
|
/>
|
|
232
296
|
</div>
|
|
233
297
|
)}
|
|
@@ -29,10 +29,11 @@ import exclusionPeriodFirstPeriod from './mockedData/exclusionPeriodFirstPeriod.
|
|
|
29
29
|
import exclusionPeriodSecondPeriod from './mockedData/exclusionPeriodSecondPeriod.json';
|
|
30
30
|
import exclusionPeriodThirdPeriod from './mockedData/exclusionPeriodThirdPeriod.json';
|
|
31
31
|
import dataLastDayForword from './mockedData/lastDayForward.json';
|
|
32
|
-
import
|
|
32
|
+
import dataLastDayThreshold from './mockedData/lastDayThreshold.json';
|
|
33
33
|
import dataLastMonth from './mockedData/lastMonth.json';
|
|
34
34
|
import dataLastWeek from './mockedData/lastWeek.json';
|
|
35
35
|
import dataZoomPreview from './mockedData/zoomPreview.json';
|
|
36
|
+
import dataLastDay from './mockedData/lastDay.json';
|
|
36
37
|
import { Interval, ThresholdType, TooltipData } from './models';
|
|
37
38
|
|
|
38
39
|
import WrapperLineChart from './index';
|
|
@@ -75,7 +76,7 @@ const Threshold = (args): JSX.Element => {
|
|
|
75
76
|
|
|
76
77
|
<WrapperLineChart
|
|
77
78
|
{...args}
|
|
78
|
-
data={
|
|
79
|
+
data={dataLastDayThreshold}
|
|
79
80
|
shapeLines={{
|
|
80
81
|
areaThresholdLines: [
|
|
81
82
|
{ type: ThresholdType.basic },
|
|
@@ -123,7 +124,7 @@ const LineChartAndCLS = (args): JSX.Element => {
|
|
|
123
124
|
useEffect(() => {
|
|
124
125
|
setTimeout(() => {
|
|
125
126
|
setLoading(false);
|
|
126
|
-
setData(
|
|
127
|
+
setData(dataLastDayThreshold as unknown as LineChartData);
|
|
127
128
|
}, 100000);
|
|
128
129
|
}, []);
|
|
129
130
|
|
|
@@ -187,7 +188,7 @@ const LineChartAndTimePeriod = (args): JSX.Element => {
|
|
|
187
188
|
}
|
|
188
189
|
|
|
189
190
|
if (start.includes(`${defaultStart.split('T')[0]}`)) {
|
|
190
|
-
setCurrentData(
|
|
191
|
+
setCurrentData(dataLastDayThreshold as unknown as LineChartData);
|
|
191
192
|
|
|
192
193
|
return;
|
|
193
194
|
}
|
|
@@ -281,7 +282,7 @@ const LineChartAndExclusionPeriod = (args): JSX.Element => {
|
|
|
281
282
|
</ButtonGroup>
|
|
282
283
|
<WrapperLineChart
|
|
283
284
|
{...args}
|
|
284
|
-
data={
|
|
285
|
+
data={dataLastDayThreshold as unknown as LineChartData}
|
|
285
286
|
shapeLines={{
|
|
286
287
|
areaThresholdLines: [
|
|
287
288
|
{
|
|
@@ -303,7 +304,7 @@ const Template: Story = {
|
|
|
303
304
|
render: (args) => (
|
|
304
305
|
<WrapperLineChart
|
|
305
306
|
{...args}
|
|
306
|
-
data={
|
|
307
|
+
data={dataLastDayThreshold as unknown as LineChartData}
|
|
307
308
|
shapeLines={{
|
|
308
309
|
areaThresholdLines: [
|
|
309
310
|
{
|
|
@@ -381,3 +382,33 @@ export const withCLS: Story = {
|
|
|
381
382
|
start: defaultStart
|
|
382
383
|
}
|
|
383
384
|
};
|
|
385
|
+
|
|
386
|
+
export const withThresholds: Story = {
|
|
387
|
+
argTypes,
|
|
388
|
+
args: {
|
|
389
|
+
...argumentsData,
|
|
390
|
+
thresholdLabels: ['threshold 1', 'threshold 2'],
|
|
391
|
+
thresholds: [300, 350]
|
|
392
|
+
},
|
|
393
|
+
render: (args) => (
|
|
394
|
+
<WrapperLineChart
|
|
395
|
+
{...args}
|
|
396
|
+
data={dataLastDay as unknown as LineChartData}
|
|
397
|
+
/>
|
|
398
|
+
)
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
export const withThresholdsAndUnit: Story = {
|
|
402
|
+
argTypes,
|
|
403
|
+
args: {
|
|
404
|
+
...argumentsData,
|
|
405
|
+
thresholdUnit: '%',
|
|
406
|
+
thresholds: [65, 79]
|
|
407
|
+
},
|
|
408
|
+
render: (args) => (
|
|
409
|
+
<WrapperLineChart
|
|
410
|
+
{...args}
|
|
411
|
+
data={dataLastDay as unknown as LineChartData}
|
|
412
|
+
/>
|
|
413
|
+
)
|
|
414
|
+
};
|
|
@@ -25,12 +25,16 @@ dayjs.extend(timezonePlugin);
|
|
|
25
25
|
interface Props extends Partial<LineChartProps> {
|
|
26
26
|
curve?: CurveType;
|
|
27
27
|
data?: LineChartData;
|
|
28
|
+
disabledThresholds?: boolean;
|
|
28
29
|
end: string;
|
|
29
30
|
legend: LegendModel;
|
|
30
31
|
loading: boolean;
|
|
31
32
|
marginBottom?: number;
|
|
32
33
|
shapeLines?: GlobalAreaLines;
|
|
33
34
|
start: string;
|
|
35
|
+
thresholdLabels?: Array<string>;
|
|
36
|
+
thresholdUnit?: string;
|
|
37
|
+
thresholds?: Array<number>;
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
const WrapperLineChart = ({
|
|
@@ -50,12 +54,16 @@ const WrapperLineChart = ({
|
|
|
50
54
|
legend,
|
|
51
55
|
header,
|
|
52
56
|
curve = Curve.curveLinear,
|
|
53
|
-
marginBottom = 0
|
|
57
|
+
marginBottom = 0,
|
|
58
|
+
thresholds,
|
|
59
|
+
thresholdUnit,
|
|
60
|
+
thresholdLabels,
|
|
61
|
+
disabledThresholds
|
|
54
62
|
}: Props): JSX.Element | null => {
|
|
55
63
|
const { adjustedData } = useLineChartData({ data, end, start });
|
|
56
64
|
const lineChartRef = useRef<HTMLDivElement | null>(null);
|
|
57
65
|
|
|
58
|
-
if (loading
|
|
66
|
+
if (loading && !adjustedData) {
|
|
59
67
|
return (
|
|
60
68
|
<LoadingSkeleton
|
|
61
69
|
displayTitleSkeleton={header?.displayTitle ?? true}
|
|
@@ -64,6 +72,10 @@ const WrapperLineChart = ({
|
|
|
64
72
|
);
|
|
65
73
|
}
|
|
66
74
|
|
|
75
|
+
if (!adjustedData) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
67
79
|
return (
|
|
68
80
|
<div
|
|
69
81
|
ref={lineChartRef as MutableRefObject<HTMLDivElement>}
|
|
@@ -79,6 +91,7 @@ const WrapperLineChart = ({
|
|
|
79
91
|
annotationEvent={annotationEvent}
|
|
80
92
|
axis={axis}
|
|
81
93
|
curve={curve}
|
|
94
|
+
disabledThresholds={disabledThresholds}
|
|
82
95
|
displayAnchor={displayAnchor}
|
|
83
96
|
graphData={adjustedData}
|
|
84
97
|
graphInterval={{ end, start }}
|
|
@@ -89,6 +102,9 @@ const WrapperLineChart = ({
|
|
|
89
102
|
loading={loading}
|
|
90
103
|
marginBottom={marginBottom}
|
|
91
104
|
shapeLines={shapeLines}
|
|
105
|
+
thresholdLabels={thresholdLabels}
|
|
106
|
+
thresholdUnit={thresholdUnit}
|
|
107
|
+
thresholds={thresholds}
|
|
92
108
|
timeShiftZones={timeShiftZones}
|
|
93
109
|
tooltip={tooltip}
|
|
94
110
|
width={width ?? responsiveWidth}
|