@scality/core-ui 0.169.0 → 0.171.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/__mocks__/uuid.js +11 -0
- package/dist/components/barchartv2/Barchart.component.js +2 -2
- package/dist/components/buttonv2/Buttonv2.component.js +1 -1
- package/dist/components/chartlegend/ChartLegend.d.ts +3 -1
- package/dist/components/chartlegend/ChartLegend.d.ts.map +1 -1
- package/dist/components/chartlegend/ChartLegend.js +2 -2
- package/dist/components/chartlegend/ChartLegendWrapper.d.ts +3 -1
- package/dist/components/chartlegend/ChartLegendWrapper.d.ts.map +1 -1
- package/dist/components/chartlegend/ChartLegendWrapper.js +43 -9
- package/dist/components/date/FormattedDateTime.d.ts +41 -2
- package/dist/components/date/FormattedDateTime.d.ts.map +1 -1
- package/dist/components/date/FormattedDateTime.js +55 -8
- package/dist/components/date/FormattedDateTime.spec.js +12 -3
- package/dist/components/icon/Icon.component.d.ts +2 -0
- package/dist/components/icon/Icon.component.d.ts.map +1 -1
- package/dist/components/icon/Icon.component.js +2 -0
- package/dist/components/layout/v2/index.d.ts +2 -1
- package/dist/components/layout/v2/index.d.ts.map +1 -1
- package/dist/components/layout/v2/index.js +3 -3
- package/dist/components/linetemporalchart/ChartUtil.d.ts.map +1 -1
- package/dist/components/linetemporalchart/ChartUtil.js +12 -0
- package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts +10 -5
- package/dist/components/linetimeseriechart/linetimeseriechart.component.d.ts.map +1 -1
- package/dist/components/linetimeseriechart/linetimeseriechart.component.js +84 -49
- package/dist/components/text/Text.component.d.ts +2 -1
- package/dist/components/text/Text.component.d.ts.map +1 -1
- package/dist/next.d.ts +1 -1
- package/dist/next.d.ts.map +1 -1
- package/dist/next.js +1 -1
- package/dist/style/theme.js +1 -1
- package/package.json +3 -1
- package/src/lib/components/barchartv2/Barchart.component.tsx +2 -2
- package/src/lib/components/barchartv2/ChartTooltip.test.tsx +1 -1
- package/src/lib/components/buttonv2/Buttonv2.component.tsx +1 -1
- package/src/lib/components/chartlegend/ChartLegend.tsx +4 -2
- package/src/lib/components/chartlegend/ChartLegendWrapper.test.tsx +197 -0
- package/src/lib/components/chartlegend/ChartLegendWrapper.tsx +65 -9
- package/src/lib/components/date/FormattedDateTime.spec.tsx +27 -2
- package/src/lib/components/date/FormattedDateTime.tsx +61 -11
- package/src/lib/components/icon/Icon.component.tsx +2 -0
- package/src/lib/components/layout/v2/index.tsx +5 -3
- package/src/lib/components/linetemporalchart/ChartUtil.ts +26 -0
- package/src/lib/components/linetimeseriechart/linetimeseriechart.component.tsx +227 -157
- package/src/lib/components/text/Text.component.tsx +8 -1
- package/src/lib/next.ts +4 -1
- package/src/lib/style/theme.ts +1 -1
- package/stories/BarChart/barchart.stories.tsx +7 -1
- package/stories/formattedate.stories.tsx +7 -0
- package/stories/layout.stories.tsx +19 -0
- package/stories/linetimeseriechart.stories.tsx +217 -1
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Line, LineChart, ReferenceLine, ResponsiveContainer, Tooltip, XAxis, YAxis,
|
|
3
|
-
import { useCallback, useMemo, useRef } from 'react';
|
|
4
|
-
import { useTheme } from 'styled-components';
|
|
5
|
-
import {
|
|
6
|
-
import styled from 'styled-components';
|
|
2
|
+
import { CartesianGrid, Line, LineChart, ReferenceLine, ResponsiveContainer, Tooltip, XAxis, YAxis, } from 'recharts';
|
|
3
|
+
import { useCallback, useMemo, useRef, useState } from 'react';
|
|
4
|
+
import styled, { useTheme } from 'styled-components';
|
|
5
|
+
import { spacing } from '../../spacing';
|
|
7
6
|
import { fontSize, fontWeight } from '../../style/theme';
|
|
7
|
+
import { Box } from '../box/Box';
|
|
8
8
|
import { useChartLegend } from '../chartlegend/ChartLegendWrapper';
|
|
9
|
-
import {
|
|
10
|
-
import { Loader } from '../loader/Loader.component';
|
|
11
|
-
import { spacing } from '../../spacing';
|
|
12
|
-
import { getUnitLabel } from '../linetemporalchart/ChartUtil';
|
|
9
|
+
import { FormattedDateTime } from '../date/FormattedDateTime';
|
|
13
10
|
import { Icon } from '../icon/Icon.component';
|
|
11
|
+
import { addMissingDataPoint, getUnitLabel, } from '../linetemporalchart/ChartUtil';
|
|
12
|
+
import { Loader } from '../loader/Loader.component';
|
|
13
|
+
import { ChartTitleText, SmallerText } from '../text/Text.component';
|
|
14
14
|
import { Tooltip as TooltipComponent } from '../tooltip/Tooltip.component';
|
|
15
|
-
import { FormattedDateTime } from '../date/FormattedDateTime';
|
|
16
|
-
import { Box } from '../box/Box';
|
|
17
15
|
import { formatXAxisLabel } from './utils';
|
|
18
16
|
const LineTemporalChartWrapper = styled.div `
|
|
19
17
|
display: flex;
|
|
@@ -25,7 +23,7 @@ const ChartHeader = styled.div `
|
|
|
25
23
|
display: flex;
|
|
26
24
|
align-items: center;
|
|
27
25
|
`;
|
|
28
|
-
const TooltipContainer = styled.div `
|
|
26
|
+
export const TooltipContainer = styled.div `
|
|
29
27
|
background-color: ${(props) => props.theme.backgroundLevel1};
|
|
30
28
|
padding: ${spacing.r8};
|
|
31
29
|
border: 1px solid ${(props) => props.theme.border};
|
|
@@ -33,7 +31,7 @@ const TooltipContainer = styled.div `
|
|
|
33
31
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
34
32
|
max-width: 250px;
|
|
35
33
|
`;
|
|
36
|
-
const TooltipTime = styled.div `
|
|
34
|
+
export const TooltipTime = styled.div `
|
|
37
35
|
margin-bottom: ${spacing.r8};
|
|
38
36
|
color: ${(props) => props.theme.textPrimary};
|
|
39
37
|
font-size: ${fontSize.smaller};
|
|
@@ -73,9 +71,13 @@ const TooltipInstanceValue = styled.div `
|
|
|
73
71
|
flex-shrink: 0;
|
|
74
72
|
text-align: right;
|
|
75
73
|
`;
|
|
76
|
-
const CustomTooltip = ({
|
|
77
|
-
|
|
74
|
+
const CustomTooltip = ({ unitLabel, timeFormat, isChartActive, tooltipProps, renderTooltip, }) => {
|
|
75
|
+
const { active, payload, label, ...rest } = tooltipProps;
|
|
76
|
+
if (!active || !payload || !payload.length || !label || !isChartActive)
|
|
78
77
|
return null;
|
|
78
|
+
if (renderTooltip) {
|
|
79
|
+
return renderTooltip(tooltipProps, unitLabel, timeFormat);
|
|
80
|
+
}
|
|
79
81
|
// We can't use the default itemSorter method because it's a custom tooltip.
|
|
80
82
|
// Sort the payload here instead
|
|
81
83
|
const sortedPayload = [...payload].sort((a, b) => {
|
|
@@ -98,23 +100,42 @@ const CustomTooltip = ({ active, payload, label, unitLabel, timeFormat, }) => {
|
|
|
98
100
|
const isSymmetricalSeries = (series) => {
|
|
99
101
|
return 'above' in series && 'below' in series;
|
|
100
102
|
};
|
|
101
|
-
export function LineTimeSerieChart({ series, title, height, startingTimeStamp, interval, duration, unitRange, isLoading = false, timeFormat = 'date-time', yAxisType = 'default', yAxisTitle, helpText, ...rest }) {
|
|
103
|
+
export function LineTimeSerieChart({ series, title, height, startingTimeStamp, interval, duration, unitRange, isLoading = false, timeFormat = 'date-time', yAxisType = 'default', yAxisTitle, helpText, syncId, renderTooltip, ...rest }) {
|
|
102
104
|
const theme = useTheme();
|
|
103
|
-
const { getColor } = useChartLegend();
|
|
105
|
+
const { getColor, selectedResources } = useChartLegend();
|
|
104
106
|
const chartRef = useRef(null);
|
|
107
|
+
const [isChartActive, setIsChartActive] = useState(false);
|
|
105
108
|
const chartData = useMemo(() => {
|
|
109
|
+
// Guard against empty/undefined series data
|
|
110
|
+
if (!series || (Array.isArray(series) && series.length === 0)) {
|
|
111
|
+
return [];
|
|
112
|
+
}
|
|
113
|
+
// Handle symmetrical series with empty above/below arrays
|
|
114
|
+
if (isSymmetricalSeries(series)) {
|
|
115
|
+
if ((!series.above || series.above.length === 0) &&
|
|
116
|
+
(!series.below || series.below.length === 0)) {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
}
|
|
106
120
|
// 1. Add missing data points
|
|
107
121
|
const normalizedSeries = yAxisType === 'symmetrical' && isSymmetricalSeries(series)
|
|
108
122
|
? {
|
|
109
|
-
above: series.above
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
123
|
+
above: series.above
|
|
124
|
+
? series.above.map((line) => ({
|
|
125
|
+
...line,
|
|
126
|
+
data: addMissingDataPoint(line.data, startingTimeStamp, duration, interval),
|
|
127
|
+
}))
|
|
128
|
+
: [],
|
|
113
129
|
// Convert positive values to negative values
|
|
114
|
-
below: series.below
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
130
|
+
below: series.below
|
|
131
|
+
? series.below.map((line) => ({
|
|
132
|
+
...line,
|
|
133
|
+
data: addMissingDataPoint(line.data, startingTimeStamp, duration, interval).map(([timestamp, value]) => [
|
|
134
|
+
timestamp,
|
|
135
|
+
value === null ? null : `-${Number(value)}`,
|
|
136
|
+
]),
|
|
137
|
+
}))
|
|
138
|
+
: [],
|
|
118
139
|
}
|
|
119
140
|
: series.map((line) => ({
|
|
120
141
|
...line,
|
|
@@ -177,6 +198,14 @@ export function LineTimeSerieChart({ series, title, height, startingTimeStamp, i
|
|
|
177
198
|
return !isNaN(num) && num !== null ? num : null;
|
|
178
199
|
})
|
|
179
200
|
.filter((value) => value !== null));
|
|
201
|
+
// Guard against empty values array
|
|
202
|
+
if (values.length === 0) {
|
|
203
|
+
return {
|
|
204
|
+
topValue: 100, // Default value for empty charts
|
|
205
|
+
unitLabel: '',
|
|
206
|
+
rechartsData: [],
|
|
207
|
+
};
|
|
208
|
+
}
|
|
180
209
|
const top = Math.abs(Math.max(...values));
|
|
181
210
|
const bottom = Math.abs(Math.min(...values));
|
|
182
211
|
const maxValue = Math.max(top, bottom);
|
|
@@ -196,11 +225,17 @@ export function LineTimeSerieChart({ series, title, height, startingTimeStamp, i
|
|
|
196
225
|
// Group series by resource and create color mapping
|
|
197
226
|
const { colorMapping, groupedSeries } = useMemo(() => {
|
|
198
227
|
const mapping = {};
|
|
228
|
+
// Guard against empty/undefined series
|
|
229
|
+
if (!series) {
|
|
230
|
+
return { colorMapping: mapping, groupedSeries: {} };
|
|
231
|
+
}
|
|
199
232
|
const allSeries = isSymmetricalSeries(series)
|
|
200
|
-
? [...series.above, ...series.below]
|
|
233
|
+
? [...(series.above || []), ...(series.below || [])]
|
|
201
234
|
: series;
|
|
202
235
|
// Group series by resource
|
|
203
|
-
const groups = allSeries
|
|
236
|
+
const groups = allSeries
|
|
237
|
+
.filter((serie) => selectedResources.includes(serie.resource))
|
|
238
|
+
.reduce((acc, serie) => {
|
|
204
239
|
const key = serie.resource;
|
|
205
240
|
if (!acc[key]) {
|
|
206
241
|
acc[key] = [];
|
|
@@ -222,30 +257,30 @@ export function LineTimeSerieChart({ series, title, height, startingTimeStamp, i
|
|
|
222
257
|
colorMapping: mapping,
|
|
223
258
|
groupedSeries: groups,
|
|
224
259
|
};
|
|
225
|
-
}, [series, getColor]);
|
|
260
|
+
}, [series, getColor, selectedResources]);
|
|
226
261
|
// Format time for display the tick in the x axis
|
|
227
262
|
const formatXAxisLabelCallback = useCallback((timestamp) => formatXAxisLabel(timestamp, timeFormat, chartData), [timeFormat, chartData]);
|
|
228
|
-
return (_jsxs(LineTemporalChartWrapper, { children: [_jsxs(ChartHeader, { children: [_jsxs(ChartTitleText, { children: [title, " ", unitLabel && `(${unitLabel})`] }), helpText && (_jsx(Box, { ml: spacing.r4, children: _jsx(TooltipComponent, { placement: 'right', overlay: _jsx(SmallerText, { children: helpText }), children: _jsx(Icon, { name: "Info", color: theme.buttonSecondary }) }) })), isLoading && _jsx(Loader, {})] }), _jsx(ResponsiveContainer, { width: "100%", height: height, children: _jsxs(LineChart, { data: rechartsData, ref: chartRef, margin: { top: 0, right: 0, bottom: 0, left: 0 }, "aria-label": `Time series chart for ${title}`, children: [_jsx(CartesianGrid, { vertical: true, horizontal: true, verticalPoints: [0], horizontalPoints: [0], stroke: theme.border, fill: theme.backgroundLevel4, strokeWidth: 1 }), _jsx(XAxis, { dataKey: "timestamp", type: "number", domain: ['dataMin', 'dataMax'], ticks: xAxisTicks, tickFormatter: formatXAxisLabelCallback, tickCount: 5, tick: {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
263
|
+
return (_jsxs(LineTemporalChartWrapper, { children: [_jsxs(ChartHeader, { children: [_jsxs(ChartTitleText, { children: [title, " ", unitLabel && `(${unitLabel})`] }), helpText && (_jsx(Box, { ml: spacing.r4, children: _jsx(TooltipComponent, { placement: 'right', overlay: _jsx(SmallerText, { children: helpText }), children: _jsx(Icon, { name: "Info", color: theme.buttonSecondary }) }) })), isLoading && _jsx(Loader, {})] }), _jsx("div", { onFocus: () => setIsChartActive(true), onBlur: () => setIsChartActive(false), onFocusCapture: () => setIsChartActive(true), onBlurCapture: () => setIsChartActive(false), children: _jsx(ResponsiveContainer, { width: "100%", height: height, children: _jsxs(LineChart, { data: rechartsData, ref: chartRef, margin: { top: 0, right: 0, bottom: 0, left: 0 }, "aria-label": `Time series chart for ${title}`, syncId: syncId, onMouseEnter: () => setIsChartActive(true), onMouseLeave: () => setIsChartActive(false), children: [_jsx(CartesianGrid, { vertical: true, horizontal: true, verticalPoints: [0], horizontalPoints: [0], stroke: theme.border, fill: theme.backgroundLevel4, strokeWidth: 1 }), _jsx(XAxis, { dataKey: "timestamp", type: "number", domain: ['dataMin', 'dataMax'], ticks: xAxisTicks, tickFormatter: formatXAxisLabelCallback, tickCount: 5, tick: {
|
|
264
|
+
fill: theme.textSecondary,
|
|
265
|
+
fontSize: fontSize.smaller,
|
|
266
|
+
}, axisLine: { stroke: theme.border } }), _jsx(YAxis, { orientation: "right", allowDataOverflow: false, label: {
|
|
267
|
+
value: yAxisTitle,
|
|
268
|
+
angle: 90,
|
|
269
|
+
position: 'insideRight',
|
|
270
|
+
style: {
|
|
271
|
+
textAnchor: 'middle',
|
|
272
|
+
fill: theme.textSecondary,
|
|
273
|
+
fontSize: fontSize.smaller,
|
|
274
|
+
},
|
|
275
|
+
}, domain: yAxisType === 'percentage'
|
|
276
|
+
? [0, 100]
|
|
277
|
+
: yAxisType === 'symmetrical'
|
|
278
|
+
? [-topValue, topValue]
|
|
279
|
+
: [0, topValue], axisLine: { stroke: theme.border }, tick: {
|
|
237
280
|
fill: theme.textSecondary,
|
|
238
281
|
fontSize: fontSize.smaller,
|
|
239
|
-
},
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
? [-topValue, topValue]
|
|
244
|
-
: [0, topValue], axisLine: { stroke: theme.border }, tick: {
|
|
245
|
-
fill: theme.textSecondary,
|
|
246
|
-
fontSize: fontSize.smaller,
|
|
247
|
-
}, tickFormatter: (value) => Math.round(value).toString(), tickCount: 5, interval: 'preserveStartEnd' }), _jsx(Tooltip, { content: _jsx(CustomTooltip, { unitLabel: unitLabel, timeFormat: timeFormat }) }), yAxisType === 'symmetrical' && (_jsx(ReferenceLine, { y: 0, stroke: theme.border })), Object.entries(groupedSeries).map(([resource, resourceSeries]) => resourceSeries.map((serie, serieIndex) => {
|
|
248
|
-
const label = serie.getTooltipLabel(serie.metricPrefix, serie.resource);
|
|
249
|
-
return (_jsx(Line, { type: "monotone", dataKey: label, stroke: colorMapping[resource], dot: false, isAnimationActive: false }, `${title}-${resource}-${serieIndex}`));
|
|
250
|
-
}))] }) })] }));
|
|
282
|
+
}, tickFormatter: (value) => Math.round(value).toString(), tickCount: 5, interval: 'preserveStartEnd' }), _jsx(Tooltip, { content: (props) => (_jsx(CustomTooltip, { unitLabel: unitLabel, timeFormat: timeFormat, renderTooltip: renderTooltip, tooltipProps: props, isChartActive: isChartActive })) }), yAxisType === 'symmetrical' && (_jsx(ReferenceLine, { y: 0, stroke: theme.border })), Object.entries(groupedSeries).map(([resource, resourceSeries]) => resourceSeries.map((serie, serieIndex) => {
|
|
283
|
+
const label = serie.getTooltipLabel(serie.metricPrefix, serie.resource);
|
|
284
|
+
return (_jsx(Line, { type: "monotone", dataKey: label, stroke: colorMapping[resource], dot: false, isAnimationActive: false }, `${title}-${resource}-${serieIndex}`));
|
|
285
|
+
}))] }) }) })] }));
|
|
251
286
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CoreUITheme } from '../../style/theme';
|
|
2
|
+
export type TextVariant = 'ChartTitle' | 'Basic' | 'Smaller' | 'Larger' | 'Large';
|
|
2
3
|
type Status = 'unknown' | 'healthy' | 'warning' | 'critical';
|
|
3
4
|
type Props = {
|
|
4
5
|
children: React.ReactNode | string;
|
|
@@ -22,7 +23,7 @@ export declare const GentleEmphaseSecondaryText: import("styled-components").Sty
|
|
|
22
23
|
alignRight?: boolean;
|
|
23
24
|
}, never>;
|
|
24
25
|
export declare const Text: import("styled-components").StyledComponent<"span", import("styled-components").DefaultTheme, {
|
|
25
|
-
variant?:
|
|
26
|
+
variant?: TextVariant;
|
|
26
27
|
color?: keyof CoreUITheme;
|
|
27
28
|
isEmphazed?: boolean;
|
|
28
29
|
isGentleEmphazed?: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Text.component.d.ts","sourceRoot":"","sources":["../../../src/lib/components/text/Text.component.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,KAAK,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;AAC7D,KAAK,KAAK,GAAG;IACX,QAAQ,EAAE,KAAK,CAAC,SAAS,GAAG,MAAM,CAAC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CAAC;AAsDF,eAAO,MAAM,uBAAuB;iBACrB,MAAM;SAIpB,CAAC;AAIF,wBAAgB,SAAS,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,2CAErD;AACD,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,2CAEzD;AACD,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,2CAEtD;AACD,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,2CAEvD;AACD,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,2CAO9D;AACD,wBAAgB,SAAS,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,2CAErD;AACD,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,2CAEvD;AACD,wBAAgB,oBAAoB,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,2CAIhE;AACD,wBAAgB,kBAAkB,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,2CAOtE;AAED,wBAAgB,cAAc,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,2CAE1D;AACD,eAAO,MAAM,0BAA0B;iBACxB,OAAO;SAUrB,CAAC;AAEF,eAAO,MAAM,IAAI;cACL,
|
|
1
|
+
{"version":3,"file":"Text.component.d.ts","sourceRoot":"","sources":["../../../src/lib/components/text/Text.component.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,MAAM,MAAM,WAAW,GACnB,YAAY,GACZ,OAAO,GACP,SAAS,GACT,QAAQ,GACR,OAAO,CAAC;AAEZ,KAAK,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,UAAU,CAAC;AAC7D,KAAK,KAAK,GAAG;IACX,QAAQ,EAAE,KAAK,CAAC,SAAS,GAAG,MAAM,CAAC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;CACb,CAAC;AAsDF,eAAO,MAAM,uBAAuB;iBACrB,MAAM;SAIpB,CAAC;AAIF,wBAAgB,SAAS,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,2CAErD;AACD,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,2CAEzD;AACD,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,2CAEtD;AACD,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,2CAEvD;AACD,wBAAgB,UAAU,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,2CAO9D;AACD,wBAAgB,SAAS,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,2CAErD;AACD,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,2CAEvD;AACD,wBAAgB,oBAAoB,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,2CAIhE;AACD,wBAAgB,kBAAkB,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,2CAOtE;AAED,wBAAgB,cAAc,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,KAAK,2CAE1D;AACD,eAAO,MAAM,0BAA0B;iBACxB,OAAO;SAUrB,CAAC;AAEF,eAAO,MAAM,IAAI;cACL,WAAW;YACb,MAAM,WAAW;iBACZ,OAAO;uBACD,OAAO;SA2C3B,CAAC;AAEF,eAAO,MAAM,IAAI,uGAchB,CAAC"}
|
package/dist/next.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export { Input } from './components/inputv2/inputv2';
|
|
|
15
15
|
export { Accordion } from './components/accordion/Accordion.component';
|
|
16
16
|
export { Barchart, BarchartSortFn, BarchartTooltipFn, } from './components/barchartv2/Barchart.component';
|
|
17
17
|
export { ChartTooltip } from './components/barchartv2/ChartTooltip';
|
|
18
|
-
export { ChartLegendWrapper } from './components/chartlegend/ChartLegendWrapper';
|
|
18
|
+
export { ChartLegendWrapper, useChartId, } from './components/chartlegend/ChartLegendWrapper';
|
|
19
19
|
export { ChartLegend } from './components/chartlegend/ChartLegend';
|
|
20
20
|
export { LineTimeSerieChart } from './components/linetimeseriechart/linetimeseriechart.component';
|
|
21
21
|
export { CoreUITheme } from './style/theme';
|
package/dist/next.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"next.d.ts","sourceRoot":"","sources":["../src/lib/next.ts"],"names":[],"mappings":"AAAA,OAAO,2CAA2C,CAAC;AACnD,OAAO,aAAa,CAAC;AACrB,OAAO,EAAE,MAAM,EAAE,MAAM,0CAA0C,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,4CAA4C,CAAC;AACxE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,sCAAsC,CAAC;AACjE,OAAO,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,4DAA4D,CAAC;AAC/F,OAAO,EACL,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,uDAAuD,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,6CAA6C,CAAC;AACjF,OAAO,EAAE,MAAM,EAAE,MAAM,0CAA0C,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,wDAAwD,CAAC;AACxF,OAAO,EAAE,mBAAmB,EAAE,MAAM,sDAAsD,CAAC;AAC3F,OAAO,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,4CAA4C,CAAC;AACvE,OAAO,EACL,QAAQ,EACR,cAAc,EACd,iBAAiB,GAClB,MAAM,4CAA4C,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AACpE,OAAO,
|
|
1
|
+
{"version":3,"file":"next.d.ts","sourceRoot":"","sources":["../src/lib/next.ts"],"names":[],"mappings":"AAAA,OAAO,2CAA2C,CAAC;AACnD,OAAO,aAAa,CAAC;AACrB,OAAO,EAAE,MAAM,EAAE,MAAM,0CAA0C,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,4CAA4C,CAAC;AACxE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,sCAAsC,CAAC;AACjE,OAAO,EAAE,KAAK,EAAE,MAAM,wCAAwC,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,4DAA4D,CAAC;AAC/F,OAAO,EACL,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,uDAAuD,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,MAAM,6CAA6C,CAAC;AACjF,OAAO,EAAE,MAAM,EAAE,MAAM,0CAA0C,CAAC;AAClE,OAAO,EAAE,cAAc,EAAE,MAAM,wDAAwD,CAAC;AACxF,OAAO,EAAE,mBAAmB,EAAE,MAAM,sDAAsD,CAAC;AAC3F,OAAO,EAAE,GAAG,EAAE,MAAM,sBAAsB,CAAC;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,4CAA4C,CAAC;AACvE,OAAO,EACL,QAAQ,EACR,cAAc,EACd,iBAAiB,GAClB,MAAM,4CAA4C,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AACpE,OAAO,EACL,kBAAkB,EAClB,UAAU,GACX,MAAM,6CAA6C,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,8DAA8D,CAAC;AAClG,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/next.js
CHANGED
|
@@ -15,6 +15,6 @@ export { Input } from './components/inputv2/inputv2';
|
|
|
15
15
|
export { Accordion } from './components/accordion/Accordion.component';
|
|
16
16
|
export { Barchart, } from './components/barchartv2/Barchart.component';
|
|
17
17
|
export { ChartTooltip } from './components/barchartv2/ChartTooltip';
|
|
18
|
-
export { ChartLegendWrapper } from './components/chartlegend/ChartLegendWrapper';
|
|
18
|
+
export { ChartLegendWrapper, useChartId, } from './components/chartlegend/ChartLegendWrapper';
|
|
19
19
|
export { ChartLegend } from './components/chartlegend/ChartLegend';
|
|
20
20
|
export { LineTimeSerieChart } from './components/linetimeseriechart/linetimeseriechart.component';
|
package/dist/style/theme.js
CHANGED
|
@@ -121,7 +121,7 @@ export const coreUIAvailableThemes = {
|
|
|
121
121
|
selectedActive: '#037AFF',
|
|
122
122
|
highlight: '#1A3C75',
|
|
123
123
|
border: '#4A4A4A',
|
|
124
|
-
buttonPrimary: 'linear-gradient(130deg, #9355E7 0%, #2E4AA3
|
|
124
|
+
buttonPrimary: 'linear-gradient(130deg, #9355E7 0%, #2E4AA3 60%)',
|
|
125
125
|
buttonSecondary: 'linear-gradient(130deg, #595A78 0%, #44455F 100%)',
|
|
126
126
|
buttonDelete: '#3D0808',
|
|
127
127
|
infoPrimary: '#8E8EAC',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scality/core-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.171.0",
|
|
4
4
|
"description": "Scality common React component library",
|
|
5
5
|
"author": "Scality Engineering",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE",
|
|
@@ -61,6 +61,7 @@
|
|
|
61
61
|
"@types/react-window": "^1.8.5",
|
|
62
62
|
"@types/styled-components": "^5.1.25",
|
|
63
63
|
"@types/styled-system": "^5.1.15",
|
|
64
|
+
"@types/uuid": "^10.0.0",
|
|
64
65
|
"@typescript-eslint/eslint-plugin": "^6.12.0",
|
|
65
66
|
"@typescript-eslint/parser": "^6.12.0",
|
|
66
67
|
"copyfiles": "^2.4.1",
|
|
@@ -109,6 +110,7 @@
|
|
|
109
110
|
"recharts": "^3.0.2",
|
|
110
111
|
"styled-components": "^5.2.1",
|
|
111
112
|
"styled-system": "^5.1.5",
|
|
113
|
+
"uuid": "^13.0.0",
|
|
112
114
|
"vega": "^5.17.3",
|
|
113
115
|
"vega-embed": "6.0.0",
|
|
114
116
|
"vega-lite": "5.0.0",
|
|
@@ -24,13 +24,13 @@ import { ChartTooltip } from './ChartTooltip';
|
|
|
24
24
|
import { UnitRange, useChartData } from './utils';
|
|
25
25
|
|
|
26
26
|
const CHART_CONSTANTS = {
|
|
27
|
-
TICK_WIDTH_OFFSET:
|
|
27
|
+
TICK_WIDTH_OFFSET: 4,
|
|
28
28
|
BAR_SIZE: 12,
|
|
29
29
|
MIN_POINT_SIZE: 3,
|
|
30
30
|
DEFAULT_HEIGHT: 200,
|
|
31
31
|
CHART_MARGIN: {
|
|
32
32
|
left: 0,
|
|
33
|
-
right:
|
|
33
|
+
right: -10,
|
|
34
34
|
top: 0,
|
|
35
35
|
bottom: 0,
|
|
36
36
|
},
|
|
@@ -27,7 +27,7 @@ describe('ChartTooltip', () => {
|
|
|
27
27
|
successValue: () => screen.queryByText(SUCCESS_VALUE),
|
|
28
28
|
failed: () => screen.queryByText(/Failed/),
|
|
29
29
|
failedValue: () => screen.queryByText(FAILED_VALUE),
|
|
30
|
-
date: () => screen.queryByText(/Monday,
|
|
30
|
+
date: () => screen.queryByText(/Monday, 01 July 2024/),
|
|
31
31
|
time: () => screen.queryByText(/00:00/),
|
|
32
32
|
};
|
|
33
33
|
it('should render the ChartTooltip component', () => {
|
|
@@ -118,6 +118,7 @@ export const ButtonStyled = styled.button<Props>`
|
|
|
118
118
|
case 'outline':
|
|
119
119
|
return css`
|
|
120
120
|
border: ${spacing.r1} solid transparent;
|
|
121
|
+
border-color: ${brand.border}; // fallback for linear-gradient button themes
|
|
121
122
|
border-color: ${brand.buttonSecondary};
|
|
122
123
|
background-color: transparent;
|
|
123
124
|
color: ${brand.textPrimary};
|
|
@@ -144,7 +145,6 @@ export const ButtonStyled = styled.button<Props>`
|
|
|
144
145
|
position: absolute;
|
|
145
146
|
inset: 0;
|
|
146
147
|
padding: ${spacing.r1};
|
|
147
|
-
background-image: ${brand.buttonSecondary};
|
|
148
148
|
border-radius: inherit;
|
|
149
149
|
mask: linear-gradient(white, white) content-box, linear-gradient(white, white);
|
|
150
150
|
mask-composite: exclude;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import styled from 'styled-components';
|
|
2
2
|
import { useChartLegend } from './ChartLegendWrapper';
|
|
3
|
-
import { Text } from '../text/Text.component';
|
|
3
|
+
import { Text, TextVariant } from '../text/Text.component';
|
|
4
4
|
import { chartColors } from '../../style/theme';
|
|
5
5
|
import { useCallback } from 'react';
|
|
6
6
|
|
|
@@ -8,6 +8,7 @@ type ChartLegendProps = {
|
|
|
8
8
|
shape: 'line' | 'rectangle';
|
|
9
9
|
disabled?: boolean;
|
|
10
10
|
direction?: 'horizontal' | 'vertical';
|
|
11
|
+
legendSize?: TextVariant;
|
|
11
12
|
};
|
|
12
13
|
|
|
13
14
|
const Legend = styled.div<{ direction: 'horizontal' | 'vertical' }>`
|
|
@@ -65,6 +66,7 @@ export const ChartLegend = ({
|
|
|
65
66
|
shape,
|
|
66
67
|
disabled = false,
|
|
67
68
|
direction = 'horizontal',
|
|
69
|
+
legendSize = 'Basic',
|
|
68
70
|
}: ChartLegendProps) => {
|
|
69
71
|
const {
|
|
70
72
|
listResources,
|
|
@@ -132,7 +134,7 @@ export const ChartLegend = ({
|
|
|
132
134
|
shape={shape}
|
|
133
135
|
chartColors={chartColors}
|
|
134
136
|
/>
|
|
135
|
-
<Text variant=
|
|
137
|
+
<Text variant={legendSize}>{resource}</Text>
|
|
136
138
|
</LegendItem>
|
|
137
139
|
);
|
|
138
140
|
})}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { render, screen } from '@testing-library/react';
|
|
2
|
+
import React, { useEffect } from 'react';
|
|
3
|
+
import {
|
|
4
|
+
ChartLegendWrapper,
|
|
5
|
+
useChartId,
|
|
6
|
+
useChartLegend,
|
|
7
|
+
} from './ChartLegendWrapper';
|
|
8
|
+
import { ChartLegend } from './ChartLegend';
|
|
9
|
+
import userEvent from '@testing-library/user-event';
|
|
10
|
+
|
|
11
|
+
describe('ChartLegendWrapper', () => {
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
jest.clearAllMocks();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const TestChart = ({ seriesNames }: { seriesNames: string[] }) => {
|
|
17
|
+
const chartId = useChartId();
|
|
18
|
+
const { register } = useChartLegend();
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
register(chartId, seriesNames);
|
|
22
|
+
}, [chartId, register, seriesNames]);
|
|
23
|
+
|
|
24
|
+
return <div data-testid={`chart-${chartId}`}>Test Chart</div>;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const generateColors = (seriesNames: string[]) => {
|
|
28
|
+
const colors: Record<string, string> = {};
|
|
29
|
+
const colorPalette = ['red', 'blue', 'green', 'yellow', 'purple'];
|
|
30
|
+
seriesNames.forEach((name, index) => {
|
|
31
|
+
colors[name] = colorPalette[index % colorPalette.length];
|
|
32
|
+
});
|
|
33
|
+
return colors;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
describe('Dynamic Color Generation', () => {
|
|
37
|
+
it('should generate colors dynamically based on registered series', () => {
|
|
38
|
+
render(
|
|
39
|
+
<ChartLegendWrapper colorSet={generateColors}>
|
|
40
|
+
<TestChart seriesNames={['CPU', 'Memory']} />
|
|
41
|
+
<ChartLegend shape="line" />
|
|
42
|
+
</ChartLegendWrapper>,
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
expect(screen.getByText('CPU')).toBeInTheDocument();
|
|
46
|
+
expect(screen.getByText('Memory')).toBeInTheDocument();
|
|
47
|
+
expect(screen.getByLabelText('CPU selected')).toBeInTheDocument();
|
|
48
|
+
expect(screen.getByLabelText('Memory selected')).toBeInTheDocument();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should handle multiple charts with overlapping series', () => {
|
|
52
|
+
const TestChart1 = () => {
|
|
53
|
+
const chartId = useChartId();
|
|
54
|
+
const { register } = useChartLegend();
|
|
55
|
+
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
register(chartId, ['CPU', 'Memory']);
|
|
58
|
+
}, [chartId, register]);
|
|
59
|
+
|
|
60
|
+
return <div data-testid={`chart1-${chartId}`}>Test Chart 1</div>;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const TestChart2 = () => {
|
|
64
|
+
const chartId = useChartId();
|
|
65
|
+
const { register } = useChartLegend();
|
|
66
|
+
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
register(chartId, ['CPU', 'Disk']);
|
|
69
|
+
}, [chartId, register]);
|
|
70
|
+
|
|
71
|
+
return <div data-testid={`chart2-${chartId}`}>Test Chart 2</div>;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
render(
|
|
75
|
+
<ChartLegendWrapper colorSet={generateColors}>
|
|
76
|
+
<TestChart1 />
|
|
77
|
+
<TestChart2 />
|
|
78
|
+
<ChartLegend shape="line" />
|
|
79
|
+
</ChartLegendWrapper>,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// Should show unique series from both charts
|
|
83
|
+
expect(screen.getByText('CPU')).toBeInTheDocument();
|
|
84
|
+
expect(screen.getByText('Memory')).toBeInTheDocument();
|
|
85
|
+
expect(screen.getByText('Disk')).toBeInTheDocument();
|
|
86
|
+
|
|
87
|
+
// All should be selected by default
|
|
88
|
+
expect(screen.getByLabelText('CPU selected')).toBeInTheDocument();
|
|
89
|
+
expect(screen.getByLabelText('Memory selected')).toBeInTheDocument();
|
|
90
|
+
expect(screen.getByLabelText('Disk selected')).toBeInTheDocument();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should handle empty series registration', () => {
|
|
94
|
+
render(
|
|
95
|
+
<ChartLegendWrapper colorSet={generateColors}>
|
|
96
|
+
<TestChart seriesNames={[]} />
|
|
97
|
+
<ChartLegend shape="line" />
|
|
98
|
+
</ChartLegendWrapper>,
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// Should not crash and should render empty legend
|
|
102
|
+
expect(screen.queryByRole('button')).not.toBeInTheDocument();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should maintain selection state when new series are added', () => {
|
|
106
|
+
const { rerender } = render(
|
|
107
|
+
<ChartLegendWrapper colorSet={generateColors}>
|
|
108
|
+
<TestChart seriesNames={['CPU']} />
|
|
109
|
+
<ChartLegend shape="line" />
|
|
110
|
+
</ChartLegendWrapper>,
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
// Initially only CPU
|
|
114
|
+
expect(screen.getByText('CPU')).toBeInTheDocument();
|
|
115
|
+
expect(screen.getByLabelText('CPU selected')).toBeInTheDocument();
|
|
116
|
+
|
|
117
|
+
// Select only CPU
|
|
118
|
+
userEvent.click(screen.getByText('CPU'));
|
|
119
|
+
expect(screen.getByLabelText('CPU selected')).toBeInTheDocument();
|
|
120
|
+
|
|
121
|
+
// Add more series
|
|
122
|
+
rerender(
|
|
123
|
+
<ChartLegendWrapper colorSet={generateColors}>
|
|
124
|
+
<TestChart seriesNames={['CPU', 'Memory']} />
|
|
125
|
+
<ChartLegend shape="line" />
|
|
126
|
+
</ChartLegendWrapper>,
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
// New series should be added and all should be selected (reset behavior)
|
|
130
|
+
expect(screen.getByText('CPU')).toBeInTheDocument();
|
|
131
|
+
expect(screen.getByText('Memory')).toBeInTheDocument();
|
|
132
|
+
expect(screen.getByLabelText('CPU selected')).toBeInTheDocument();
|
|
133
|
+
expect(screen.getByLabelText('Memory selected')).toBeInTheDocument();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('should work with different chart configurations', () => {
|
|
137
|
+
render(
|
|
138
|
+
<ChartLegendWrapper colorSet={generateColors}>
|
|
139
|
+
<TestChart seriesNames={['Series1', 'Series2', 'Series3']} />
|
|
140
|
+
<ChartLegend shape="rectangle" direction="vertical" />
|
|
141
|
+
</ChartLegendWrapper>,
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
expect(screen.getByText('Series1')).toBeInTheDocument();
|
|
145
|
+
expect(screen.getByText('Series2')).toBeInTheDocument();
|
|
146
|
+
expect(screen.getByText('Series3')).toBeInTheDocument();
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe('Static Color Set', () => {
|
|
151
|
+
const staticColorSet = {
|
|
152
|
+
CPU: 'red',
|
|
153
|
+
Memory: 'blue',
|
|
154
|
+
Disk: 'green',
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
it('should work with static color sets', () => {
|
|
158
|
+
render(
|
|
159
|
+
<ChartLegendWrapper colorSet={staticColorSet}>
|
|
160
|
+
<ChartLegend shape="line" />
|
|
161
|
+
</ChartLegendWrapper>,
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
expect(screen.getByText('CPU')).toBeInTheDocument();
|
|
165
|
+
expect(screen.getByText('Memory')).toBeInTheDocument();
|
|
166
|
+
expect(screen.getByText('Disk')).toBeInTheDocument();
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it('should ignore registration when using static color sets', () => {
|
|
170
|
+
render(
|
|
171
|
+
<ChartLegendWrapper colorSet={staticColorSet}>
|
|
172
|
+
<TestChart seriesNames={['DifferentSeries']} />
|
|
173
|
+
<ChartLegend shape="line" />
|
|
174
|
+
</ChartLegendWrapper>,
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
// Should only show static color set items, not registered series
|
|
178
|
+
expect(screen.getByText('CPU')).toBeInTheDocument();
|
|
179
|
+
expect(screen.getByText('Memory')).toBeInTheDocument();
|
|
180
|
+
expect(screen.getByText('Disk')).toBeInTheDocument();
|
|
181
|
+
expect(screen.queryByText('DifferentSeries')).not.toBeInTheDocument();
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
describe('Error Handling', () => {
|
|
186
|
+
it('should throw error when useChartLegend is used outside wrapper', () => {
|
|
187
|
+
const TestComponent = () => {
|
|
188
|
+
useChartLegend();
|
|
189
|
+
return <div>Test</div>;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
expect(() => render(<TestComponent />)).toThrow(
|
|
193
|
+
'useChartLegend must be used within a ChartLegendWrapper',
|
|
194
|
+
);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
});
|