@centreon/ui 25.11.9 → 26.1.0
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/BarChart/BarChart.cypress.spec.tsx +55 -83
- package/src/Graph/BarChart/BarChart.tsx +4 -0
- package/src/Graph/BarChart/BarStack.tsx +26 -15
- package/src/Graph/BarChart/MemoizedGroup.tsx +8 -7
- package/src/Graph/Chart/BasicComponents/Lines/StackedLines/index.tsx +5 -4
- package/src/Graph/Chart/BasicComponents/Lines/index.tsx +92 -92
- package/src/Graph/Chart/Chart.tsx +4 -2
- package/src/Graph/Chart/Legend/index.tsx +15 -14
- package/src/Graph/Chart/index.tsx +4 -0
- package/src/Graph/common/timeSeries/index.ts +65 -40
- package/src/Graph/mockedData/dataWithMissingPoint.json +2 -0
package/package.json
CHANGED
|
@@ -87,6 +87,61 @@ const checkWidth = (orientation): void => {
|
|
|
87
87
|
};
|
|
88
88
|
|
|
89
89
|
describe('Bar chart', () => {
|
|
90
|
+
it('displays a tooltip when a single bar is hovered', () => {
|
|
91
|
+
initialize({
|
|
92
|
+
orientation: 'horizontal'
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
checkWidth('horizontal');
|
|
96
|
+
cy.contains('0 ms').should('be.visible');
|
|
97
|
+
cy.contains('20').should('be.visible');
|
|
98
|
+
cy.contains(':40 AM').should('be.visible');
|
|
99
|
+
|
|
100
|
+
cy.findByTestId('stacked-bar-10-0-7650.368581547736').realHover();
|
|
101
|
+
|
|
102
|
+
cy.contains('06/19/2024').should('be.visible');
|
|
103
|
+
cy.contains('Centreon-Server: Round-Trip Maximum Time').should(
|
|
104
|
+
'be.visible'
|
|
105
|
+
);
|
|
106
|
+
cy.contains('7.47 KB').should('be.visible');
|
|
107
|
+
|
|
108
|
+
cy.makeSnapshot();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('displays a tooltip when a stacked bar is hovered', () => {
|
|
112
|
+
initialize({
|
|
113
|
+
data: dataPingServiceStacked,
|
|
114
|
+
orientation: 'horizontal',
|
|
115
|
+
tooltip: {
|
|
116
|
+
mode: 'all',
|
|
117
|
+
sortOrder: 'ascending'
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
checkWidth('horizontal');
|
|
122
|
+
cy.contains('0 ms').should('be.visible');
|
|
123
|
+
cy.contains('20').should('be.visible');
|
|
124
|
+
cy.contains(':40 AM').should('be.visible');
|
|
125
|
+
|
|
126
|
+
cy.findByTestId('stacked-bar-1-0-0.05296').realHover();
|
|
127
|
+
|
|
128
|
+
cy.contains('06/19/2024').should('be.visible');
|
|
129
|
+
cy.contains('Centreon-Server: Round-Trip Maximum Time').should(
|
|
130
|
+
'be.visible'
|
|
131
|
+
);
|
|
132
|
+
cy.contains('Centreon-Server: Round-Trip Average Time').should(
|
|
133
|
+
'be.visible'
|
|
134
|
+
);
|
|
135
|
+
cy.contains('Centreon-Server: Round-Trip Minimum Time').should(
|
|
136
|
+
'be.visible'
|
|
137
|
+
);
|
|
138
|
+
cy.contains('0.05 ms').should('be.visible');
|
|
139
|
+
cy.contains('0.02 ms').should('be.visible');
|
|
140
|
+
cy.contains('0.11 ms').should('be.visible');
|
|
141
|
+
|
|
142
|
+
cy.findByTestId('stacked-bar-3-0-0.16196').should('be.visible');
|
|
143
|
+
});
|
|
144
|
+
|
|
90
145
|
['horizontal', 'vertical'].forEach((orientation) => {
|
|
91
146
|
it(`displays the bar chart ${orientation}ly`, () => {
|
|
92
147
|
initialize({ orientation });
|
|
@@ -186,27 +241,6 @@ describe('Bar chart', () => {
|
|
|
186
241
|
});
|
|
187
242
|
});
|
|
188
243
|
|
|
189
|
-
it('displays a tooltip when a single bar is hovered', () => {
|
|
190
|
-
initialize({
|
|
191
|
-
orientation: 'horizontal'
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
checkWidth('horizontal');
|
|
195
|
-
cy.contains('0 ms').should('be.visible');
|
|
196
|
-
cy.contains('20').should('be.visible');
|
|
197
|
-
cy.contains(':40 AM').should('be.visible');
|
|
198
|
-
|
|
199
|
-
cy.findByTestId('stacked-bar-10-0-7650.368581547736').realHover();
|
|
200
|
-
|
|
201
|
-
cy.contains('06/19/2024').should('be.visible');
|
|
202
|
-
cy.contains('Centreon-Server: Round-Trip Maximum Time').should(
|
|
203
|
-
'be.visible'
|
|
204
|
-
);
|
|
205
|
-
cy.contains('7.47 KB').should('be.visible');
|
|
206
|
-
|
|
207
|
-
cy.makeSnapshot();
|
|
208
|
-
});
|
|
209
|
-
|
|
210
244
|
it('does not display a tooltip when a bar is hovered and a props is set', () => {
|
|
211
245
|
initialize({
|
|
212
246
|
data: dataPingServiceStacked,
|
|
@@ -231,68 +265,6 @@ describe('Bar chart', () => {
|
|
|
231
265
|
cy.makeSnapshot();
|
|
232
266
|
});
|
|
233
267
|
|
|
234
|
-
it('displays a tooltip when a stacked bar is hovered', () => {
|
|
235
|
-
initialize({
|
|
236
|
-
data: dataPingServiceStacked,
|
|
237
|
-
orientation: 'horizontal',
|
|
238
|
-
tooltip: {
|
|
239
|
-
mode: 'all',
|
|
240
|
-
sortOrder: 'ascending'
|
|
241
|
-
}
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
checkWidth('horizontal');
|
|
245
|
-
cy.contains('0 ms').should('be.visible');
|
|
246
|
-
cy.contains('20').should('be.visible');
|
|
247
|
-
cy.contains(':40 AM').should('be.visible');
|
|
248
|
-
|
|
249
|
-
cy.findByTestId('stacked-bar-1-0-0.05296').realHover();
|
|
250
|
-
|
|
251
|
-
cy.contains('06/19/2024').should('be.visible');
|
|
252
|
-
cy.contains('Centreon-Server: Round-Trip Maximum Time').should(
|
|
253
|
-
'be.visible'
|
|
254
|
-
);
|
|
255
|
-
cy.contains('Centreon-Server: Round-Trip Average Time').should(
|
|
256
|
-
'be.visible'
|
|
257
|
-
);
|
|
258
|
-
cy.contains('Centreon-Server: Round-Trip Minimum Time').should(
|
|
259
|
-
'be.visible'
|
|
260
|
-
);
|
|
261
|
-
cy.contains('0.05 ms').should('be.visible');
|
|
262
|
-
cy.contains('0.02 ms').should('be.visible');
|
|
263
|
-
cy.contains('0.11 ms').should('be.visible');
|
|
264
|
-
|
|
265
|
-
cy.findByTestId('stacked-bar-3-0-0.16196').should('be.visible');
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
it('displays a tooltip with a single metric when a stacked bar is hovered and a prop is set', () => {
|
|
269
|
-
initialize({
|
|
270
|
-
data: dataPingServiceStacked,
|
|
271
|
-
orientation: 'horizontal',
|
|
272
|
-
tooltip: {
|
|
273
|
-
mode: 'single',
|
|
274
|
-
sortOrder: 'descending'
|
|
275
|
-
}
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
checkWidth('horizontal');
|
|
279
|
-
cy.contains('0 ms').should('be.visible');
|
|
280
|
-
cy.contains('20').should('be.visible');
|
|
281
|
-
cy.contains(':40 AM').should('be.visible');
|
|
282
|
-
|
|
283
|
-
cy.findByTestId('stacked-bar-1-0-0.05296').realHover();
|
|
284
|
-
|
|
285
|
-
cy.contains('06/19/2024').should('be.visible');
|
|
286
|
-
cy.contains('Centreon-Server: Round-Trip Average Time').should(
|
|
287
|
-
'be.visible'
|
|
288
|
-
);
|
|
289
|
-
cy.contains('0.05 ms').should('be.visible');
|
|
290
|
-
|
|
291
|
-
cy.findByTestId('stacked-bar-3-0-0.16196').should('be.visible');
|
|
292
|
-
|
|
293
|
-
cy.makeSnapshot();
|
|
294
|
-
});
|
|
295
|
-
|
|
296
268
|
it('displays the bottom axis correctly when data starts from several days ago', () => {
|
|
297
269
|
initialize({
|
|
298
270
|
data: dataLastWeek,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { memo } from 'react';
|
|
1
|
+
import { memo, ReactElement } from 'react';
|
|
2
2
|
|
|
3
3
|
import { ScaleType, scaleBand } from '@visx/scale';
|
|
4
4
|
import { BarRounded } from '@visx/shape';
|
|
@@ -22,6 +22,7 @@ interface Props extends Omit<UseBarStackProps, 'xScale'> {
|
|
|
22
22
|
barWidth: number;
|
|
23
23
|
isTooltipHidden: boolean;
|
|
24
24
|
neutralValue: number;
|
|
25
|
+
isStacked?: boolean;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
const getPadding = ({ padding, size, isNegativeValue }): number => {
|
|
@@ -83,8 +84,9 @@ const BarStack = ({
|
|
|
83
84
|
barIndex,
|
|
84
85
|
isTooltipHidden,
|
|
85
86
|
barStyle = { opacity: 1, radius: 0.2 },
|
|
86
|
-
neutralValue
|
|
87
|
-
|
|
87
|
+
neutralValue,
|
|
88
|
+
isStacked
|
|
89
|
+
}: Props): ReactElement => {
|
|
88
90
|
const {
|
|
89
91
|
BarStackComponent,
|
|
90
92
|
commonBarStackProps,
|
|
@@ -118,6 +120,15 @@ const BarStack = ({
|
|
|
118
120
|
metricId: Number(bar.key)
|
|
119
121
|
}) as BarStyle;
|
|
120
122
|
|
|
123
|
+
const barY =
|
|
124
|
+
isNegativeValue && isStacked && !shouldApplyRadiusOnBottom
|
|
125
|
+
? getPadding({
|
|
126
|
+
isNegativeValue,
|
|
127
|
+
padding: bar.y,
|
|
128
|
+
size: bar.height
|
|
129
|
+
})
|
|
130
|
+
: bar.y;
|
|
131
|
+
|
|
121
132
|
return (
|
|
122
133
|
<BarRounded
|
|
123
134
|
{...barRoundedProps}
|
|
@@ -128,10 +139,10 @@ const BarStack = ({
|
|
|
128
139
|
barWidth,
|
|
129
140
|
y: isHorizontal
|
|
130
141
|
? getPadding({
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
142
|
+
isNegativeValue,
|
|
143
|
+
padding: bar.y,
|
|
144
|
+
size: bar.height
|
|
145
|
+
})
|
|
135
146
|
: barPadding,
|
|
136
147
|
isFirstBar: shouldApplyRadiusOnBottom,
|
|
137
148
|
isHorizontal,
|
|
@@ -146,19 +157,19 @@ const BarStack = ({
|
|
|
146
157
|
isHorizontal
|
|
147
158
|
? barPadding
|
|
148
159
|
: getPadding({
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
160
|
+
isNegativeValue,
|
|
161
|
+
padding: bar.x,
|
|
162
|
+
size: bar.width
|
|
163
|
+
})
|
|
153
164
|
}
|
|
154
|
-
y={isHorizontal ?
|
|
165
|
+
y={isHorizontal ? barY : barPadding}
|
|
155
166
|
onMouseEnter={
|
|
156
167
|
isTooltipHidden
|
|
157
168
|
? undefined
|
|
158
169
|
: hoverBar({
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
170
|
+
barIndex,
|
|
171
|
+
highlightedMetric: Number(bar.key)
|
|
172
|
+
})
|
|
162
173
|
}
|
|
163
174
|
onMouseLeave={isTooltipHidden ? undefined : exitBar}
|
|
164
175
|
/>
|
|
@@ -59,17 +59,18 @@ const MemoizedGroup = ({
|
|
|
59
59
|
const linesBar = isStackedBar
|
|
60
60
|
? stackedLinesTimeSeriesPerStackKeyAndUnit[bar.key].lines
|
|
61
61
|
: (notStackedLines.find(({ metric_id }) =>
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
equals(metric_id, Number(bar.key))
|
|
63
|
+
) as Line);
|
|
64
64
|
const timeSeriesBar = isStackedBar
|
|
65
65
|
? stackedLinesTimeSeriesPerStackKeyAndUnit[bar.key].timeSeries
|
|
66
66
|
: notStackedTimeSeries.map((timeSerie) => ({
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
timeTick: timeSerie.timeTick,
|
|
68
|
+
[bar.key]: timeSerie[Number(bar.key)]
|
|
69
|
+
}));
|
|
70
70
|
|
|
71
71
|
return isStackedBar ? (
|
|
72
72
|
<BarStack
|
|
73
|
+
isStacked
|
|
73
74
|
key={`bar-${barGroup.index}-${bar.width}-${bar.y}-${bar.height}-${bar.x}`}
|
|
74
75
|
barIndex={barGroup.index}
|
|
75
76
|
barPadding={isHorizontal ? bar.x : bar.y}
|
|
@@ -79,7 +80,7 @@ const MemoizedGroup = ({
|
|
|
79
80
|
isTooltipHidden={isTooltipHidden}
|
|
80
81
|
lines={linesBar as Array<Line>}
|
|
81
82
|
timeSeries={timeSeriesBar}
|
|
82
|
-
yScale={yScalesPerUnit[bar.key.split('-')[1]
|
|
83
|
+
yScale={yScalesPerUnit[bar.key.split('-')[1] ?? undefined]}
|
|
83
84
|
neutralValue={neutralValue}
|
|
84
85
|
/>
|
|
85
86
|
) : (
|
|
@@ -93,7 +94,7 @@ const MemoizedGroup = ({
|
|
|
93
94
|
isTooltipHidden={isTooltipHidden}
|
|
94
95
|
lines={[linesBar as Line]}
|
|
95
96
|
timeSeries={timeSeriesBar}
|
|
96
|
-
yScale={yScalesPerUnit[(linesBar as Line).unit]}
|
|
97
|
+
yScale={yScalesPerUnit[(linesBar as Line).unit ?? undefined]}
|
|
97
98
|
neutralValue={neutralValue}
|
|
98
99
|
/>
|
|
99
100
|
);
|
|
@@ -27,6 +27,7 @@ import { StackValue } from '../../../InteractiveComponents/AnchorPoint/models';
|
|
|
27
27
|
import { getCurveFactory, getFillColor } from '../../../common';
|
|
28
28
|
import { LineStyle } from '../../../models';
|
|
29
29
|
import Point from '../Point';
|
|
30
|
+
import { ReactElement } from 'react';
|
|
30
31
|
|
|
31
32
|
interface Props {
|
|
32
33
|
areaTransparency?: number;
|
|
@@ -56,7 +57,7 @@ const StackLines = ({
|
|
|
56
57
|
lineStyle,
|
|
57
58
|
hasSecondUnit,
|
|
58
59
|
maxLeftAxisCharacters
|
|
59
|
-
}: Props):
|
|
60
|
+
}: Props): ReactElement => {
|
|
60
61
|
const curveType = getCurveFactory(
|
|
61
62
|
(equals(type(lineStyle), 'Array')
|
|
62
63
|
? lineStyle?.[0].curve
|
|
@@ -134,9 +135,9 @@ const StackLines = ({
|
|
|
134
135
|
equals(style?.showArea, false)
|
|
135
136
|
? 'transparent'
|
|
136
137
|
: getFillColor({
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
areaColor: areaColor || lineColor,
|
|
139
|
+
transparency: formattedTransparency
|
|
140
|
+
})
|
|
140
141
|
}
|
|
141
142
|
opacity={highlight === false ? 0.3 : 1}
|
|
142
143
|
stroke={lineColor}
|
|
@@ -109,7 +109,7 @@ const Lines = ({
|
|
|
109
109
|
key={`stacked-${unit}`}
|
|
110
110
|
lines={lines}
|
|
111
111
|
timeSeries={stackedTimeSeries}
|
|
112
|
-
yScale={yScalesPerUnit[unit
|
|
112
|
+
yScale={yScalesPerUnit[unit ?? undefined]}
|
|
113
113
|
{...commonStackedLinesProps}
|
|
114
114
|
/>
|
|
115
115
|
);
|
|
@@ -128,7 +128,7 @@ const Lines = ({
|
|
|
128
128
|
invert: '1',
|
|
129
129
|
scale,
|
|
130
130
|
scaleLogarithmicBase,
|
|
131
|
-
unit: unit
|
|
131
|
+
unit: unit ?? undefined,
|
|
132
132
|
yScalesPerUnit
|
|
133
133
|
})}
|
|
134
134
|
{...commonStackedLinesProps}
|
|
@@ -152,107 +152,107 @@ const Lines = ({
|
|
|
152
152
|
|
|
153
153
|
{displayAreaRegularLines
|
|
154
154
|
? regularLines.map(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
155
|
+
({
|
|
156
|
+
areaColor,
|
|
157
|
+
transparency,
|
|
158
|
+
lineColor,
|
|
159
|
+
filled,
|
|
160
|
+
unit,
|
|
161
|
+
highlight,
|
|
162
|
+
invert,
|
|
163
|
+
metric_id,
|
|
164
|
+
...rest
|
|
165
|
+
}) => {
|
|
166
|
+
const yScale = getYScale({
|
|
167
|
+
invert,
|
|
168
|
+
scale,
|
|
169
|
+
scaleLogarithmicBase,
|
|
160
170
|
unit,
|
|
161
|
-
|
|
171
|
+
yScalesPerUnit
|
|
172
|
+
});
|
|
173
|
+
const relatedTimeSeries = getTimeSeriesForLines({
|
|
162
174
|
invert,
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
filled,
|
|
179
|
-
highlight,
|
|
180
|
-
invert,
|
|
181
|
-
lineColor,
|
|
182
|
-
metric_id,
|
|
183
|
-
transparency,
|
|
184
|
-
unit,
|
|
185
|
-
...rest
|
|
186
|
-
}
|
|
187
|
-
],
|
|
188
|
-
timeSeries
|
|
189
|
-
});
|
|
175
|
+
lines: [
|
|
176
|
+
{
|
|
177
|
+
areaColor,
|
|
178
|
+
filled,
|
|
179
|
+
highlight,
|
|
180
|
+
invert,
|
|
181
|
+
lineColor,
|
|
182
|
+
metric_id,
|
|
183
|
+
transparency,
|
|
184
|
+
unit,
|
|
185
|
+
...rest
|
|
186
|
+
}
|
|
187
|
+
],
|
|
188
|
+
timeSeries
|
|
189
|
+
});
|
|
190
190
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
191
|
+
const style = getStyle({
|
|
192
|
+
style: lineStyle,
|
|
193
|
+
metricId: metric_id
|
|
194
|
+
}) as LineStyle;
|
|
195
195
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
areaColor={areaColor || lineColor}
|
|
201
|
-
lineColor={lineColor}
|
|
202
|
-
metric_id={metric_id}
|
|
203
|
-
timeSeries={relatedTimeSeries}
|
|
204
|
-
transparency={transparency}
|
|
205
|
-
xScale={xScale}
|
|
206
|
-
yScale={yScale}
|
|
207
|
-
maxLeftAxisCharacters={maxLeftAxisCharacters}
|
|
208
|
-
hasSecondUnit={hasSecondUnit}
|
|
209
|
-
/>
|
|
210
|
-
)}
|
|
211
|
-
{style?.showPoints &&
|
|
212
|
-
getDates(relatedTimeSeries).map((timeTick) => (
|
|
213
|
-
<Point
|
|
214
|
-
key={timeTick.toString()}
|
|
215
|
-
lineColor={lineColor}
|
|
216
|
-
metric_id={metric_id}
|
|
217
|
-
radius={getPointRadius(style?.lineWidth)}
|
|
218
|
-
timeSeries={relatedTimeSeries}
|
|
219
|
-
timeTick={timeTick}
|
|
220
|
-
xScale={xScale}
|
|
221
|
-
yPoint={getYAnchorPoint({
|
|
222
|
-
metric_id,
|
|
223
|
-
timeSeries: relatedTimeSeries,
|
|
224
|
-
timeTick,
|
|
225
|
-
yScale
|
|
226
|
-
})}
|
|
227
|
-
yScale={yScale}
|
|
228
|
-
/>
|
|
229
|
-
))}
|
|
230
|
-
<RegularLine
|
|
196
|
+
return (
|
|
197
|
+
<g key={metric_id}>
|
|
198
|
+
{displayGuidingLines && (
|
|
199
|
+
<RegularAnchorPoint
|
|
231
200
|
areaColor={areaColor || lineColor}
|
|
232
|
-
curve={style?.curve || 'linear'}
|
|
233
|
-
dashLength={style?.dashLength}
|
|
234
|
-
dashOffset={style?.dashOffset}
|
|
235
|
-
dotOffset={style?.dotOffset}
|
|
236
|
-
filled={isNil(style?.showArea) ? filled : style.showArea}
|
|
237
|
-
graphHeight={height}
|
|
238
|
-
highlight={highlight}
|
|
239
201
|
lineColor={lineColor}
|
|
240
|
-
lineWidth={style?.lineWidth || 2}
|
|
241
202
|
metric_id={metric_id}
|
|
242
203
|
timeSeries={relatedTimeSeries}
|
|
243
|
-
transparency={
|
|
244
|
-
isNil(style?.areaTransparency)
|
|
245
|
-
? transparency || 80
|
|
246
|
-
: style.areaTransparency
|
|
247
|
-
}
|
|
248
|
-
unit={unit}
|
|
204
|
+
transparency={transparency}
|
|
249
205
|
xScale={xScale}
|
|
250
206
|
yScale={yScale}
|
|
207
|
+
maxLeftAxisCharacters={maxLeftAxisCharacters}
|
|
208
|
+
hasSecondUnit={hasSecondUnit}
|
|
251
209
|
/>
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
210
|
+
)}
|
|
211
|
+
{style?.showPoints &&
|
|
212
|
+
getDates(relatedTimeSeries).map((timeTick) => (
|
|
213
|
+
<Point
|
|
214
|
+
key={timeTick.toString()}
|
|
215
|
+
lineColor={lineColor}
|
|
216
|
+
metric_id={metric_id}
|
|
217
|
+
radius={getPointRadius(style?.lineWidth)}
|
|
218
|
+
timeSeries={relatedTimeSeries}
|
|
219
|
+
timeTick={timeTick}
|
|
220
|
+
xScale={xScale}
|
|
221
|
+
yPoint={getYAnchorPoint({
|
|
222
|
+
metric_id,
|
|
223
|
+
timeSeries: relatedTimeSeries,
|
|
224
|
+
timeTick,
|
|
225
|
+
yScale
|
|
226
|
+
})}
|
|
227
|
+
yScale={yScale}
|
|
228
|
+
/>
|
|
229
|
+
))}
|
|
230
|
+
<RegularLine
|
|
231
|
+
areaColor={areaColor || lineColor}
|
|
232
|
+
curve={style?.curve || 'linear'}
|
|
233
|
+
dashLength={style?.dashLength}
|
|
234
|
+
dashOffset={style?.dashOffset}
|
|
235
|
+
dotOffset={style?.dotOffset}
|
|
236
|
+
filled={isNil(style?.showArea) ? filled : style.showArea}
|
|
237
|
+
graphHeight={height}
|
|
238
|
+
highlight={highlight}
|
|
239
|
+
lineColor={lineColor}
|
|
240
|
+
lineWidth={style?.lineWidth || 2}
|
|
241
|
+
metric_id={metric_id}
|
|
242
|
+
timeSeries={relatedTimeSeries}
|
|
243
|
+
transparency={
|
|
244
|
+
isNil(style?.areaTransparency)
|
|
245
|
+
? transparency || 80
|
|
246
|
+
: style.areaTransparency
|
|
247
|
+
}
|
|
248
|
+
unit={unit}
|
|
249
|
+
xScale={xScale}
|
|
250
|
+
yScale={yScale}
|
|
251
|
+
/>
|
|
252
|
+
</g>
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
)
|
|
256
256
|
: null}
|
|
257
257
|
</g>
|
|
258
258
|
);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
type MutableRefObject,
|
|
3
|
+
ReactElement,
|
|
3
4
|
useEffect,
|
|
4
5
|
useMemo,
|
|
5
6
|
useRef,
|
|
@@ -117,7 +118,7 @@ const Chart = ({
|
|
|
117
118
|
min,
|
|
118
119
|
max,
|
|
119
120
|
boundariesUnit
|
|
120
|
-
}: Props):
|
|
121
|
+
}: Props): ReactElement => {
|
|
121
122
|
const { classes } = useChartStyles();
|
|
122
123
|
|
|
123
124
|
const { title, timeSeries, baseAxis, lines } = graphData;
|
|
@@ -202,7 +203,8 @@ const Chart = ({
|
|
|
202
203
|
valueGraphHeight: graphHeight - margin.bottom,
|
|
203
204
|
min,
|
|
204
205
|
max,
|
|
205
|
-
boundariesUnit
|
|
206
|
+
boundariesUnit,
|
|
207
|
+
isFilled: lineStyle?.showArea
|
|
206
208
|
}),
|
|
207
209
|
[
|
|
208
210
|
linesGraph,
|
|
@@ -81,17 +81,17 @@ const MainLegend = ({
|
|
|
81
81
|
|
|
82
82
|
const contextMenuClick =
|
|
83
83
|
(metricId: number) =>
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
84
|
+
(event: MouseEvent): void => {
|
|
85
|
+
if (!secondaryClick) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
event.preventDefault();
|
|
89
|
+
secondaryClick({
|
|
90
|
+
element: event.target,
|
|
91
|
+
metricId,
|
|
92
|
+
position: [event.pageX, event.pageY]
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
95
|
|
|
96
96
|
const selectMetric = ({
|
|
97
97
|
event,
|
|
@@ -120,16 +120,17 @@ const MainLegend = ({
|
|
|
120
120
|
|
|
121
121
|
return (
|
|
122
122
|
<div
|
|
123
|
-
className={`overflow-x-hidden overflow-y-auto ${!equals(placement, 'bottom') ? 'h-full mt-[15px]' : 'ml-[50px] mr-[40px]'}
|
|
123
|
+
className={`overflow-x-hidden overflow-y-auto ${!equals(placement, 'bottom') ? 'h-full mt-[15px]' : 'ml-[50px] mr-[40px]'}`}
|
|
124
124
|
data-display-side={!equals(placement, 'bottom')}
|
|
125
125
|
>
|
|
126
126
|
<ul
|
|
127
|
-
className={`list-none flex gap-3 w-full ${!isListMode && equals(placement, 'bottom') && 'flex-wrap'} ${isListMode || !equals(placement, 'bottom') ? 'flex-col h-full w-fit' : ''} ${equals(placement, 'bottom') ? 'max-h-17' : 'max-h-
|
|
127
|
+
className={`list-none flex gap-3 w-full overflow-y-auto ${!isListMode && equals(placement, 'bottom') && 'flex-wrap'} ${isListMode || !equals(placement, 'bottom') ? 'flex-col h-full w-fit' : ''} ${equals(placement, 'bottom') ? 'max-h-17' : 'max-h-fit'} ${!equals(placement, 'bottom') ? 'overflow-x-hidden' : ''}`}
|
|
128
128
|
style={{
|
|
129
129
|
height: equals(placement, 'bottom') ? 'auto' : `${graphHeight}px`
|
|
130
130
|
}}
|
|
131
131
|
data-as-list={isListMode || !equals(placement, 'bottom')}
|
|
132
132
|
data-mode={itemMode}
|
|
133
|
+
data-legend
|
|
133
134
|
>
|
|
134
135
|
{displayedLines.map((line) => {
|
|
135
136
|
const { color, display, metric_id, unit } = line;
|
|
@@ -155,7 +156,7 @@ const MainLegend = ({
|
|
|
155
156
|
|
|
156
157
|
return (
|
|
157
158
|
<li
|
|
158
|
-
className={`${!display ? 'text-text-disabled' : 'text-text-primary'} flex gap-1 ${toggable && 'cursor-pointer'}`}
|
|
159
|
+
className={`${!display ? 'text-text-disabled' : 'text-text-primary'} flex gap-1 ${toggable && 'cursor-pointer'} ${!equals(placement, 'bottom') ? 'w-fit' : ''}`}
|
|
159
160
|
key={metric_id}
|
|
160
161
|
onClick={(event): void => selectMetric({ event, metric_id })}
|
|
161
162
|
onKeyUp={(event) =>
|
|
@@ -33,7 +33,8 @@ import {
|
|
|
33
33
|
reject,
|
|
34
34
|
sortBy,
|
|
35
35
|
split,
|
|
36
|
-
uniq
|
|
36
|
+
uniq,
|
|
37
|
+
isNotNil
|
|
37
38
|
} from 'ramda';
|
|
38
39
|
|
|
39
40
|
import { margin } from '../../Chart/common';
|
|
@@ -160,8 +161,8 @@ const getMetrics = (timeValue: TimeValue): Array<string> =>
|
|
|
160
161
|
|
|
161
162
|
const getValueForMetric =
|
|
162
163
|
(timeValue: TimeValue) =>
|
|
163
|
-
|
|
164
|
-
|
|
164
|
+
(metric_id: number): number =>
|
|
165
|
+
prop(metric_id, timeValue) as number;
|
|
165
166
|
|
|
166
167
|
const getUnits = (lines: Array<Line>): Array<string> =>
|
|
167
168
|
pipe(map(prop('unit')), uniq)(lines);
|
|
@@ -308,8 +309,8 @@ const getTimeSeriesForLines = ({
|
|
|
308
309
|
...acc,
|
|
309
310
|
[metric_id]:
|
|
310
311
|
invert &&
|
|
311
|
-
|
|
312
|
-
|
|
312
|
+
metricsValue[metric_id] &&
|
|
313
|
+
gt(metricsValue[metric_id], 0)
|
|
313
314
|
? negate(metricsValue[metric_id])
|
|
314
315
|
: metricsValue[metric_id]
|
|
315
316
|
};
|
|
@@ -342,10 +343,10 @@ const getYScale = ({
|
|
|
342
343
|
|
|
343
344
|
return invert
|
|
344
345
|
? getScaleType(scale)({
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
346
|
+
base: scaleLogarithmicBase,
|
|
347
|
+
domain: yScale.domain().reverse(),
|
|
348
|
+
range: yScale.range().reverse()
|
|
349
|
+
})
|
|
349
350
|
: yScale;
|
|
350
351
|
};
|
|
351
352
|
|
|
@@ -382,6 +383,7 @@ const getScale = ({
|
|
|
382
383
|
invert,
|
|
383
384
|
hasDisplayAsBar,
|
|
384
385
|
hasLineFilled,
|
|
386
|
+
hasStackedLines,
|
|
385
387
|
min,
|
|
386
388
|
max
|
|
387
389
|
}): ScaleLinear<number, number> => {
|
|
@@ -389,30 +391,42 @@ const getScale = ({
|
|
|
389
391
|
const sanitizedValuesForMinimum = min
|
|
390
392
|
? [min]
|
|
391
393
|
: getSanitizedValues([
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
const minValue = Math.min(...sanitizedValuesForMinimum);
|
|
401
|
-
const minValueWithMargin =
|
|
402
|
-
(hasDisplayAsBar || hasLineFilled) && minValue > 0 && !min
|
|
403
|
-
? 0
|
|
404
|
-
: minValue - Math.abs(minValue) * 0.05;
|
|
394
|
+
invert && graphValues.every(lt(0))
|
|
395
|
+
? negate(getMax(graphValues))
|
|
396
|
+
: getMin(graphValues),
|
|
397
|
+
!isEmpty(stackedValues) &&
|
|
398
|
+
!equals(stackedValues, [0]) &&
|
|
399
|
+
getMin(stackedValues),
|
|
400
|
+
Math.min(...thresholds)
|
|
401
|
+
]);
|
|
402
|
+
const minValue = Math.min(...sanitizedValuesForMinimum.filter(isNotNil));
|
|
405
403
|
|
|
406
404
|
const sanitizedValuesForMaximum = max
|
|
407
405
|
? [max]
|
|
408
406
|
: getSanitizedValues([
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
const maxValue = Math.max(...sanitizedValuesForMaximum);
|
|
415
|
-
|
|
407
|
+
getMax(graphValues),
|
|
408
|
+
getMax(stackedValues),
|
|
409
|
+
hasOnlyZeroesHasValue(graphValues) ? 1 : null,
|
|
410
|
+
Math.max(...thresholds)
|
|
411
|
+
]);
|
|
412
|
+
const maxValue = Math.max(...sanitizedValuesForMaximum.filter(isNotNil));
|
|
413
|
+
|
|
414
|
+
const minValueWithMargin =
|
|
415
|
+
(hasDisplayAsBar && minValue > 0) ||
|
|
416
|
+
(hasLineFilled &&
|
|
417
|
+
Math.max(maxValue, minValue) > minValue &&
|
|
418
|
+
minValue > 0) ||
|
|
419
|
+
(hasStackedLines && minValue > maxValue)
|
|
420
|
+
? 0
|
|
421
|
+
: minValue - Math.abs(minValue) * 0.05;
|
|
422
|
+
const maxValueWithMargin =
|
|
423
|
+
(hasDisplayAsBar && maxValue < 0) ||
|
|
424
|
+
(hasLineFilled &&
|
|
425
|
+
Math.min(maxValue, minValue) < maxValue &&
|
|
426
|
+
maxValue < 0) ||
|
|
427
|
+
(hasStackedLines && minValue > maxValue)
|
|
428
|
+
? 0
|
|
429
|
+
: maxValue + Math.abs(maxValue) * 0.05;
|
|
416
430
|
|
|
417
431
|
const scaleType = getScaleType(scale);
|
|
418
432
|
|
|
@@ -479,7 +493,8 @@ const getYScaleUnit = ({
|
|
|
479
493
|
min,
|
|
480
494
|
max,
|
|
481
495
|
isBarChart,
|
|
482
|
-
boundariesUnit
|
|
496
|
+
boundariesUnit,
|
|
497
|
+
isFilled
|
|
483
498
|
}: AxeScale & {
|
|
484
499
|
invert?: boolean | string | null;
|
|
485
500
|
unit: string;
|
|
@@ -487,6 +502,7 @@ const getYScaleUnit = ({
|
|
|
487
502
|
min?: number;
|
|
488
503
|
boundariesUnit?: string;
|
|
489
504
|
isBarChart?: boolean;
|
|
505
|
+
isFilled?: boolean;
|
|
490
506
|
}): ScaleLinear<number, number> => {
|
|
491
507
|
const [firstUnit] = getUnits(dataLines);
|
|
492
508
|
const shouldApplyThresholds =
|
|
@@ -505,12 +521,12 @@ const getYScaleUnit = ({
|
|
|
505
521
|
|
|
506
522
|
const stackedValues = hasStackedLines
|
|
507
523
|
? getStackedMetricValues({
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
: [
|
|
524
|
+
lines: getSortedStackedLines(dataLines).filter(
|
|
525
|
+
({ unit: stackedUnit }) => equals(unit, stackedUnit)
|
|
526
|
+
),
|
|
527
|
+
timeSeries: dataTimeSeries
|
|
528
|
+
})
|
|
529
|
+
: [];
|
|
514
530
|
|
|
515
531
|
return getScale({
|
|
516
532
|
graphValues,
|
|
@@ -520,8 +536,14 @@ const getYScaleUnit = ({
|
|
|
520
536
|
({ displayAs, unit: lineUnit }) =>
|
|
521
537
|
equals(unit, lineUnit) && equals(displayAs, 'bar')
|
|
522
538
|
),
|
|
523
|
-
hasLineFilled:
|
|
524
|
-
|
|
539
|
+
hasLineFilled: isNil(isFilled)
|
|
540
|
+
? dataLines.some(
|
|
541
|
+
({ unit: lineUnit, filled }) => equals(unit, lineUnit) && filled
|
|
542
|
+
)
|
|
543
|
+
: isFilled,
|
|
544
|
+
hasStackedLines: dataLines.some(
|
|
545
|
+
({ unit: lineUnit, stackKey, stackOrder }) =>
|
|
546
|
+
equals(unit, lineUnit) && (stackKey || stackOrder)
|
|
525
547
|
),
|
|
526
548
|
height: valueGraphHeight,
|
|
527
549
|
invert,
|
|
@@ -561,12 +583,14 @@ const getYScalePerUnit = ({
|
|
|
561
583
|
isBarChart,
|
|
562
584
|
min,
|
|
563
585
|
max,
|
|
564
|
-
boundariesUnit
|
|
586
|
+
boundariesUnit,
|
|
587
|
+
isFilled
|
|
565
588
|
}: AxeScale & {
|
|
566
589
|
min?: number;
|
|
567
590
|
max?: number;
|
|
568
591
|
isBarChart?: boolean;
|
|
569
592
|
boundariesUnit?: string;
|
|
593
|
+
isFilled?: boolean;
|
|
570
594
|
}): Record<string, ScaleLinear<number, number>> => {
|
|
571
595
|
const units = getUnits(dataLines);
|
|
572
596
|
|
|
@@ -590,7 +614,8 @@ const getYScalePerUnit = ({
|
|
|
590
614
|
min,
|
|
591
615
|
max,
|
|
592
616
|
isBarChart,
|
|
593
|
-
boundariesUnit
|
|
617
|
+
boundariesUnit,
|
|
618
|
+
isFilled
|
|
594
619
|
})
|
|
595
620
|
};
|
|
596
621
|
}, {});
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
"metric_id": 1,
|
|
7
7
|
"legend": "count",
|
|
8
8
|
"displayAs": "bar",
|
|
9
|
+
"unit": "",
|
|
9
10
|
"ds_data": {
|
|
10
11
|
"ds_stack": true,
|
|
11
12
|
"ds_transparency": 0,
|
|
@@ -30,6 +31,7 @@
|
|
|
30
31
|
"metric_id": 2,
|
|
31
32
|
"legend": "count",
|
|
32
33
|
"displayAs": "bar",
|
|
34
|
+
"unit": "",
|
|
33
35
|
"ds_data": {
|
|
34
36
|
"ds_stack": true,
|
|
35
37
|
"ds_transparency": 0,
|