@centreon/ui 25.11.10 → 26.3.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 +57 -85
- 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.cypress.spec.tsx +9 -9
- package/src/Graph/Chart/Chart.tsx +4 -2
- package/src/Graph/common/BaseChart/useComputeYAxisMaxCharacters.ts +3 -2
- package/src/Graph/common/timeSeries/index.ts +65 -40
- package/src/Graph/mockedData/dataWithMissingPoint.json +2 -0
- package/src/components/CopyCommand/CopyCommand.cypress.spec.tsx +15 -6
package/package.json
CHANGED
|
@@ -76,17 +76,72 @@ const checkWidth = (orientation): void => {
|
|
|
76
76
|
cy.get('g[class*="visx-rows"] > line')
|
|
77
77
|
.eq(0)
|
|
78
78
|
.should('have.attr', 'x2')
|
|
79
|
-
.and('equal', '
|
|
79
|
+
.and('equal', '1133');
|
|
80
80
|
|
|
81
81
|
return;
|
|
82
82
|
}
|
|
83
83
|
cy.get('g[class*="visx-rows"] > line')
|
|
84
84
|
.eq(0)
|
|
85
85
|
.should('have.attr', 'x2')
|
|
86
|
-
.and('equal', '
|
|
86
|
+
.and('equal', '1168');
|
|
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
|
);
|
|
@@ -157,7 +157,7 @@ const checkGraphWidth = (): void => {
|
|
|
157
157
|
.and('equal', '392');
|
|
158
158
|
|
|
159
159
|
cy.findByTestId('graph-interaction-zone').then((graph) => {
|
|
160
|
-
expect(Number(graph[0].attributes.width.value)).to.be.greaterThan(
|
|
160
|
+
expect(Number(graph[0].attributes.width.value)).to.be.greaterThan(1167);
|
|
161
161
|
});
|
|
162
162
|
};
|
|
163
163
|
|
|
@@ -180,8 +180,8 @@ describe('Line chart', () => {
|
|
|
180
180
|
|
|
181
181
|
cy.contains('06/18/2023').should('be.visible');
|
|
182
182
|
|
|
183
|
-
cy.contains('0.
|
|
184
|
-
cy.contains('
|
|
183
|
+
cy.contains('0.44 s').should('be.visible');
|
|
184
|
+
cy.contains('75.19%').should('be.visible');
|
|
185
185
|
|
|
186
186
|
cy.makeSnapshot();
|
|
187
187
|
});
|
|
@@ -235,7 +235,7 @@ describe('Line chart', () => {
|
|
|
235
235
|
|
|
236
236
|
cy.contains('Min: 70.31').should('be.visible');
|
|
237
237
|
|
|
238
|
-
cy.findByTestId('graph-interaction-zone').realMouseMove(
|
|
238
|
+
cy.findByTestId('graph-interaction-zone').realMouseMove(1100, 100);
|
|
239
239
|
|
|
240
240
|
cy.get('[data-metric="querytime"]').should('be.visible');
|
|
241
241
|
cy.get('[data-metric="hitratio"]').should('be.visible');
|
|
@@ -533,8 +533,8 @@ describe('Line chart', () => {
|
|
|
533
533
|
|
|
534
534
|
checkGraphWidth();
|
|
535
535
|
cy.contains(':00 AM').should('be.visible');
|
|
536
|
-
cy.get('circle[cx="
|
|
537
|
-
cy.get('circle[cy="
|
|
536
|
+
cy.get('circle[cx="245.83333333333334"]').should('be.visible');
|
|
537
|
+
cy.get('circle[cy="256.7205013298516"]').should('be.visible');
|
|
538
538
|
|
|
539
539
|
cy.makeSnapshot();
|
|
540
540
|
});
|
|
@@ -592,7 +592,7 @@ describe('Line chart', () => {
|
|
|
592
592
|
cy.get('path.visx-area-closed')
|
|
593
593
|
.should('have.attr', 'stroke-dasharray')
|
|
594
594
|
.and('equals', '5 4');
|
|
595
|
-
cy.get('circle[cx="
|
|
595
|
+
cy.get('circle[cx="32.77777777777778"]').should('be.visible');
|
|
596
596
|
|
|
597
597
|
cy.makeSnapshot();
|
|
598
598
|
});
|
|
@@ -748,10 +748,10 @@ describe('Lines and bars', () => {
|
|
|
748
748
|
checkGraphWidth();
|
|
749
749
|
|
|
750
750
|
cy.get(
|
|
751
|
-
'path[d="M7.
|
|
751
|
+
'path[d="M7.435261707988982,289.92398616599974 h55.91735537190083 h1v1 v100.07601383400026 a1,1 0 0 1 -1,1 h-55.91735537190083 a1,1 0 0 1 -1,-1 v-100.07601383400026 v-1h1z"]'
|
|
752
752
|
).should('be.visible');
|
|
753
753
|
cy.get(
|
|
754
|
-
'path[d="
|
|
754
|
+
'path[d="M23.81046831955923,235.36727985204413 h23.166942148760334 a17.37520661157025,17.37520661157025 0 0 1 17.37520661157025,17.37520661157025 v19.806293090815103 v17.37520661157025h-17.37520661157025 h-23.166942148760334 h-17.37520661157025v-17.37520661157025 v-19.806293090815103 a17.37520661157025,17.37520661157025 0 0 1 17.37520661157025,-17.37520661157025z"]'
|
|
755
755
|
).should('be.visible');
|
|
756
756
|
|
|
757
757
|
cy.makeSnapshot();
|
|
@@ -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,
|
|
@@ -69,11 +69,12 @@ export const useComputeYAxisMaxCharacters = ({
|
|
|
69
69
|
]
|
|
70
70
|
);
|
|
71
71
|
|
|
72
|
+
// Always add a character space in case the algorithm does not compute the displayed value in axis.
|
|
72
73
|
const maxLeftAxisCharacters = useMemo(
|
|
73
74
|
() =>
|
|
74
75
|
isEmpty(maxLeftValue)
|
|
75
76
|
? 2
|
|
76
|
-
: Math.max(...maxLeftValue.map((value) => value.length), 2),
|
|
77
|
+
: Math.max(...maxLeftValue.map((value) => value.length), 2) + 1,
|
|
77
78
|
[maxLeftValue]
|
|
78
79
|
);
|
|
79
80
|
|
|
@@ -81,7 +82,7 @@ export const useComputeYAxisMaxCharacters = ({
|
|
|
81
82
|
() =>
|
|
82
83
|
isEmpty(maxRightValue)
|
|
83
84
|
? 5
|
|
84
|
-
: Math.max(...maxRightValue.map((value) => value.length), 5),
|
|
85
|
+
: Math.max(...maxRightValue.map((value) => value.length), 5) + 1,
|
|
85
86
|
[maxRightValue]
|
|
86
87
|
);
|
|
87
88
|
|
|
@@ -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,
|
|
@@ -131,6 +131,21 @@ echo 'Hello ' . htmlspecialchars($_POST["name"]) . '!';
|
|
|
131
131
|
});
|
|
132
132
|
|
|
133
133
|
it('copies the command to the clipboard when the button is clicked', () => {
|
|
134
|
+
cy.wrap(
|
|
135
|
+
Cypress.automation('remote:debugger:protocol', {
|
|
136
|
+
command: 'Browser.grantPermissions',
|
|
137
|
+
params: {
|
|
138
|
+
permissions: ['clipboardReadWrite', 'clipboardSanitizedWrite'],
|
|
139
|
+
origin: window.location.origin
|
|
140
|
+
}
|
|
141
|
+
})
|
|
142
|
+
);
|
|
143
|
+
cy.window()
|
|
144
|
+
.its('navigator.clipboard')
|
|
145
|
+
.then((clipboard) => {
|
|
146
|
+
cy.spy(clipboard, 'writeText').as('writeText');
|
|
147
|
+
});
|
|
148
|
+
|
|
134
149
|
initialize({
|
|
135
150
|
text: `# a simple command
|
|
136
151
|
echo "hello" | grep "hel"`,
|
|
@@ -138,12 +153,6 @@ echo "hello" | grep "hel"`,
|
|
|
138
153
|
commandToCopy: 'echo "hello" | grep "hel"'
|
|
139
154
|
});
|
|
140
155
|
|
|
141
|
-
cy.window()
|
|
142
|
-
.its('navigator.clipboard')
|
|
143
|
-
.then((clipboard) => {
|
|
144
|
-
cy.spy(clipboard, 'writeText').as('writeText');
|
|
145
|
-
});
|
|
146
|
-
|
|
147
156
|
cy.findByTestId('Copy command').click();
|
|
148
157
|
|
|
149
158
|
cy.get('@writeText').should(
|