@centreon/ui 25.11.3 → 25.11.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 +2 -1
- package/src/Graph/BarChart/BarChart.cypress.spec.tsx +21 -0
- package/src/Graph/BarChart/BarChart.stories.tsx +18 -0
- package/src/Graph/BarChart/BarChart.tsx +10 -1
- package/src/Graph/BarChart/ResponsiveBarChart.tsx +2 -1
- package/src/Graph/BarChart/useBarStack.ts +9 -9
- package/src/Graph/Chart/Chart.cypress.spec.tsx +37 -17
- package/src/Graph/Chart/Chart.stories.tsx +21 -0
- package/src/Graph/Chart/Chart.tsx +2 -0
- package/src/Graph/Chart/Legend/Legend.styles.ts +1 -30
- package/src/Graph/Chart/Legend/LegendContent.tsx +1 -1
- package/src/Graph/Chart/Legend/LegendHeader.tsx +7 -23
- package/src/Graph/Chart/Legend/index.tsx +76 -63
- package/src/Graph/Chart/index.tsx +6 -1
- package/src/Graph/Chart/models.ts +5 -0
- package/src/Graph/common/BaseChart/BaseChart.tsx +7 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@centreon/ui",
|
|
3
|
-
"version": "25.11.
|
|
3
|
+
"version": "25.11.4",
|
|
4
4
|
"description": "Centreon UI Components",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"update:deps": "pnpx npm-check-updates -i --format group",
|
|
@@ -142,6 +142,7 @@
|
|
|
142
142
|
"@visx/visx": "3.12.0",
|
|
143
143
|
"@visx/zoom": "^3.12.0",
|
|
144
144
|
"anylogger": "^1.0.11",
|
|
145
|
+
"chromatic": "^13.3.3",
|
|
145
146
|
"d3-array": "3.2.4",
|
|
146
147
|
"dayjs": "^1.11.13",
|
|
147
148
|
"highlight.js": "^11.11.1",
|
|
@@ -11,6 +11,7 @@ import dataPingServiceMixedStacked from '../mockedData/pingServiceMixedStacked.j
|
|
|
11
11
|
import dataPingServiceStacked from '../mockedData/pingServiceStacked.json';
|
|
12
12
|
import dataPingServiceLinesStackKeys from '../mockedData/pingServiceWithStackedKeys.json';
|
|
13
13
|
|
|
14
|
+
import { labelAvg, labelMax, labelMin } from '../Chart/translatedLabels';
|
|
14
15
|
import BarChart, { BarChartProps } from './BarChart';
|
|
15
16
|
|
|
16
17
|
const defaultStart = new Date(
|
|
@@ -331,4 +332,24 @@ describe('Bar chart', () => {
|
|
|
331
332
|
|
|
332
333
|
cy.makeSnapshot();
|
|
333
334
|
});
|
|
335
|
+
|
|
336
|
+
it('does not displays corresponding calculations when props are set', () => {
|
|
337
|
+
initialize({
|
|
338
|
+
data: dataLastWeek,
|
|
339
|
+
orientation: 'horizontal',
|
|
340
|
+
legend: {
|
|
341
|
+
placement: 'bottom',
|
|
342
|
+
mode: 'grid',
|
|
343
|
+
showCalculations: {
|
|
344
|
+
min: true,
|
|
345
|
+
max: false,
|
|
346
|
+
avg: false
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
cy.contains(labelMin).should('be.visible');
|
|
352
|
+
cy.contains(labelMax).should('not.exist');
|
|
353
|
+
cy.contains(labelAvg).should('not.exist');
|
|
354
|
+
});
|
|
334
355
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Meta, StoryObj } from '@storybook/react';
|
|
2
2
|
import dayjs from 'dayjs';
|
|
3
|
+
import '../../ThemeProvider/tailwindcss.css';
|
|
3
4
|
|
|
4
5
|
import { LineChartData } from '../common/models';
|
|
5
6
|
import dataPingService from '../mockedData/pingService.json';
|
|
@@ -45,6 +46,11 @@ export const withCenteredZero: Story = {
|
|
|
45
46
|
...defaultArgs,
|
|
46
47
|
axis: {
|
|
47
48
|
isCenteredZero: true
|
|
49
|
+
},
|
|
50
|
+
legend: {
|
|
51
|
+
showCalculations: { avg: true, max: false, min: false },
|
|
52
|
+
mode: 'grid',
|
|
53
|
+
placement: 'bottom'
|
|
48
54
|
}
|
|
49
55
|
},
|
|
50
56
|
render: Template
|
|
@@ -310,3 +316,15 @@ export const stackKey: Story = {
|
|
|
310
316
|
},
|
|
311
317
|
render: Template
|
|
312
318
|
};
|
|
319
|
+
|
|
320
|
+
export const withControlledCalculations: Story = {
|
|
321
|
+
args: {
|
|
322
|
+
...defaultArgs,
|
|
323
|
+
legend: {
|
|
324
|
+
showCalculations: { avg: true, max: false, min: false },
|
|
325
|
+
mode: 'grid',
|
|
326
|
+
placement: 'bottom'
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
render: Template
|
|
330
|
+
};
|
|
@@ -61,7 +61,16 @@ const BarChart = ({
|
|
|
61
61
|
height = 500,
|
|
62
62
|
tooltip,
|
|
63
63
|
axis,
|
|
64
|
-
legend
|
|
64
|
+
legend = {
|
|
65
|
+
display: true,
|
|
66
|
+
mode: 'grid',
|
|
67
|
+
placement: 'bottom',
|
|
68
|
+
showCalculations: {
|
|
69
|
+
min: true,
|
|
70
|
+
max: true,
|
|
71
|
+
avg: true
|
|
72
|
+
}
|
|
73
|
+
},
|
|
65
74
|
loading,
|
|
66
75
|
limitLegend,
|
|
67
76
|
thresholdUnit,
|
|
@@ -235,7 +235,8 @@ const ResponsiveBarChart = ({
|
|
|
235
235
|
mode: legend?.mode,
|
|
236
236
|
placement: legend?.placement,
|
|
237
237
|
renderExtraComponent: legend?.renderExtraComponent,
|
|
238
|
-
secondaryClick: legend?.secondaryClick
|
|
238
|
+
secondaryClick: legend?.secondaryClick,
|
|
239
|
+
showCalculations: legend?.showCalculations
|
|
239
240
|
}}
|
|
240
241
|
legendRef={legendRef}
|
|
241
242
|
limitLegend={limitLegend}
|
|
@@ -72,15 +72,15 @@ export const useBarStack = ({
|
|
|
72
72
|
|
|
73
73
|
const commonBarStackProps = isHorizontal
|
|
74
74
|
? {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
x: (d) => d.timeTick,
|
|
76
|
+
xScale,
|
|
77
|
+
yScale
|
|
78
|
+
}
|
|
79
79
|
: {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
xScale: yScale,
|
|
81
|
+
y: (d) => d.timeTick,
|
|
82
|
+
yScale: xScale
|
|
83
|
+
};
|
|
84
84
|
|
|
85
85
|
const hoverBar = useCallback(
|
|
86
86
|
({ highlightedMetric, barIndex }: HoverBarProps) =>
|
|
@@ -96,7 +96,7 @@ export const useBarStack = ({
|
|
|
96
96
|
index: barIndex
|
|
97
97
|
});
|
|
98
98
|
},
|
|
99
|
-
[]
|
|
99
|
+
[lines, timeSeries]
|
|
100
100
|
);
|
|
101
101
|
|
|
102
102
|
const exitBar = useCallback((): void => {
|
|
@@ -18,6 +18,7 @@ import { args as argumentsData } from './helpers/doc';
|
|
|
18
18
|
import { LineChartProps } from './models';
|
|
19
19
|
|
|
20
20
|
import WrapperChart from '.';
|
|
21
|
+
import { labelAvg, labelMin } from './translatedLabels';
|
|
21
22
|
|
|
22
23
|
interface Props
|
|
23
24
|
extends Pick<
|
|
@@ -153,7 +154,7 @@ const initializeCustomUnits = ({
|
|
|
153
154
|
const checkGraphWidth = (): void => {
|
|
154
155
|
cy.findByTestId('graph-interaction-zone')
|
|
155
156
|
.should('have.attr', 'height')
|
|
156
|
-
.and('equal', '
|
|
157
|
+
.and('equal', '389');
|
|
157
158
|
|
|
158
159
|
cy.findByTestId('graph-interaction-zone').then((graph) => {
|
|
159
160
|
expect(Number(graph[0].attributes.width.value)).to.be.greaterThan(1170);
|
|
@@ -295,23 +296,23 @@ describe('Line chart', () => {
|
|
|
295
296
|
.should('have.attr', 'width')
|
|
296
297
|
.and('equal', '1200');
|
|
297
298
|
|
|
298
|
-
cy.
|
|
299
|
-
.
|
|
299
|
+
cy.get('[data-icon="true"]')
|
|
300
|
+
.eq(0)
|
|
300
301
|
.should('have.css', 'background-color', 'rgb(41, 175, 238)');
|
|
301
|
-
cy.
|
|
302
|
-
.
|
|
302
|
+
cy.get('[data-icon="true"]')
|
|
303
|
+
.eq(1)
|
|
303
304
|
.should('have.css', 'background-color', 'rgb(83, 191, 241)');
|
|
304
|
-
cy.
|
|
305
|
-
.
|
|
305
|
+
cy.get('[data-icon="true"]')
|
|
306
|
+
.eq(2)
|
|
306
307
|
.should('have.css', 'background-color', 'rgb(8, 34, 47)');
|
|
307
|
-
cy.
|
|
308
|
-
.
|
|
308
|
+
cy.get('[data-icon="true"]')
|
|
309
|
+
.eq(3)
|
|
309
310
|
.should('have.css', 'background-color', 'rgb(16, 70, 95)');
|
|
310
|
-
cy.
|
|
311
|
-
.
|
|
311
|
+
cy.get('[data-icon="true"]')
|
|
312
|
+
.eq(4)
|
|
312
313
|
.should('have.css', 'background-color', 'rgb(24, 105, 142)');
|
|
313
|
-
cy.
|
|
314
|
-
.
|
|
314
|
+
cy.get('[data-icon="true"]')
|
|
315
|
+
.eq(5)
|
|
315
316
|
.should('have.css', 'background-color', 'rgb(32, 140, 190)');
|
|
316
317
|
|
|
317
318
|
cy.get('[data-metric="1"]').should(
|
|
@@ -451,7 +452,7 @@ describe('Line chart', () => {
|
|
|
451
452
|
|
|
452
453
|
cy.contains(':00 AM').should('be.visible');
|
|
453
454
|
|
|
454
|
-
cy.get('text[transform="rotate(-35, -2,
|
|
455
|
+
cy.get('text[transform="rotate(-35, -2, 205.66612100897808)"]').should(
|
|
455
456
|
'be.visible'
|
|
456
457
|
);
|
|
457
458
|
|
|
@@ -533,7 +534,7 @@ describe('Line chart', () => {
|
|
|
533
534
|
checkGraphWidth();
|
|
534
535
|
cy.contains(':00 AM').should('be.visible');
|
|
535
536
|
cy.get('circle[cx="248.33333333333334"]').should('be.visible');
|
|
536
|
-
cy.get('circle[cy="
|
|
537
|
+
cy.get('circle[cy="257.3178022124914"]').should('be.visible');
|
|
537
538
|
|
|
538
539
|
cy.makeSnapshot();
|
|
539
540
|
});
|
|
@@ -747,10 +748,10 @@ describe('Lines and bars', () => {
|
|
|
747
748
|
checkGraphWidth();
|
|
748
749
|
|
|
749
750
|
cy.get(
|
|
750
|
-
'path[d="M7.501377410468319,
|
|
751
|
+
'path[d="M7.501377410468319,287.7051801494232 h56.51239669421488 h1v1 v99.2948198505768 a1,1 0 0 1 -1,1 h-56.51239669421488 a1,1 0 0 1 -1,-1 v-99.2948198505768 v-1h1z"]'
|
|
751
752
|
).should('be.visible');
|
|
752
753
|
cy.get(
|
|
753
|
-
'path[d="M24.05509641873278,
|
|
754
|
+
'path[d="M24.05509641873278,233.5659996490948 h23.404958677685954 a17.553719008264462,17.553719008264462 0 0 1 17.553719008264462,17.553719008264462 v19.03174248379947 v17.553719008264462h-17.553719008264462 h-23.404958677685954 h-17.553719008264462v-17.553719008264462 v-19.03174248379947 a17.553719008264462,17.553719008264462 0 0 1 17.553719008264462,-17.553719008264462z"]'
|
|
754
755
|
).should('be.visible');
|
|
755
756
|
|
|
756
757
|
cy.makeSnapshot();
|
|
@@ -833,4 +834,23 @@ describe('Lines and bars', () => {
|
|
|
833
834
|
cy.contains('Packet Loss').rightclick();
|
|
834
835
|
cy.get('@secondaryClick').should('have.been.called');
|
|
835
836
|
});
|
|
837
|
+
|
|
838
|
+
it('does not displays corresponding calculations when props are set', () => {
|
|
839
|
+
initialize({
|
|
840
|
+
data: dataPingServiceLines,
|
|
841
|
+
legend: {
|
|
842
|
+
placement: 'bottom',
|
|
843
|
+
mode: 'grid',
|
|
844
|
+
showCalculations: {
|
|
845
|
+
min: true,
|
|
846
|
+
max: false,
|
|
847
|
+
avg: false
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
cy.contains(labelMin).should('be.visible');
|
|
853
|
+
cy.contains(/^Max$/).should('not.exist');
|
|
854
|
+
cy.contains(labelAvg).should('not.exist');
|
|
855
|
+
});
|
|
836
856
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
2
|
|
|
3
3
|
import { Meta, StoryObj } from '@storybook/react';
|
|
4
|
+
import '../../ThemeProvider/tailwindcss.css';
|
|
4
5
|
|
|
5
6
|
import { Button, Menu } from '@mui/material';
|
|
6
7
|
import ButtonGroup from '@mui/material/ButtonGroup';
|
|
@@ -800,3 +801,23 @@ export const stackedKey: Story = {
|
|
|
800
801
|
data: dataPingServiceLinesStackKeys
|
|
801
802
|
}
|
|
802
803
|
};
|
|
804
|
+
|
|
805
|
+
export const WithControlledCalculations: Story = {
|
|
806
|
+
...Template,
|
|
807
|
+
argTypes,
|
|
808
|
+
args: {
|
|
809
|
+
...argumentsData,
|
|
810
|
+
lineStyle: {
|
|
811
|
+
curve: 'step'
|
|
812
|
+
},
|
|
813
|
+
legend: {
|
|
814
|
+
mode: 'grid',
|
|
815
|
+
placement: 'bottom',
|
|
816
|
+
showCalculations: {
|
|
817
|
+
avg: true,
|
|
818
|
+
max: true,
|
|
819
|
+
min: false
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
};
|
|
@@ -273,11 +273,13 @@ const Chart = ({
|
|
|
273
273
|
header={header}
|
|
274
274
|
height={height}
|
|
275
275
|
legend={{
|
|
276
|
+
...legend,
|
|
276
277
|
displayLegend,
|
|
277
278
|
legendHeight: legend?.height,
|
|
278
279
|
mode: legend?.mode,
|
|
279
280
|
placement: legend?.placement,
|
|
280
281
|
renderExtraComponent: legend?.renderExtraComponent,
|
|
282
|
+
showCalculations: legend?.showCalculations,
|
|
281
283
|
secondaryClick: legend?.secondaryClick
|
|
282
284
|
}}
|
|
283
285
|
legendRef={legendRef}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { makeStyles } from 'tss-react/mui';
|
|
2
2
|
|
|
3
|
-
import { equals, lt } from 'ramda';
|
|
4
|
-
import { margin } from '../common';
|
|
5
3
|
import type { LegendModel } from '../models';
|
|
6
4
|
|
|
7
5
|
interface MakeStylesProps {
|
|
@@ -14,20 +12,8 @@ export const legendWidth = 21;
|
|
|
14
12
|
const legendItemHeight = 5.25;
|
|
15
13
|
const legendItemHeightCompact = 2;
|
|
16
14
|
|
|
17
|
-
const getLegendMaxHeight = ({ placement, height }) => {
|
|
18
|
-
if (!equals(placement, 'bottom')) {
|
|
19
|
-
return height || 0;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (lt(height || 0, 220)) {
|
|
23
|
-
return 40;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return 90;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
15
|
export const useStyles = makeStyles<MakeStylesProps>()(
|
|
30
|
-
(theme, { limitLegendRows
|
|
16
|
+
(theme, { limitLegendRows }) => ({
|
|
31
17
|
highlight: {
|
|
32
18
|
color: theme.typography.body1.color
|
|
33
19
|
},
|
|
@@ -54,21 +40,6 @@ export const useStyles = makeStyles<MakeStylesProps>()(
|
|
|
54
40
|
rowGap: theme.spacing(1),
|
|
55
41
|
width: '100%'
|
|
56
42
|
},
|
|
57
|
-
legend: {
|
|
58
|
-
'&[data-display-side="false"]': {
|
|
59
|
-
marginLeft: margin.left,
|
|
60
|
-
marginRight: margin.right
|
|
61
|
-
},
|
|
62
|
-
'&[data-display-side="true"]': {
|
|
63
|
-
height: '100%',
|
|
64
|
-
marginTop: `${margin.top / 2}px`
|
|
65
|
-
},
|
|
66
|
-
maxHeight: limitLegendRows
|
|
67
|
-
? theme.spacing(legendItemHeight * 2 + 1)
|
|
68
|
-
: getLegendMaxHeight({ placement, height }),
|
|
69
|
-
overflowY: 'auto',
|
|
70
|
-
overflowX: 'hidden'
|
|
71
|
-
},
|
|
72
43
|
minMaxAvgContainer: {
|
|
73
44
|
columnGap: theme.spacing(0.5),
|
|
74
45
|
display: 'grid',
|
|
@@ -15,7 +15,7 @@ const LegendContent = ({ data, label }: Props): JSX.Element => {
|
|
|
15
15
|
const { t } = useTranslation();
|
|
16
16
|
|
|
17
17
|
return (
|
|
18
|
-
<div className=
|
|
18
|
+
<div className="leading-[1.2]" data-testid={label}>
|
|
19
19
|
<Typography className={classes.text} component="span" variant="caption">
|
|
20
20
|
{t(label)}:{' '}
|
|
21
21
|
<Typography
|
|
@@ -8,13 +8,11 @@ import {
|
|
|
8
8
|
import { Tooltip } from '../../../components';
|
|
9
9
|
import { Line } from '../../common/timeSeries/models';
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { ReactElement } from 'react';
|
|
12
12
|
import LegendContent from './LegendContent';
|
|
13
13
|
import { LegendDisplayMode } from './models';
|
|
14
14
|
|
|
15
15
|
interface Props {
|
|
16
|
-
color: string;
|
|
17
|
-
disabled?: boolean;
|
|
18
16
|
isDisplayedOnSide: boolean;
|
|
19
17
|
isListMode: boolean;
|
|
20
18
|
line: Line;
|
|
@@ -25,16 +23,12 @@ interface Props {
|
|
|
25
23
|
|
|
26
24
|
const LegendHeader = ({
|
|
27
25
|
line,
|
|
28
|
-
color,
|
|
29
|
-
disabled,
|
|
30
26
|
value,
|
|
31
27
|
minMaxAvg,
|
|
32
28
|
isListMode,
|
|
33
29
|
isDisplayedOnSide,
|
|
34
30
|
unit
|
|
35
|
-
}: Props):
|
|
36
|
-
const { classes, cx } = useLegendHeaderStyles({ color });
|
|
37
|
-
|
|
31
|
+
}: Props): ReactElement => {
|
|
38
32
|
const { name, legend } = line;
|
|
39
33
|
|
|
40
34
|
const metricName = formatMetricName({ legend, name });
|
|
@@ -42,16 +36,14 @@ const LegendHeader = ({
|
|
|
42
36
|
const legendName = legend || name;
|
|
43
37
|
|
|
44
38
|
return (
|
|
45
|
-
<div
|
|
46
|
-
className={cx(!isListMode ? classes.container : classes.containerList)}
|
|
47
|
-
>
|
|
39
|
+
<div className={isListMode ? 'w-fit' : 'w-full'}>
|
|
48
40
|
<Tooltip
|
|
49
41
|
followCursor={false}
|
|
50
42
|
label={
|
|
51
43
|
minMaxAvg ? (
|
|
52
44
|
<div>
|
|
53
45
|
<Typography>{legendName}</Typography>
|
|
54
|
-
<div className=
|
|
46
|
+
<div className="flex flex-wrap gap-1 whitespace-nowrap">
|
|
55
47
|
{minMaxAvg.map(({ label, value: subValue }) => (
|
|
56
48
|
<LegendContent
|
|
57
49
|
data={formatMetricValue({
|
|
@@ -70,18 +62,10 @@ const LegendHeader = ({
|
|
|
70
62
|
}
|
|
71
63
|
placement={isListMode ? 'right' : 'top'}
|
|
72
64
|
>
|
|
73
|
-
<div className=
|
|
74
|
-
<div
|
|
75
|
-
data-icon
|
|
76
|
-
className={cx(classes.icon, { [classes.disabled]: disabled })}
|
|
77
|
-
/>
|
|
65
|
+
<div className="flex items-center gap-1">
|
|
78
66
|
<EllipsisTypography
|
|
79
|
-
className=
|
|
80
|
-
containerClassname={
|
|
81
|
-
!isListMode && classes.legendName,
|
|
82
|
-
isListMode && !isDisplayedOnSide && classes.textListBottom,
|
|
83
|
-
isListMode && isDisplayedOnSide && classes.legendName
|
|
84
|
-
)}
|
|
67
|
+
className="text-xs leading-none font-medium"
|
|
68
|
+
containerClassname={`w-auto ${(!isListMode || (isListMode && isDisplayedOnSide)) && 'max-w-[166px]'}`}
|
|
85
69
|
data-mode={
|
|
86
70
|
value ? LegendDisplayMode.Compact : LegendDisplayMode.Normal
|
|
87
71
|
}
|
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
Dispatch,
|
|
3
|
+
KeyboardEvent,
|
|
4
|
+
MouseEvent,
|
|
5
|
+
ReactElement,
|
|
6
|
+
ReactNode,
|
|
7
|
+
SetStateAction,
|
|
8
|
+
useMemo
|
|
9
|
+
} from 'react';
|
|
2
10
|
|
|
3
11
|
import { equals, prop, slice, sortBy } from 'ramda';
|
|
4
12
|
|
|
5
|
-
import {
|
|
13
|
+
import { alpha, useTheme } from '@mui/material';
|
|
6
14
|
|
|
7
15
|
import { useMemoComponent } from '@centreon/ui';
|
|
8
16
|
|
|
@@ -10,14 +18,13 @@ import { formatMetricValue } from '../../common/timeSeries';
|
|
|
10
18
|
import { Line } from '../../common/timeSeries/models';
|
|
11
19
|
import { LegendModel } from '../models';
|
|
12
20
|
import { labelAvg, labelMax, labelMin } from '../translatedLabels';
|
|
13
|
-
|
|
14
|
-
import { useStyles } from './Legend.styles';
|
|
15
21
|
import LegendContent from './LegendContent';
|
|
16
22
|
import LegendHeader from './LegendHeader';
|
|
17
23
|
import { GetMetricValueProps, LegendDisplayMode } from './models';
|
|
18
24
|
import useLegend from './useLegend';
|
|
19
25
|
|
|
20
|
-
interface Props
|
|
26
|
+
interface Props
|
|
27
|
+
extends Pick<LegendModel, 'placement' | 'mode' | 'showCalculations'> {
|
|
21
28
|
base: number;
|
|
22
29
|
height: number | null;
|
|
23
30
|
limitLegend?: false | number;
|
|
@@ -42,15 +49,14 @@ const MainLegend = ({
|
|
|
42
49
|
setLinesGraph,
|
|
43
50
|
shouldDisplayLegendInCompactMode,
|
|
44
51
|
placement,
|
|
45
|
-
height,
|
|
46
52
|
mode,
|
|
53
|
+
showCalculations = {
|
|
54
|
+
min: true,
|
|
55
|
+
max: true,
|
|
56
|
+
avg: true
|
|
57
|
+
},
|
|
47
58
|
secondaryClick
|
|
48
|
-
}: Props):
|
|
49
|
-
const { classes, cx } = useStyles({
|
|
50
|
-
limitLegendRows: Boolean(limitLegend),
|
|
51
|
-
placement,
|
|
52
|
-
height
|
|
53
|
-
});
|
|
59
|
+
}: Props): ReactElement => {
|
|
54
60
|
const theme = useTheme();
|
|
55
61
|
|
|
56
62
|
const { selectMetricLine, clearHighlight, highlightLine, toggleMetricLine } =
|
|
@@ -73,22 +79,25 @@ const MainLegend = ({
|
|
|
73
79
|
|
|
74
80
|
const contextMenuClick =
|
|
75
81
|
(metricId: number) =>
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
(event: MouseEvent): void => {
|
|
83
|
+
if (!secondaryClick) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
event.preventDefault();
|
|
87
|
+
secondaryClick({
|
|
88
|
+
element: event.target,
|
|
89
|
+
metricId,
|
|
90
|
+
position: [event.pageX, event.pageY]
|
|
91
|
+
});
|
|
92
|
+
};
|
|
87
93
|
|
|
88
94
|
const selectMetric = ({
|
|
89
95
|
event,
|
|
90
96
|
metric_id
|
|
91
|
-
}: {
|
|
97
|
+
}: {
|
|
98
|
+
event: MouseEvent<HTMLLIElement> | KeyboardEvent<HTMLLIElement>;
|
|
99
|
+
metric_id: number;
|
|
100
|
+
}): void => {
|
|
92
101
|
if (!toggable) {
|
|
93
102
|
return;
|
|
94
103
|
}
|
|
@@ -109,83 +118,87 @@ const MainLegend = ({
|
|
|
109
118
|
|
|
110
119
|
return (
|
|
111
120
|
<div
|
|
112
|
-
className={
|
|
121
|
+
className={`overflow-x-hidden overflow-y-auto ${!equals(placement, 'bottom') ? 'h-full mt-[15px]' : 'ml-[50px] mr-[40px]'} legend`}
|
|
113
122
|
data-display-side={!equals(placement, 'bottom')}
|
|
114
123
|
>
|
|
115
|
-
<
|
|
116
|
-
className={
|
|
124
|
+
<ul
|
|
125
|
+
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-[67px]' : 'max-h-0'}`}
|
|
117
126
|
data-as-list={isListMode || !equals(placement, 'bottom')}
|
|
118
127
|
data-mode={itemMode}
|
|
119
128
|
>
|
|
120
129
|
{displayedLines.map((line) => {
|
|
121
|
-
const { color, display,
|
|
130
|
+
const { color, display, metric_id, unit } = line;
|
|
122
131
|
|
|
123
132
|
const markerColor = display
|
|
124
133
|
? color
|
|
125
134
|
: alpha(theme.palette.text.disabled, 0.2);
|
|
126
135
|
|
|
127
136
|
const minMaxAvg = [
|
|
128
|
-
{
|
|
137
|
+
showCalculations.min && {
|
|
129
138
|
label: labelMin,
|
|
130
139
|
value: line.minimum_value
|
|
131
140
|
},
|
|
132
|
-
{
|
|
141
|
+
showCalculations.max && {
|
|
133
142
|
label: labelMax,
|
|
134
143
|
value: line.maximum_value
|
|
135
144
|
},
|
|
136
|
-
{
|
|
145
|
+
showCalculations.avg && {
|
|
137
146
|
label: labelAvg,
|
|
138
147
|
value: line.average_value
|
|
139
148
|
}
|
|
140
|
-
];
|
|
149
|
+
].filter(Boolean);
|
|
141
150
|
|
|
142
151
|
return (
|
|
143
|
-
<
|
|
144
|
-
className={
|
|
145
|
-
classes.item,
|
|
146
|
-
highlight ? classes.highlight : classes.normal,
|
|
147
|
-
toggable && classes.toggable
|
|
148
|
-
)}
|
|
152
|
+
<li
|
|
153
|
+
className={`${!display ? 'text-text-disabled' : 'text-text-primary'} flex gap-1 ${toggable && 'cursor-pointer'}`}
|
|
149
154
|
key={metric_id}
|
|
150
155
|
onClick={(event): void => selectMetric({ event, metric_id })}
|
|
156
|
+
onKeyUp={(event) =>
|
|
157
|
+
event.key === 'Enter' && selectMetric({ event, metric_id })
|
|
158
|
+
}
|
|
151
159
|
onMouseEnter={(): void => highlightLine(metric_id)}
|
|
152
160
|
onMouseLeave={(): void => clearHighlight()}
|
|
153
161
|
onContextMenu={contextMenuClick(metric_id)}
|
|
154
162
|
>
|
|
155
|
-
<
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
isListMode={isListMode}
|
|
160
|
-
line={line}
|
|
161
|
-
minMaxAvg={
|
|
162
|
-
shouldDisplayLegendInCompactMode ? minMaxAvg : undefined
|
|
163
|
-
}
|
|
164
|
-
unit={unit}
|
|
163
|
+
<div
|
|
164
|
+
className="h-full rounded-sm w-1"
|
|
165
|
+
style={{ backgroundColor: markerColor }}
|
|
166
|
+
data-icon
|
|
165
167
|
/>
|
|
166
|
-
|
|
167
|
-
<
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
168
|
+
<div>
|
|
169
|
+
<LegendHeader
|
|
170
|
+
isDisplayedOnSide={!equals(placement, 'bottom')}
|
|
171
|
+
isListMode={isListMode}
|
|
172
|
+
line={line}
|
|
173
|
+
minMaxAvg={
|
|
174
|
+
shouldDisplayLegendInCompactMode ? minMaxAvg : undefined
|
|
175
|
+
}
|
|
176
|
+
unit={unit}
|
|
177
|
+
/>
|
|
178
|
+
{!shouldDisplayLegendInCompactMode && !isListMode && (
|
|
179
|
+
<div>
|
|
180
|
+
<div className="flex flex-wrap gap-1 whitespace-nowrap">
|
|
181
|
+
{minMaxAvg.map(({ label, value }) => (
|
|
182
|
+
<LegendContent
|
|
183
|
+
data={getMetricValue({ unit: line.unit, value })}
|
|
184
|
+
key={label}
|
|
185
|
+
label={label}
|
|
186
|
+
/>
|
|
187
|
+
))}
|
|
188
|
+
</div>
|
|
176
189
|
</div>
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
</
|
|
190
|
+
)}
|
|
191
|
+
</div>
|
|
192
|
+
</li>
|
|
180
193
|
);
|
|
181
194
|
})}
|
|
182
|
-
</
|
|
195
|
+
</ul>
|
|
183
196
|
{renderExtraComponent}
|
|
184
197
|
</div>
|
|
185
198
|
);
|
|
186
199
|
};
|
|
187
200
|
|
|
188
|
-
const Legend = (props: Props):
|
|
201
|
+
const Legend = (props: Props): ReactElement => {
|
|
189
202
|
const {
|
|
190
203
|
toggable,
|
|
191
204
|
limitLegend,
|
|
@@ -175,6 +175,11 @@ export interface LegendModel {
|
|
|
175
175
|
mode: 'grid' | 'list';
|
|
176
176
|
placement: 'bottom' | 'left' | 'right';
|
|
177
177
|
renderExtraComponent?: ReactNode;
|
|
178
|
+
showCalculations?: {
|
|
179
|
+
min: boolean;
|
|
180
|
+
max: boolean;
|
|
181
|
+
avg: boolean;
|
|
182
|
+
};
|
|
178
183
|
secondaryClick?: (props: {
|
|
179
184
|
element: EventTarget | null;
|
|
180
185
|
metricId: number | string;
|
|
@@ -21,7 +21,11 @@ interface Props {
|
|
|
21
21
|
isHorizontal?: boolean;
|
|
22
22
|
legend: Pick<
|
|
23
23
|
LegendModel,
|
|
24
|
-
|
|
24
|
+
| 'renderExtraComponent'
|
|
25
|
+
| 'placement'
|
|
26
|
+
| 'mode'
|
|
27
|
+
| 'secondaryClick'
|
|
28
|
+
| 'showCalculations'
|
|
25
29
|
> & {
|
|
26
30
|
displayLegend: boolean;
|
|
27
31
|
legendHeight?: number;
|
|
@@ -104,6 +108,7 @@ const BaseChart = ({
|
|
|
104
108
|
shouldDisplayLegendInCompactMode={
|
|
105
109
|
shouldDisplayLegendInCompactMode
|
|
106
110
|
}
|
|
111
|
+
showCalculations={legend?.showCalculations}
|
|
107
112
|
secondaryClick={legend?.secondaryClick}
|
|
108
113
|
/>
|
|
109
114
|
</div>
|
|
@@ -127,6 +132,7 @@ const BaseChart = ({
|
|
|
127
132
|
setLinesGraph={setLines}
|
|
128
133
|
shouldDisplayLegendInCompactMode={shouldDisplayLegendInCompactMode}
|
|
129
134
|
secondaryClick={legend?.secondaryClick}
|
|
135
|
+
showCalculations={legend?.showCalculations}
|
|
130
136
|
/>
|
|
131
137
|
</div>
|
|
132
138
|
)}
|