@centreon/ui 24.4.1-MON-34377-implement-tree-chart-on-centreon-ui.2 → 24.4.1-MON-34377-implement-tree-chart-on-centreon-ui.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.
- package/package.json +5 -2
- package/src/Graph/LineChart/Header/index.tsx +3 -31
- package/src/Graph/LineChart/InteractiveComponents/AnchorPoint/useTickGraph.ts +9 -11
- package/src/Graph/LineChart/InteractiveComponents/GraphValueTooltip/GraphValueTooltip.tsx +68 -0
- package/src/Graph/LineChart/InteractiveComponents/GraphValueTooltip/useGraphValueTooltip.ts +27 -0
- package/src/Graph/LineChart/InteractiveComponents/GraphValueTooltip/useGraphValueTooltipStyles.ts +31 -0
- package/src/Graph/LineChart/InteractiveComponents/index.tsx +132 -16
- package/src/Graph/LineChart/InteractiveComponents/interactionWithGraphAtoms.ts +7 -27
- package/src/Graph/LineChart/Legend/LegendHeader.tsx +8 -5
- package/src/Graph/LineChart/Legend/index.tsx +10 -47
- package/src/Graph/LineChart/LineChart.cypress.spec.tsx +91 -0
- package/src/Graph/LineChart/LineChart.styles.ts +8 -0
- package/src/Graph/LineChart/LineChart.tsx +107 -109
- package/src/Graph/LineChart/mockedData/lastDayWithIncompleteValues.json +1320 -0
- package/src/Graph/LineChart/mockedData/lastDayWithNullValues.json +1314 -0
- package/src/Graph/LineChart/models.ts +12 -0
- package/src/Graph/Tree/DescendantNodes.tsx +88 -0
- package/src/Graph/Tree/Links.tsx +64 -0
- package/src/Graph/Tree/StandaloneTree.tsx +32 -0
- package/src/Graph/Tree/Tree.stories.tsx +156 -0
- package/src/Graph/Tree/Tree.tsx +111 -0
- package/src/Graph/Tree/constants.ts +2 -0
- package/src/Graph/Tree/index.ts +4 -0
- package/src/Graph/Tree/models.ts +50 -0
- package/src/Graph/Tree/stories/contents.tsx +164 -0
- package/src/Graph/Tree/stories/datas.ts +305 -0
- package/src/Graph/Tree/utils.ts +49 -0
- package/src/Graph/common/timeSeries/index.ts +31 -1
- package/src/Graph/index.ts +1 -0
- package/src/components/Zoom/Minimap.tsx +4 -1
- package/src/components/Zoom/Zoom.tsx +32 -6
- package/src/components/Zoom/localPoint.ts +61 -0
- package/src/components/Zoom/models.ts +10 -0
- package/src/components/Zoom/useMinimap.ts +9 -11
- package/src/components/Zoom/useZoom.ts +69 -0
- package/src/components/index.ts +1 -0
- package/src/Graph/LineChart/InteractiveComponents/AnchorPoint/TooltipAnchorPoint.tsx +0 -96
- package/src/Graph/LineChart/InteractiveComponents/AnchorPoint/useTooltipAnchorPoint.ts +0 -107
- package/src/Graph/LineChart/Legend/InteractiveValue.tsx +0 -22
- package/src/Graph/LineChart/Legend/useInteractiveValues.ts +0 -99
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@centreon/ui",
|
|
3
|
-
"version": "24.4.1-MON-34377-implement-tree-chart-on-centreon-ui.
|
|
3
|
+
"version": "24.4.1-MON-34377-implement-tree-chart-on-centreon-ui.4",
|
|
4
4
|
"description": "Centreon UI Components",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"update:deps": "pnpx npm-check-updates -i --format group",
|
|
@@ -116,11 +116,14 @@
|
|
|
116
116
|
"@react-spring/web": "^9.7.3",
|
|
117
117
|
"@visx/clip-path": "^3.3.0",
|
|
118
118
|
"@visx/curve": "^2.1.0",
|
|
119
|
+
"@visx/event": "^3.3.0",
|
|
119
120
|
"@visx/group": "^3.3.0",
|
|
121
|
+
"@visx/hierarchy": "^3.3.0",
|
|
120
122
|
"@visx/legend": "^3.5.0",
|
|
121
123
|
"@visx/pattern": "^3.3.0",
|
|
124
|
+
"@visx/point": "^3.3.0",
|
|
122
125
|
"@visx/scale": "^3.5.0",
|
|
123
|
-
"@visx/shape": "^2.
|
|
126
|
+
"@visx/shape": "^2.18.0",
|
|
124
127
|
"@visx/text": "^3.3.0",
|
|
125
128
|
"@visx/threshold": "^2.12.2",
|
|
126
129
|
"@visx/visx": "2.16.0",
|
|
@@ -1,41 +1,17 @@
|
|
|
1
|
-
import { ScaleLinear } from 'd3-scale';
|
|
2
|
-
import { isNil } from 'ramda';
|
|
3
|
-
|
|
4
1
|
import Typography from '@mui/material/Typography';
|
|
5
2
|
|
|
6
|
-
import {
|
|
3
|
+
import { useMemoComponent } from '@centreon/ui';
|
|
7
4
|
|
|
8
5
|
import { useStyles } from '../LineChart.styles';
|
|
9
|
-
import useTickGraph from '../InteractiveComponents/AnchorPoint/useTickGraph';
|
|
10
6
|
import { LineChartHeader } from '../models';
|
|
11
|
-
import { TimeValue } from '../../common/timeSeries/models';
|
|
12
7
|
|
|
13
8
|
interface Props {
|
|
14
|
-
displayTimeTick?: boolean;
|
|
15
9
|
header?: LineChartHeader;
|
|
16
|
-
timeSeries: Array<TimeValue>;
|
|
17
10
|
title: string;
|
|
18
|
-
xScale: ScaleLinear<number, number>;
|
|
19
11
|
}
|
|
20
12
|
|
|
21
|
-
const Header = ({
|
|
22
|
-
title,
|
|
23
|
-
displayTimeTick = true,
|
|
24
|
-
header,
|
|
25
|
-
timeSeries,
|
|
26
|
-
xScale
|
|
27
|
-
}: Props): JSX.Element => {
|
|
13
|
+
const Header = ({ title, header }: Props): JSX.Element => {
|
|
28
14
|
const { classes } = useStyles();
|
|
29
|
-
const { toDateTime } = useLocaleDateTimeFormat();
|
|
30
|
-
|
|
31
|
-
const { tickAxisBottom } = useTickGraph({
|
|
32
|
-
timeSeries,
|
|
33
|
-
xScale
|
|
34
|
-
});
|
|
35
|
-
const time =
|
|
36
|
-
displayTimeTick && !isNil(tickAxisBottom)
|
|
37
|
-
? toDateTime(tickAxisBottom)
|
|
38
|
-
: null;
|
|
39
15
|
|
|
40
16
|
const displayTitle = header?.displayTitle ?? true;
|
|
41
17
|
|
|
@@ -49,16 +25,12 @@ const Header = ({
|
|
|
49
25
|
{title}
|
|
50
26
|
</Typography>
|
|
51
27
|
)}
|
|
52
|
-
|
|
53
|
-
<Typography align="center" style={{ height: 20 }} variant="body1">
|
|
54
|
-
{time}
|
|
55
|
-
</Typography>
|
|
56
28
|
</div>
|
|
57
29
|
{header?.extraComponent}
|
|
58
30
|
</div>
|
|
59
31
|
),
|
|
60
32
|
|
|
61
|
-
memoProps: [
|
|
33
|
+
memoProps: [title, header]
|
|
62
34
|
});
|
|
63
35
|
};
|
|
64
36
|
|
|
@@ -2,13 +2,12 @@ import { useEffect, useState } from 'react';
|
|
|
2
2
|
|
|
3
3
|
import { ScaleLinear } from 'd3-scale';
|
|
4
4
|
import { useAtomValue } from 'jotai';
|
|
5
|
-
import { isEmpty, isNil, not } from 'ramda';
|
|
6
5
|
|
|
7
6
|
import useAxisY from '../../BasicComponents/Axes/useAxisY';
|
|
8
7
|
import { margin } from '../../common';
|
|
9
|
-
import {
|
|
8
|
+
import { getTimeValue } from '../../../common/timeSeries';
|
|
10
9
|
import { Line, TimeValue } from '../../../common/timeSeries/models';
|
|
11
|
-
import { mousePositionAtom
|
|
10
|
+
import { mousePositionAtom } from '../interactionWithGraphAtoms';
|
|
12
11
|
|
|
13
12
|
interface AnchorPointResult {
|
|
14
13
|
positionX?: number;
|
|
@@ -42,14 +41,13 @@ const useTickGraph = ({
|
|
|
42
41
|
const { axisRight, axisLeft } = useAxisY({ data: { baseAxis, lines } });
|
|
43
42
|
|
|
44
43
|
const mousePosition = useAtomValue(mousePositionAtom);
|
|
45
|
-
const timeValueData = useAtomValue(timeValueAtom);
|
|
46
44
|
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
45
|
+
const positionX = mousePosition
|
|
46
|
+
? mousePosition[0] - margin.left - 4
|
|
47
|
+
: undefined;
|
|
48
|
+
const positionY = mousePosition
|
|
49
|
+
? mousePosition[1] - margin.top + 2
|
|
50
|
+
: undefined;
|
|
53
51
|
|
|
54
52
|
useEffect(() => {
|
|
55
53
|
if (!mousePosition) {
|
|
@@ -62,7 +60,7 @@ const useTickGraph = ({
|
|
|
62
60
|
const mousePositionTimeTick = mousePosition
|
|
63
61
|
? getTimeValue({ timeSeries, x: mousePosition[0], xScale }).timeTick
|
|
64
62
|
: 0;
|
|
65
|
-
const timeTickValue =
|
|
63
|
+
const timeTickValue = mousePosition
|
|
66
64
|
? new Date(mousePositionTimeTick)
|
|
67
65
|
: null;
|
|
68
66
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { useAtomValue } from 'jotai';
|
|
2
|
+
import { equals, isNil } from 'ramda';
|
|
3
|
+
|
|
4
|
+
import { Box, Typography } from '@mui/material';
|
|
5
|
+
|
|
6
|
+
import { mousePositionAtom } from '../interactionWithGraphAtoms';
|
|
7
|
+
import { formatMetricValueWithUnit } from '../../../common/timeSeries';
|
|
8
|
+
|
|
9
|
+
import { useGraphValueTooltip } from './useGraphValueTooltip';
|
|
10
|
+
import { useGraphValueTooltipStyles } from './useGraphValueTooltipStyles';
|
|
11
|
+
|
|
12
|
+
interface Props {
|
|
13
|
+
base: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const GraphValueTooltip = ({ base }: Props): JSX.Element | null => {
|
|
17
|
+
const { classes } = useGraphValueTooltipStyles();
|
|
18
|
+
const mousePosition = useAtomValue(mousePositionAtom);
|
|
19
|
+
|
|
20
|
+
const graphValue = useGraphValueTooltip();
|
|
21
|
+
|
|
22
|
+
if (isNil(graphValue) || isNil(mousePosition)) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div className={classes.tooltipContainer}>
|
|
28
|
+
<Typography fontWeight="bold">{graphValue.dateTime}</Typography>
|
|
29
|
+
<div className={classes.metrics}>
|
|
30
|
+
{graphValue.metrics.map(({ unit, color, id, value, name }) => {
|
|
31
|
+
const isMetricHighlighted = equals(
|
|
32
|
+
id,
|
|
33
|
+
graphValue.highlightedMetricId
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<div
|
|
38
|
+
className={classes.metric}
|
|
39
|
+
data-highlight={isMetricHighlighted}
|
|
40
|
+
data-metric={name}
|
|
41
|
+
key={id}
|
|
42
|
+
>
|
|
43
|
+
<Box
|
|
44
|
+
className={classes.metricColorBox}
|
|
45
|
+
sx={{ backgroundColor: color }}
|
|
46
|
+
/>
|
|
47
|
+
<Typography
|
|
48
|
+
className={classes.metricName}
|
|
49
|
+
fontWeight={isMetricHighlighted ? 'bold' : undefined}
|
|
50
|
+
>
|
|
51
|
+
{name}
|
|
52
|
+
</Typography>
|
|
53
|
+
<Typography fontWeight={isMetricHighlighted ? 'bold' : undefined}>
|
|
54
|
+
{formatMetricValueWithUnit({
|
|
55
|
+
base,
|
|
56
|
+
unit,
|
|
57
|
+
value
|
|
58
|
+
})}
|
|
59
|
+
</Typography>
|
|
60
|
+
</div>
|
|
61
|
+
);
|
|
62
|
+
})}
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
);
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export default GraphValueTooltip;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useAtomValue } from 'jotai';
|
|
2
|
+
import { isNil } from 'ramda';
|
|
3
|
+
|
|
4
|
+
import { graphTooltipDataAtom } from '../interactionWithGraphAtoms';
|
|
5
|
+
import { useLocaleDateTimeFormat } from '../../../../utils';
|
|
6
|
+
import { GraphTooltipData } from '../../models';
|
|
7
|
+
|
|
8
|
+
interface UseGraphValueTooltipState extends Omit<GraphTooltipData, 'date'> {
|
|
9
|
+
dateTime: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const useGraphValueTooltip = (): UseGraphValueTooltipState | null => {
|
|
13
|
+
const { toDate, toTime } = useLocaleDateTimeFormat();
|
|
14
|
+
const graphTooltipData = useAtomValue(graphTooltipDataAtom);
|
|
15
|
+
|
|
16
|
+
if (isNil(graphTooltipData)) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const formattedDateTime = `${toDate(graphTooltipData.date)} / ${toTime(graphTooltipData.date)}`;
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
dateTime: formattedDateTime,
|
|
24
|
+
highlightedMetricId: graphTooltipData.highlightedMetricId,
|
|
25
|
+
metrics: graphTooltipData.metrics
|
|
26
|
+
};
|
|
27
|
+
};
|
package/src/Graph/LineChart/InteractiveComponents/GraphValueTooltip/useGraphValueTooltipStyles.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { makeStyles } from 'tss-react/mui';
|
|
2
|
+
|
|
3
|
+
export const useGraphValueTooltipStyles = makeStyles()((theme) => ({
|
|
4
|
+
dateTimeLabel: {
|
|
5
|
+
fontSize: '1.1rem'
|
|
6
|
+
},
|
|
7
|
+
metric: {
|
|
8
|
+
alignItems: 'center',
|
|
9
|
+
display: 'flex',
|
|
10
|
+
gap: theme.spacing(1),
|
|
11
|
+
weidth: '100%'
|
|
12
|
+
},
|
|
13
|
+
metricColorBox: {
|
|
14
|
+
borderRadius: theme.shape.borderRadius,
|
|
15
|
+
flexShrink: 0,
|
|
16
|
+
height: theme.spacing(1.5),
|
|
17
|
+
width: theme.spacing(1.5)
|
|
18
|
+
},
|
|
19
|
+
metricName: {
|
|
20
|
+
flexGrow: 1
|
|
21
|
+
},
|
|
22
|
+
metrics: {
|
|
23
|
+
display: 'flex',
|
|
24
|
+
flexDirection: 'column',
|
|
25
|
+
marginTop: theme.spacing(0.5),
|
|
26
|
+
width: '100%'
|
|
27
|
+
},
|
|
28
|
+
tooltipContainer: {
|
|
29
|
+
padding: theme.spacing(1)
|
|
30
|
+
}
|
|
31
|
+
}));
|
|
@@ -3,7 +3,21 @@ import { MutableRefObject } from 'react';
|
|
|
3
3
|
import { Event } from '@visx/visx';
|
|
4
4
|
import { ScaleTime } from 'd3-scale';
|
|
5
5
|
import { useSetAtom } from 'jotai';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
all,
|
|
8
|
+
equals,
|
|
9
|
+
find,
|
|
10
|
+
isEmpty,
|
|
11
|
+
isNil,
|
|
12
|
+
keys,
|
|
13
|
+
map,
|
|
14
|
+
pick,
|
|
15
|
+
pipe,
|
|
16
|
+
pluck,
|
|
17
|
+
reduce,
|
|
18
|
+
toPairs,
|
|
19
|
+
values
|
|
20
|
+
} from 'ramda';
|
|
7
21
|
import { makeStyles } from 'tss-react/mui';
|
|
8
22
|
|
|
9
23
|
import {
|
|
@@ -12,8 +26,16 @@ import {
|
|
|
12
26
|
InteractedZone,
|
|
13
27
|
InteractedZone as ZoomPreviewModel
|
|
14
28
|
} from '../models';
|
|
15
|
-
import {
|
|
16
|
-
|
|
29
|
+
import {
|
|
30
|
+
formatMetricName,
|
|
31
|
+
getLineForMetric,
|
|
32
|
+
getLinesForMetrics,
|
|
33
|
+
getTimeValue,
|
|
34
|
+
getUnits,
|
|
35
|
+
getYScale
|
|
36
|
+
} from '../../common/timeSeries';
|
|
37
|
+
import { Line, TimeValue } from '../../common/timeSeries/models';
|
|
38
|
+
import { margin } from '../common';
|
|
17
39
|
|
|
18
40
|
import Annotations from './Annotations';
|
|
19
41
|
import { TimelineEvent } from './Annotations/models';
|
|
@@ -22,10 +44,11 @@ import TimeShiftZones from './TimeShiftZones';
|
|
|
22
44
|
import ZoomPreview from './ZoomPreview';
|
|
23
45
|
import {
|
|
24
46
|
MousePosition,
|
|
25
|
-
|
|
47
|
+
changeMousePositionDerivedAtom,
|
|
26
48
|
eventMouseDownAtom,
|
|
27
49
|
eventMouseLeaveAtom,
|
|
28
|
-
eventMouseUpAtom
|
|
50
|
+
eventMouseUpAtom,
|
|
51
|
+
graphTooltipDataAtom
|
|
29
52
|
} from './interactionWithGraphAtoms';
|
|
30
53
|
|
|
31
54
|
const useStyles = makeStyles()(() => ({
|
|
@@ -38,6 +61,9 @@ interface CommonData {
|
|
|
38
61
|
graphHeight: number;
|
|
39
62
|
graphSvgRef: MutableRefObject<SVGSVGElement | null>;
|
|
40
63
|
graphWidth: number;
|
|
64
|
+
leftScale;
|
|
65
|
+
lines;
|
|
66
|
+
rightScale;
|
|
41
67
|
timeSeries: Array<TimeValue>;
|
|
42
68
|
xScale: ScaleTime<number, number>;
|
|
43
69
|
}
|
|
@@ -65,13 +91,19 @@ const InteractionWithGraph = ({
|
|
|
65
91
|
const setEventMouseDown = useSetAtom(eventMouseDownAtom);
|
|
66
92
|
const setEventMouseUp = useSetAtom(eventMouseUpAtom);
|
|
67
93
|
const setEventMouseLeave = useSetAtom(eventMouseLeaveAtom);
|
|
94
|
+
const changeMousePosition = useSetAtom(changeMousePositionDerivedAtom);
|
|
95
|
+
const setGraphTooltipData = useSetAtom(graphTooltipDataAtom);
|
|
68
96
|
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
97
|
+
const {
|
|
98
|
+
graphHeight,
|
|
99
|
+
graphWidth,
|
|
100
|
+
graphSvgRef,
|
|
101
|
+
xScale,
|
|
102
|
+
timeSeries,
|
|
103
|
+
leftScale,
|
|
104
|
+
rightScale,
|
|
105
|
+
lines
|
|
106
|
+
} = commonData;
|
|
75
107
|
|
|
76
108
|
const displayZoomPreview = zoomData?.enable ?? true;
|
|
77
109
|
|
|
@@ -83,6 +115,7 @@ const InteractionWithGraph = ({
|
|
|
83
115
|
setEventMouseLeave(event);
|
|
84
116
|
setEventMouseDown(null);
|
|
85
117
|
updateMousePosition(null);
|
|
118
|
+
setGraphTooltipData(null);
|
|
86
119
|
};
|
|
87
120
|
|
|
88
121
|
const mouseUp = (event): void => {
|
|
@@ -107,10 +140,10 @@ const InteractionWithGraph = ({
|
|
|
107
140
|
|
|
108
141
|
const updateMousePosition = (pointPosition: MousePosition): void => {
|
|
109
142
|
if (isNil(pointPosition)) {
|
|
110
|
-
|
|
111
|
-
position: null
|
|
112
|
-
timeValue: null
|
|
143
|
+
changeMousePosition({
|
|
144
|
+
position: null
|
|
113
145
|
});
|
|
146
|
+
setGraphTooltipData(null);
|
|
114
147
|
|
|
115
148
|
return;
|
|
116
149
|
}
|
|
@@ -120,7 +153,89 @@ const InteractionWithGraph = ({
|
|
|
120
153
|
xScale
|
|
121
154
|
});
|
|
122
155
|
|
|
123
|
-
|
|
156
|
+
if (isNil(timeValue)) {
|
|
157
|
+
changeMousePosition({
|
|
158
|
+
position: null
|
|
159
|
+
});
|
|
160
|
+
setGraphTooltipData(null);
|
|
161
|
+
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const date = timeValue.timeTick;
|
|
166
|
+
const displayedMetricIds = pluck('metric_id', lines);
|
|
167
|
+
const filteredMetricsValue = pick(displayedMetricIds, timeValue);
|
|
168
|
+
const [, secondUnit, thirdUnit] = getUnits(lines as Array<Line>);
|
|
169
|
+
const areAllValuesEmpty = pipe(values, all(isNil))(filteredMetricsValue);
|
|
170
|
+
|
|
171
|
+
const linesData = getLinesForMetrics({
|
|
172
|
+
lines,
|
|
173
|
+
metricIds: keys(filteredMetricsValue).map(Number)
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
if (areAllValuesEmpty) {
|
|
177
|
+
changeMousePosition({ position: pointPosition });
|
|
178
|
+
setGraphTooltipData(null);
|
|
179
|
+
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const distanceWithPointPositionPerMetric = reduce(
|
|
184
|
+
(acc, [metricId, value]) => {
|
|
185
|
+
if (isNil(value)) {
|
|
186
|
+
return acc;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const lineData = getLineForMetric({
|
|
190
|
+
lines,
|
|
191
|
+
metric_id: Number(metricId)
|
|
192
|
+
});
|
|
193
|
+
const yScale = getYScale({
|
|
194
|
+
hasMoreThanTwoUnits: Boolean(thirdUnit),
|
|
195
|
+
invert: (lineData as Line).invert,
|
|
196
|
+
leftScale,
|
|
197
|
+
rightScale,
|
|
198
|
+
secondUnit,
|
|
199
|
+
unit: (lineData as Line).unit
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const y0 = yScale(value);
|
|
203
|
+
|
|
204
|
+
const diffBetweenY0AndPointPosition = Math.abs(
|
|
205
|
+
y0 + margin.top - pointPosition[1]
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
...acc,
|
|
210
|
+
[metricId]: diffBetweenY0AndPointPosition
|
|
211
|
+
};
|
|
212
|
+
},
|
|
213
|
+
{},
|
|
214
|
+
Object.entries(filteredMetricsValue)
|
|
215
|
+
);
|
|
216
|
+
|
|
217
|
+
const nearestY0 = Math.min(...values(distanceWithPointPositionPerMetric));
|
|
218
|
+
|
|
219
|
+
const nearestLine = pipe(
|
|
220
|
+
toPairs,
|
|
221
|
+
find(([, y0]) => equals(y0, nearestY0)) as () => [string, number]
|
|
222
|
+
)(distanceWithPointPositionPerMetric);
|
|
223
|
+
|
|
224
|
+
changeMousePosition({ position: pointPosition });
|
|
225
|
+
setGraphTooltipData({
|
|
226
|
+
date,
|
|
227
|
+
highlightedMetricId: Number(nearestLine[0]),
|
|
228
|
+
metrics: map(
|
|
229
|
+
({ metric_id, color, unit, legend, name }) => ({
|
|
230
|
+
color,
|
|
231
|
+
id: metric_id,
|
|
232
|
+
name: formatMetricName({ legend, name }),
|
|
233
|
+
unit,
|
|
234
|
+
value: timeValue?.[metric_id]
|
|
235
|
+
}),
|
|
236
|
+
linesData
|
|
237
|
+
).filter(({ value }) => value)
|
|
238
|
+
});
|
|
124
239
|
};
|
|
125
240
|
|
|
126
241
|
return (
|
|
@@ -151,8 +266,9 @@ const InteractionWithGraph = ({
|
|
|
151
266
|
)}
|
|
152
267
|
<Bar
|
|
153
268
|
className={classes.overlay}
|
|
269
|
+
data-testid="graph-interaction-zone"
|
|
154
270
|
fill="transparent"
|
|
155
|
-
height={graphHeight}
|
|
271
|
+
height={graphHeight - margin.bottom}
|
|
156
272
|
width={graphWidth}
|
|
157
273
|
x={0}
|
|
158
274
|
y={0}
|
|
@@ -1,52 +1,32 @@
|
|
|
1
1
|
import { atom } from 'jotai';
|
|
2
|
-
import {
|
|
2
|
+
import { isNil } from 'ramda';
|
|
3
3
|
|
|
4
4
|
import { TimeValue } from '../../common/timeSeries/models';
|
|
5
|
+
import { GraphTooltipData } from '../models';
|
|
5
6
|
|
|
6
7
|
export const eventMouseDownAtom = atom<null | MouseEvent>(null);
|
|
7
8
|
export const eventMouseUpAtom = atom<null | MouseEvent>(null);
|
|
8
9
|
export const eventMouseLeaveAtom = atom<null | MouseEvent>(null);
|
|
10
|
+
export const graphTooltipDataAtom = atom<GraphTooltipData | null>(null);
|
|
9
11
|
|
|
10
12
|
export const timeValueAtom = atom<TimeValue | null>(null);
|
|
11
13
|
export const mousePositionAtom = atom<MousePosition>(null);
|
|
12
14
|
export const isListingGraphOpenAtom = atom(false);
|
|
13
15
|
export type MousePosition = [number, number] | null;
|
|
14
16
|
|
|
15
|
-
interface
|
|
17
|
+
interface Position {
|
|
16
18
|
position: MousePosition;
|
|
17
|
-
timeValue: TimeValue | null;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
isInViewport?: boolean;
|
|
22
|
-
newTimeValue: TimeValue | null;
|
|
23
|
-
}
|
|
24
|
-
export const changeTimeValueDerivedAtom = atom(
|
|
25
|
-
null,
|
|
26
|
-
(
|
|
27
|
-
_,
|
|
28
|
-
set,
|
|
29
|
-
{ newTimeValue, isInViewport }: NewTimeValueInViewportState
|
|
30
|
-
): void => {
|
|
31
|
-
if (not(isInViewport)) {
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
set(timeValueAtom, newTimeValue);
|
|
35
|
-
}
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
export const changeMousePositionAndTimeValueDerivedAtom = atom(
|
|
21
|
+
export const changeMousePositionDerivedAtom = atom(
|
|
39
22
|
null,
|
|
40
|
-
(_, set, { position
|
|
41
|
-
if (isNil(position)
|
|
23
|
+
(_, set, { position }: Position): void => {
|
|
24
|
+
if (isNil(position)) {
|
|
42
25
|
set(mousePositionAtom, null);
|
|
43
|
-
set(timeValueAtom, null);
|
|
44
26
|
|
|
45
27
|
return;
|
|
46
28
|
}
|
|
47
29
|
|
|
48
30
|
set(mousePositionAtom, position);
|
|
49
|
-
|
|
50
|
-
set(timeValueAtom, timeValue);
|
|
51
31
|
}
|
|
52
32
|
);
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isEmpty } from 'ramda';
|
|
2
2
|
|
|
3
3
|
import { Typography } from '@mui/material';
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
EllipsisTypography,
|
|
7
|
+
formatMetricName,
|
|
8
|
+
formatMetricValue
|
|
9
|
+
} from '../../..';
|
|
6
10
|
import { Line } from '../../common/timeSeries/models';
|
|
7
11
|
import { Tooltip } from '../../../components';
|
|
8
12
|
|
|
@@ -29,12 +33,11 @@ const LegendHeader = ({
|
|
|
29
33
|
|
|
30
34
|
const { unit, name, legend } = line;
|
|
31
35
|
|
|
36
|
+
const metricName = formatMetricName({ legend, name });
|
|
37
|
+
|
|
32
38
|
const legendName = legend || name;
|
|
33
39
|
const hasUnit = !isEmpty(unit);
|
|
34
40
|
const unitName = `(${unit})`;
|
|
35
|
-
const metricName = includes('#', legendName)
|
|
36
|
-
? split('#')(legendName)[1]
|
|
37
|
-
: legendName;
|
|
38
41
|
|
|
39
42
|
const getEndText = (): string => {
|
|
40
43
|
if (value) {
|