@scality/core-ui 0.163.0 → 0.164.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/dist/components/barchartv2/Barchart.component.d.ts +9 -1
- package/dist/components/barchartv2/Barchart.component.d.ts.map +1 -1
- package/dist/components/barchartv2/Barchart.component.js +13 -6
- package/dist/components/barchartv2/utils.d.ts +3 -3
- package/dist/components/barchartv2/utils.d.ts.map +1 -1
- package/dist/components/barchartv2/utils.js +42 -20
- package/dist/next.d.ts +1 -0
- package/dist/next.d.ts.map +1 -1
- package/dist/next.js +1 -0
- package/package.json +1 -1
- package/src/lib/components/accordion/Accordion.test.tsx +1 -1
- package/src/lib/components/barchartv2/Barchart.component.test.tsx +39 -14
- package/src/lib/components/barchartv2/Barchart.component.tsx +41 -6
- package/src/lib/components/barchartv2/utils.test.ts +10 -2
- package/src/lib/components/barchartv2/utils.ts +50 -14
- package/src/lib/components/healthselectorv2/HealthSelector.component.test.tsx +7 -7
- package/src/lib/components/infomessage/InfoMessageUtils.test.tsx +0 -1
- package/src/lib/components/inlineinput/InlineInput.test.tsx +10 -7
- package/src/lib/components/inputlist/InputList.test.tsx +1 -2
- package/src/lib/components/linetemporalchart/ChartUtil.test.ts +5 -4
- package/src/lib/components/selectv2/selectv2.test.tsx +8 -4
- package/src/lib/components/tablev2/TableSync.test.tsx +2 -3
- package/src/lib/components/tablev2/TableUtils.test.ts +6 -3
- package/src/lib/components/tablev2/Tablev2.test.tsx +2 -3
- package/src/lib/components/toggle/Toggle.test.tsx +1 -1
- package/src/lib/next.ts +1 -0
- package/stories/BarChart/barchart.stories.tsx +96 -5
- package/tsconfig.json +0 -1
|
@@ -37,17 +37,25 @@ export type BarchartSortFn<T extends BarchartBars> = (pointA: Record<T[number]['
|
|
|
37
37
|
}) => 1 | -1 | 0;
|
|
38
38
|
export type BarchartProps<T extends BarchartBars> = {
|
|
39
39
|
type: 'category' | TimeType;
|
|
40
|
-
bars
|
|
40
|
+
bars?: T;
|
|
41
41
|
tooltip?: BarchartTooltipFn<T>;
|
|
42
42
|
defaultSort?: BarchartSortFn<T>;
|
|
43
43
|
unitRange?: UnitRange;
|
|
44
44
|
helpTooltip?: string;
|
|
45
45
|
stacked?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Sort the bars by default or by legend order
|
|
48
|
+
* legend will sort the bars by the order of the colorSet property of the ChartLegendWrapper component
|
|
49
|
+
* default will sort the bars by average values in descending order (biggest values will be at bottom)
|
|
50
|
+
* @default 'default'
|
|
51
|
+
*/
|
|
52
|
+
stackedBarSort?: 'default' | 'legend';
|
|
46
53
|
title?: string;
|
|
47
54
|
secondaryTitle?: string;
|
|
48
55
|
rightTitle?: React.ReactNode;
|
|
49
56
|
height?: number;
|
|
50
57
|
isLoading?: boolean;
|
|
58
|
+
isError?: boolean;
|
|
51
59
|
};
|
|
52
60
|
export declare const Barchart: <T extends BarchartBars>(props: BarchartProps<T>) => import("react/jsx-runtime").JSX.Element;
|
|
53
61
|
//# sourceMappingURL=Barchart.component.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Barchart.component.d.ts","sourceRoot":"","sources":["../../../src/lib/components/barchartv2/Barchart.component.tsx"],"names":[],"mappings":"AAoBA,OAAO,EAAwB,SAAS,EAAgB,MAAM,SAAS,CAAC;AAkBxE,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE;QACT,SAAS,EAAE,IAAI,CAAC;QAChB,OAAO,EAAE,IAAI,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH,CAAC;AACF,MAAM,MAAM,KAAK,GAAG;IAClB,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,SAAS;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,GAAG,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC;CACvE,EAAE,CAAC;AAEJ,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,YAAY,IAAI,CAAC,YAAY,EAAE;IACrE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,MAAM,EAAE;QAAE,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;CAC5E,KAAK,KAAK,CAAC,SAAS,CAAC;AAEtB,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,YAAY,IAAI,CACnD,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,GAAG;IAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,EAC1E,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,GAAG;IAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,KACvE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;AAEhB,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,YAAY,IAAI;IAClD,IAAI,EAAE,UAAU,GAAG,QAAQ,CAAC;IAC5B,IAAI,EAAE,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"Barchart.component.d.ts","sourceRoot":"","sources":["../../../src/lib/components/barchartv2/Barchart.component.tsx"],"names":[],"mappings":"AAoBA,OAAO,EAAwB,SAAS,EAAgB,MAAM,SAAS,CAAC;AAkBxE,MAAM,MAAM,QAAQ,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE;QACT,SAAS,EAAE,IAAI,CAAC;QAChB,OAAO,EAAE,IAAI,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH,CAAC;AACF,MAAM,MAAM,KAAK,GAAG;IAClB,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,SAAS;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,GAAG,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC;CACvE,EAAE,CAAC;AAEJ,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,YAAY,IAAI,CAAC,YAAY,EAAE;IACrE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,MAAM,EAAE;QAAE,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;CAC5E,KAAK,KAAK,CAAC,SAAS,CAAC;AAEtB,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,YAAY,IAAI,CACnD,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,GAAG;IAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,EAC1E,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,GAAG;IAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,KACvE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;AAEhB,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,YAAY,IAAI;IAClD,IAAI,EAAE,UAAU,GAAG,QAAQ,CAAC;IAC5B,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,OAAO,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC;IAC/B,WAAW,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;IAChC,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAgIF,eAAO,MAAM,QAAQ,GAAI,CAAC,SAAS,YAAY,SAAS,aAAa,CAAC,CAAC,CAAC,4CAwIvE,CAAC"}
|
|
@@ -14,7 +14,7 @@ import { useChartLegend } from '../chartlegend/ChartLegendWrapper';
|
|
|
14
14
|
const CHART_CONSTANTS = {
|
|
15
15
|
TICK_WIDTH_OFFSET: 5,
|
|
16
16
|
BAR_SIZE: 12,
|
|
17
|
-
MIN_POINT_SIZE:
|
|
17
|
+
MIN_POINT_SIZE: 3,
|
|
18
18
|
DEFAULT_HEIGHT: 200,
|
|
19
19
|
CHART_MARGIN: {
|
|
20
20
|
left: 0,
|
|
@@ -47,6 +47,13 @@ const ChartHeader = ({ title, secondaryTitle, helpTooltip, rightTitle, }) => {
|
|
|
47
47
|
marginLeft: spacing.r8,
|
|
48
48
|
}, children: secondaryTitle }))] }), rightTitle && _jsx(Text, { children: rightTitle })] }));
|
|
49
49
|
};
|
|
50
|
+
const Error = ({ height }) => {
|
|
51
|
+
return (_jsx(Box, { height: height, style: {
|
|
52
|
+
alignItems: 'center',
|
|
53
|
+
justifyContent: 'center',
|
|
54
|
+
display: 'flex',
|
|
55
|
+
}, children: _jsx(Text, { children: "Chart data is not available" }) }));
|
|
56
|
+
};
|
|
50
57
|
const Loading = ({ height }) => {
|
|
51
58
|
return (_jsx(Box, { height: height, style: {
|
|
52
59
|
alignItems: 'center',
|
|
@@ -59,19 +66,19 @@ export const Barchart = (props) => {
|
|
|
59
66
|
const theme = useTheme();
|
|
60
67
|
const { getColor } = useChartLegend();
|
|
61
68
|
const [hoveredValue, setHoveredValue] = useState();
|
|
62
|
-
const { height = CHART_CONSTANTS.DEFAULT_HEIGHT, bars, type = 'category', unitRange, stacked, defaultSort, tooltip, title, secondaryTitle, helpTooltip, rightTitle, isLoading, } = props;
|
|
69
|
+
const { height = CHART_CONSTANTS.DEFAULT_HEIGHT, bars, type = 'category', unitRange, stacked, stackedBarSort = 'default', defaultSort, tooltip, title, secondaryTitle, helpTooltip, rightTitle, isLoading, isError, } = props;
|
|
63
70
|
// Create colorSet from ChartLegendWrapper
|
|
64
|
-
const colorSet = bars.reduce((acc, bar) => {
|
|
71
|
+
const colorSet = bars === null || bars === void 0 ? void 0 : bars.reduce((acc, bar) => {
|
|
65
72
|
const color = getColor(bar.label);
|
|
66
73
|
if (color) {
|
|
67
74
|
acc[bar.label] = color;
|
|
68
75
|
}
|
|
69
76
|
return acc;
|
|
70
77
|
}, {});
|
|
71
|
-
const { rechartsBars, unitLabel, roundReferenceValue, rechartsData } = useChartData(bars, type, colorSet, stacked, defaultSort, unitRange);
|
|
72
|
-
return (_jsxs(Stack, { direction: "vertical", gap: "r8", children: [_jsx(ChartHeader, { title: title, secondaryTitle: secondaryTitle, helpTooltip: helpTooltip, rightTitle: rightTitle }), isLoading ? (_jsx(Loading, { height: height })) : (_jsx(StyledResponsiveContainer, { width: "100%", height: height, children: _jsxs(BarChart, { data: rechartsData, accessibilityLayer: true, barSize: CHART_CONSTANTS.BAR_SIZE, height: height, margin: CHART_CONSTANTS.CHART_MARGIN, children: [_jsx(CartesianGrid, { vertical: false, horizontal: false, fill: theme.backgroundLevel1 }), rechartsBars.map((bar) => {
|
|
78
|
+
const { rechartsBars, unitLabel, roundReferenceValue, rechartsData } = useChartData(bars || [], type, colorSet || {}, stacked, defaultSort, unitRange, stackedBarSort);
|
|
79
|
+
return (_jsxs(Stack, { direction: "vertical", gap: "r8", children: [_jsx(ChartHeader, { title: title, secondaryTitle: secondaryTitle, helpTooltip: helpTooltip, rightTitle: rightTitle }), isError || (!bars && !isLoading) ? (_jsx(Error, { height: height })) : isLoading ? (_jsx(Loading, { height: height })) : (_jsx(StyledResponsiveContainer, { width: "100%", height: height, children: _jsxs(BarChart, { data: rechartsData, accessibilityLayer: true, barSize: CHART_CONSTANTS.BAR_SIZE, height: height, margin: CHART_CONSTANTS.CHART_MARGIN, children: [_jsx(CartesianGrid, { vertical: false, horizontal: false, fill: theme.backgroundLevel1 }), rechartsBars.map((bar) => {
|
|
73
80
|
const { fill, dataKey, stackId } = bar;
|
|
74
|
-
return (_jsx(Bar, { dataKey: dataKey, fill: chartColors[fill] || fill, minPointSize: CHART_CONSTANTS.MIN_POINT_SIZE, stackId: stackId, onMouseOver: () => setHoveredValue(dataKey), onMouseLeave: () => setHoveredValue(undefined) }, dataKey));
|
|
81
|
+
return (_jsx(Bar, { dataKey: dataKey, fill: chartColors[fill] || fill, minPointSize: stacked ? 0 : CHART_CONSTANTS.MIN_POINT_SIZE, stackId: stackId, onMouseOver: () => setHoveredValue(dataKey), onMouseLeave: () => setHoveredValue(undefined) }, dataKey));
|
|
75
82
|
}), _jsx(YAxis, { tickCount: 1, unit: ` ${unitLabel}`, domain: [0, roundReferenceValue], tickFormatter: (value) => new Intl.NumberFormat('fr-FR').format(value.toFixed(0)) // Add a space as thousand separator
|
|
76
83
|
, axisLine: false, tick: {
|
|
77
84
|
fill: theme.textSecondary,
|
|
@@ -38,7 +38,7 @@ export declare const applySortingToData: <T extends BarchartBars>(data: {
|
|
|
38
38
|
* @param type - The chart type (category or time)
|
|
39
39
|
* @returns Recharts data format
|
|
40
40
|
*/
|
|
41
|
-
export declare const formatPrometheusDataToRechartsDataAndBars: <T extends BarchartBars>(bars: T, type: BarchartProps<T>["type"], colorSet: Record<string, ChartColors | string>, stacked?: boolean, defaultSort?: BarchartProps<T>["defaultSort"]) => {
|
|
41
|
+
export declare const formatPrometheusDataToRechartsDataAndBars: <T extends BarchartBars>(bars: T, type: BarchartProps<T>["type"], colorSet: Record<string, ChartColors | string>, stacked?: boolean, defaultSort?: BarchartProps<T>["defaultSort"], legendOrder?: string[]) => {
|
|
42
42
|
data: {
|
|
43
43
|
[key: string]: string | number;
|
|
44
44
|
}[];
|
|
@@ -76,7 +76,7 @@ export declare const sortStackedBars: (rechartsBars: {
|
|
|
76
76
|
stackId?: string;
|
|
77
77
|
}[], data: {
|
|
78
78
|
[key: string]: string | number;
|
|
79
|
-
}[], stacked?: boolean) => {
|
|
79
|
+
}[], stacked?: boolean, legendOrder?: string[]) => {
|
|
80
80
|
dataKey: string;
|
|
81
81
|
fill: string;
|
|
82
82
|
stackId?: string;
|
|
@@ -105,7 +105,7 @@ export declare const filterChartDataAndBarsByLegendSelection: (data: {
|
|
|
105
105
|
stackId?: string;
|
|
106
106
|
}[];
|
|
107
107
|
};
|
|
108
|
-
export declare const useChartData: <T extends BarchartBars>(bars: T, type: BarchartProps<T>["type"], colorSet: Record<string, ChartColors | string>, stacked?: boolean, defaultSort?: BarchartProps<T>["defaultSort"], unitRange?: UnitRange) => {
|
|
108
|
+
export declare const useChartData: <T extends BarchartBars>(bars: T, type: BarchartProps<T>["type"], colorSet: Record<string, ChartColors | string>, stacked?: boolean, defaultSort?: BarchartProps<T>["defaultSort"], unitRange?: UnitRange, stackedBarSort?: "default" | "legend") => {
|
|
109
109
|
rechartsBars: {
|
|
110
110
|
dataKey: string;
|
|
111
111
|
fill: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/lib/components/barchartv2/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,aAAa,EACb,YAAY,EACZ,iBAAiB,EAClB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,EAAe,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAG7D,eAAO,MAAM,sBAAsB,UAAW,MAAM,KAAG,
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/lib/components/barchartv2/utils.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,13 +9,17 @@ export const getRoundReferenceValue = (value) => {
|
|
|
9
9
|
// Normalized value between 1 and 10
|
|
10
10
|
const normalized = value / magnitude;
|
|
11
11
|
// Round to nice numbers based on normalized value
|
|
12
|
+
let result;
|
|
12
13
|
if (normalized <= 1)
|
|
13
|
-
|
|
14
|
-
if (normalized <= 2.5)
|
|
15
|
-
|
|
16
|
-
if (normalized <= 5)
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
result = magnitude;
|
|
15
|
+
else if (normalized <= 2.5)
|
|
16
|
+
result = 2.5 * magnitude;
|
|
17
|
+
else if (normalized <= 5)
|
|
18
|
+
result = 5 * magnitude;
|
|
19
|
+
else
|
|
20
|
+
result = 10 * magnitude;
|
|
21
|
+
// Ensure minimum value of 5 for better chart appearance
|
|
22
|
+
return Math.max(result, 5);
|
|
19
23
|
};
|
|
20
24
|
export const getMaxBarValue = (data, stacked) => {
|
|
21
25
|
const values = data.map((item) => {
|
|
@@ -34,9 +38,9 @@ export const getMaxBarValue = (data, stacked) => {
|
|
|
34
38
|
.filter((key) => key !== 'category')
|
|
35
39
|
.map((key) => Number(item[key]));
|
|
36
40
|
// Get the max value among the values in the object (corresponding to one bar)
|
|
37
|
-
return Math.max(...numberValues);
|
|
41
|
+
return Math.max(...numberValues, 0); // Ensure we don't get -Infinity
|
|
38
42
|
});
|
|
39
|
-
return Math.max(...values);
|
|
43
|
+
return Math.max(...values, 0);
|
|
40
44
|
};
|
|
41
45
|
/**
|
|
42
46
|
* Generates time ranges between start and end dates based on the given interval
|
|
@@ -47,9 +51,6 @@ export const getMaxBarValue = (data, stacked) => {
|
|
|
47
51
|
*/
|
|
48
52
|
const generateTimeRanges = (startDate, endDate, interval) => {
|
|
49
53
|
const ranges = [];
|
|
50
|
-
if (!startDate || !endDate || !interval) {
|
|
51
|
-
return ranges;
|
|
52
|
-
}
|
|
53
54
|
let currentDate = new Date(startDate.getTime());
|
|
54
55
|
while (currentDate.getTime() <= endDate.getTime()) {
|
|
55
56
|
const rangeEnd = new Date(currentDate.getTime() + interval);
|
|
@@ -198,7 +199,7 @@ const getRechartsBarsAndBarDataKeys = (bars, colorSet, stacked) => {
|
|
|
198
199
|
* @param type - The chart type (category or time)
|
|
199
200
|
* @returns Recharts data format
|
|
200
201
|
*/
|
|
201
|
-
export const formatPrometheusDataToRechartsDataAndBars = (bars, type, colorSet, stacked, defaultSort) => {
|
|
202
|
+
export const formatPrometheusDataToRechartsDataAndBars = (bars, type, colorSet, stacked, defaultSort, legendOrder) => {
|
|
202
203
|
const { rechartsBars, barDataKeys } = getRechartsBarsAndBarDataKeys(bars, colorSet, stacked);
|
|
203
204
|
let data = type !== 'category' && type.type === 'time'
|
|
204
205
|
? transformTimeData(bars, type, barDataKeys)
|
|
@@ -206,7 +207,7 @@ export const formatPrometheusDataToRechartsDataAndBars = (bars, type, colorSet,
|
|
|
206
207
|
if (type === 'category' && defaultSort) {
|
|
207
208
|
data = applySortingToData(data, barDataKeys, defaultSort);
|
|
208
209
|
}
|
|
209
|
-
const sortedRechartsBars = sortStackedBars(rechartsBars, data, stacked);
|
|
210
|
+
const sortedRechartsBars = sortStackedBars(rechartsBars, data, stacked, legendOrder);
|
|
210
211
|
return {
|
|
211
212
|
rechartsBars: sortedRechartsBars,
|
|
212
213
|
data,
|
|
@@ -217,7 +218,7 @@ export const computeUnitLabelAndRoundReferenceValue = (data, maxValue, unitRange
|
|
|
217
218
|
const roundReferenceValue = getRoundReferenceValue(maxValue);
|
|
218
219
|
return { unitLabel: '', roundReferenceValue, rechartsData: data };
|
|
219
220
|
}
|
|
220
|
-
const { valueBase, unitLabel } = getUnitLabel(unitRange
|
|
221
|
+
const { valueBase, unitLabel } = getUnitLabel(unitRange, maxValue);
|
|
221
222
|
const topValue = Math.ceil(maxValue / valueBase / 10) * 10;
|
|
222
223
|
const roundReferenceValue = getRoundReferenceValue(topValue);
|
|
223
224
|
const rechartsData = data.map((dataPoint) => {
|
|
@@ -259,12 +260,31 @@ export function getUnitLabel(unitRange, maxValue) {
|
|
|
259
260
|
unitLabel: unitRange[index - 1].label,
|
|
260
261
|
};
|
|
261
262
|
}
|
|
262
|
-
// Sort stacked bars by their average values in descending order
|
|
263
|
-
// This ensures the largest bars appear at the bottom of the stack
|
|
264
|
-
export const sortStackedBars = (rechartsBars, data, stacked) => {
|
|
263
|
+
// Sort stacked bars by their average values in descending order or by legend order
|
|
264
|
+
// This ensures the largest bars appear at the bottom of the stack (default) or follow legend order
|
|
265
|
+
export const sortStackedBars = (rechartsBars, data, stacked, legendOrder) => {
|
|
265
266
|
if (!stacked) {
|
|
266
267
|
return rechartsBars;
|
|
267
268
|
}
|
|
269
|
+
// If legend order is provided, sort by legend order
|
|
270
|
+
if (legendOrder && legendOrder.length > 0) {
|
|
271
|
+
return [...rechartsBars].sort((a, b) => {
|
|
272
|
+
const indexA = legendOrder.indexOf(a.dataKey);
|
|
273
|
+
const indexB = legendOrder.indexOf(b.dataKey);
|
|
274
|
+
// If both items are in legend order, sort by their position
|
|
275
|
+
if (indexA !== -1 && indexB !== -1) {
|
|
276
|
+
return indexA - indexB;
|
|
277
|
+
}
|
|
278
|
+
// If only one item is in legend order, prioritize it
|
|
279
|
+
if (indexA !== -1)
|
|
280
|
+
return -1;
|
|
281
|
+
if (indexB !== -1)
|
|
282
|
+
return 1;
|
|
283
|
+
// If neither is in legend order, maintain original order
|
|
284
|
+
return 0;
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
// Default behavior: sort by average values
|
|
268
288
|
const barAverages = rechartsBars.map((bar) => {
|
|
269
289
|
const values = data
|
|
270
290
|
.map((item) => Number(item[bar.dataKey]) || 0)
|
|
@@ -321,9 +341,11 @@ export const filterChartDataAndBarsByLegendSelection = (data, rechartsBars, sele
|
|
|
321
341
|
});
|
|
322
342
|
return { filteredData, filteredRechartsBars };
|
|
323
343
|
};
|
|
324
|
-
export const useChartData = (bars, type, colorSet, stacked, defaultSort, unitRange) => {
|
|
325
|
-
const { selectedResources } = useChartLegend();
|
|
326
|
-
|
|
344
|
+
export const useChartData = (bars, type, colorSet, stacked, defaultSort, unitRange, stackedBarSort) => {
|
|
345
|
+
const { selectedResources, listResources } = useChartLegend();
|
|
346
|
+
// Get legend order when stackedBarSort is 'legend'
|
|
347
|
+
const legendOrder = stackedBarSort === 'legend' ? listResources() : undefined;
|
|
348
|
+
const { data, rechartsBars } = formatPrometheusDataToRechartsDataAndBars(bars, type, colorSet, stacked, defaultSort, legendOrder);
|
|
327
349
|
// Filter both data and bars to only include selected resources for accurate maxValue calculation
|
|
328
350
|
const { filteredData, filteredRechartsBars } = filterChartDataAndBarsByLegendSelection(data, rechartsBars, selectedResources);
|
|
329
351
|
const maxValue = getMaxBarValue(filteredData, stacked);
|
package/dist/next.d.ts
CHANGED
|
@@ -15,4 +15,5 @@ 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 { ChartLegendWrapper } from './components/chartlegend/ChartLegendWrapper';
|
|
18
|
+
export { ChartLegend } from './components/chartlegend/ChartLegend';
|
|
18
19
|
//# sourceMappingURL=next.d.ts.map
|
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,kBAAkB,EAAE,MAAM,6CAA6C,CAAC"}
|
|
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,kBAAkB,EAAE,MAAM,6CAA6C,CAAC;AACjF,OAAO,EAAE,WAAW,EAAE,MAAM,sCAAsC,CAAC"}
|
package/dist/next.js
CHANGED
|
@@ -15,3 +15,4 @@ 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 { ChartLegendWrapper } from './components/chartlegend/ChartLegendWrapper';
|
|
18
|
+
export { ChartLegend } from './components/chartlegend/ChartLegend';
|
package/package.json
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { render, screen, waitFor } from '@testing-library/react';
|
|
2
|
-
import React from 'react';
|
|
3
2
|
import { getWrapper } from '../../testUtils';
|
|
4
3
|
import { Barchart } from './Barchart.component';
|
|
5
4
|
import { ChartLegendWrapper } from '../chartlegend/ChartLegendWrapper';
|
|
@@ -15,7 +14,7 @@ jest.mock('recharts', () => {
|
|
|
15
14
|
...OriginalResponsiveContainerModule,
|
|
16
15
|
ResponsiveContainer: ({ height, children }) => (
|
|
17
16
|
<OriginalResponsiveContainerModule.ResponsiveContainer
|
|
18
|
-
|
|
17
|
+
aspect={3}
|
|
19
18
|
height={300}
|
|
20
19
|
data-testid="responsive-container"
|
|
21
20
|
>
|
|
@@ -93,6 +92,43 @@ describe('Barchart', () => {
|
|
|
93
92
|
expect(screen.getByText('Sat06Jul')).toBeInTheDocument();
|
|
94
93
|
expect(screen.getByText('Sun07Jul')).toBeInTheDocument();
|
|
95
94
|
});
|
|
95
|
+
it('should render the Barchart component with error state', async () => {
|
|
96
|
+
const { Wrapper } = getWrapper();
|
|
97
|
+
render(
|
|
98
|
+
<Wrapper>
|
|
99
|
+
<ChartLegendWrapper colorSet={testColorSet}>
|
|
100
|
+
<Barchart type="category" bars={[]} isError />
|
|
101
|
+
</ChartLegendWrapper>
|
|
102
|
+
</Wrapper>,
|
|
103
|
+
);
|
|
104
|
+
expect(
|
|
105
|
+
screen.getByText('Chart data is not available'),
|
|
106
|
+
).toBeInTheDocument();
|
|
107
|
+
});
|
|
108
|
+
it('should render the Barchart component with loading state', async () => {
|
|
109
|
+
const { Wrapper } = getWrapper();
|
|
110
|
+
render(
|
|
111
|
+
<Wrapper>
|
|
112
|
+
<ChartLegendWrapper colorSet={testColorSet}>
|
|
113
|
+
<Barchart type="category" bars={[]} isLoading />
|
|
114
|
+
</ChartLegendWrapper>
|
|
115
|
+
</Wrapper>,
|
|
116
|
+
);
|
|
117
|
+
expect(screen.getByText('Loading Chart Data...')).toBeInTheDocument();
|
|
118
|
+
});
|
|
119
|
+
it('should render the Barchart component with undefined bars', async () => {
|
|
120
|
+
const { Wrapper } = getWrapper();
|
|
121
|
+
render(
|
|
122
|
+
<Wrapper>
|
|
123
|
+
<ChartLegendWrapper colorSet={testColorSet}>
|
|
124
|
+
<Barchart type="category" bars={undefined} />
|
|
125
|
+
</ChartLegendWrapper>
|
|
126
|
+
</Wrapper>,
|
|
127
|
+
);
|
|
128
|
+
expect(
|
|
129
|
+
screen.getByText('Chart data is not available'),
|
|
130
|
+
).toBeInTheDocument();
|
|
131
|
+
});
|
|
96
132
|
});
|
|
97
133
|
|
|
98
134
|
describe('Time data', () => {
|
|
@@ -278,7 +314,7 @@ describe('Barchart', () => {
|
|
|
278
314
|
const { Wrapper } = getWrapper();
|
|
279
315
|
render(
|
|
280
316
|
<Wrapper>
|
|
281
|
-
<ChartLegendWrapper colorSet={testColorSet}>
|
|
317
|
+
<ChartLegendWrapper colorSet={{ ...testColorSet, Failed: 'red' }}>
|
|
282
318
|
<Barchart type="category" bars={testStackedBars} stacked={true} />
|
|
283
319
|
</ChartLegendWrapper>
|
|
284
320
|
</Wrapper>,
|
|
@@ -326,17 +362,6 @@ describe('Barchart', () => {
|
|
|
326
362
|
expect(categories[2]).toHaveTextContent('category1'); // 10 (lowest)
|
|
327
363
|
});
|
|
328
364
|
|
|
329
|
-
it('should render the Barchart component with loading state', () => {
|
|
330
|
-
const { Wrapper } = getWrapper();
|
|
331
|
-
render(
|
|
332
|
-
<Wrapper>
|
|
333
|
-
<ChartLegendWrapper colorSet={testColorSet}>
|
|
334
|
-
<Barchart type="category" bars={[]} isLoading />
|
|
335
|
-
</ChartLegendWrapper>
|
|
336
|
-
</Wrapper>,
|
|
337
|
-
);
|
|
338
|
-
expect(screen.getByText('Loading Chart Data...')).toBeInTheDocument();
|
|
339
|
-
});
|
|
340
365
|
it('should render header with title, secondary title, right title and help tooltip', async () => {
|
|
341
366
|
const { Wrapper } = getWrapper();
|
|
342
367
|
render(
|
|
@@ -24,7 +24,7 @@ import { useChartLegend } from '../chartlegend/ChartLegendWrapper';
|
|
|
24
24
|
const CHART_CONSTANTS = {
|
|
25
25
|
TICK_WIDTH_OFFSET: 5,
|
|
26
26
|
BAR_SIZE: 12,
|
|
27
|
-
MIN_POINT_SIZE:
|
|
27
|
+
MIN_POINT_SIZE: 3,
|
|
28
28
|
DEFAULT_HEIGHT: 200,
|
|
29
29
|
CHART_MARGIN: {
|
|
30
30
|
left: 0,
|
|
@@ -70,17 +70,25 @@ export type BarchartSortFn<T extends BarchartBars> = (
|
|
|
70
70
|
|
|
71
71
|
export type BarchartProps<T extends BarchartBars> = {
|
|
72
72
|
type: 'category' | TimeType;
|
|
73
|
-
bars
|
|
73
|
+
bars?: T;
|
|
74
74
|
tooltip?: BarchartTooltipFn<T>;
|
|
75
75
|
defaultSort?: BarchartSortFn<T>;
|
|
76
76
|
unitRange?: UnitRange;
|
|
77
77
|
helpTooltip?: string;
|
|
78
78
|
stacked?: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Sort the bars by default or by legend order
|
|
81
|
+
* legend will sort the bars by the order of the colorSet property of the ChartLegendWrapper component
|
|
82
|
+
* default will sort the bars by average values in descending order (biggest values will be at bottom)
|
|
83
|
+
* @default 'default'
|
|
84
|
+
*/
|
|
85
|
+
stackedBarSort?: 'default' | 'legend';
|
|
79
86
|
title?: string;
|
|
80
87
|
secondaryTitle?: string;
|
|
81
88
|
rightTitle?: React.ReactNode;
|
|
82
89
|
height?: number;
|
|
83
90
|
isLoading?: boolean;
|
|
91
|
+
isError?: boolean;
|
|
84
92
|
};
|
|
85
93
|
|
|
86
94
|
interface CustomTickProps {
|
|
@@ -177,6 +185,21 @@ const ChartHeader = ({
|
|
|
177
185
|
);
|
|
178
186
|
};
|
|
179
187
|
|
|
188
|
+
const Error = ({ height }: { height: number }) => {
|
|
189
|
+
return (
|
|
190
|
+
<Box
|
|
191
|
+
height={height}
|
|
192
|
+
style={{
|
|
193
|
+
alignItems: 'center',
|
|
194
|
+
justifyContent: 'center',
|
|
195
|
+
display: 'flex',
|
|
196
|
+
}}
|
|
197
|
+
>
|
|
198
|
+
<Text>Chart data is not available</Text>
|
|
199
|
+
</Box>
|
|
200
|
+
);
|
|
201
|
+
};
|
|
202
|
+
|
|
180
203
|
const Loading = ({ height }: { height: number }) => {
|
|
181
204
|
return (
|
|
182
205
|
<Box
|
|
@@ -205,6 +228,7 @@ export const Barchart = <T extends BarchartBars>(props: BarchartProps<T>) => {
|
|
|
205
228
|
type = 'category',
|
|
206
229
|
unitRange,
|
|
207
230
|
stacked,
|
|
231
|
+
stackedBarSort = 'default',
|
|
208
232
|
defaultSort,
|
|
209
233
|
tooltip,
|
|
210
234
|
title,
|
|
@@ -212,10 +236,11 @@ export const Barchart = <T extends BarchartBars>(props: BarchartProps<T>) => {
|
|
|
212
236
|
helpTooltip,
|
|
213
237
|
rightTitle,
|
|
214
238
|
isLoading,
|
|
239
|
+
isError,
|
|
215
240
|
} = props;
|
|
216
241
|
|
|
217
242
|
// Create colorSet from ChartLegendWrapper
|
|
218
|
-
const colorSet = bars
|
|
243
|
+
const colorSet = bars?.reduce(
|
|
219
244
|
(acc, bar) => {
|
|
220
245
|
const color = getColor(bar.label);
|
|
221
246
|
if (color) {
|
|
@@ -227,7 +252,15 @@ export const Barchart = <T extends BarchartBars>(props: BarchartProps<T>) => {
|
|
|
227
252
|
);
|
|
228
253
|
|
|
229
254
|
const { rechartsBars, unitLabel, roundReferenceValue, rechartsData } =
|
|
230
|
-
useChartData(
|
|
255
|
+
useChartData(
|
|
256
|
+
bars || [],
|
|
257
|
+
type,
|
|
258
|
+
colorSet || {},
|
|
259
|
+
stacked,
|
|
260
|
+
defaultSort,
|
|
261
|
+
unitRange,
|
|
262
|
+
stackedBarSort,
|
|
263
|
+
);
|
|
231
264
|
|
|
232
265
|
return (
|
|
233
266
|
<Stack direction="vertical" gap="r8">
|
|
@@ -237,7 +270,9 @@ export const Barchart = <T extends BarchartBars>(props: BarchartProps<T>) => {
|
|
|
237
270
|
helpTooltip={helpTooltip}
|
|
238
271
|
rightTitle={rightTitle}
|
|
239
272
|
/>
|
|
240
|
-
{isLoading ? (
|
|
273
|
+
{isError || (!bars && !isLoading) ? (
|
|
274
|
+
<Error height={height} />
|
|
275
|
+
) : isLoading ? (
|
|
241
276
|
<Loading height={height} />
|
|
242
277
|
) : (
|
|
243
278
|
<StyledResponsiveContainer width="100%" height={height}>
|
|
@@ -260,7 +295,7 @@ export const Barchart = <T extends BarchartBars>(props: BarchartProps<T>) => {
|
|
|
260
295
|
key={dataKey}
|
|
261
296
|
dataKey={dataKey}
|
|
262
297
|
fill={chartColors[fill] || fill}
|
|
263
|
-
minPointSize={CHART_CONSTANTS.MIN_POINT_SIZE}
|
|
298
|
+
minPointSize={stacked ? 0 : CHART_CONSTANTS.MIN_POINT_SIZE}
|
|
264
299
|
stackId={stackId}
|
|
265
300
|
onMouseOver={() => setHoveredValue(dataKey)}
|
|
266
301
|
onMouseLeave={() => setHoveredValue(undefined)}
|
|
@@ -482,8 +482,8 @@ describe('applySortingToData', () => {
|
|
|
482
482
|
|
|
483
483
|
describe('getRoundReferenceValue', () => {
|
|
484
484
|
it('should return appropriate rounded values', () => {
|
|
485
|
-
expect(getRoundReferenceValue(1)).toBe(
|
|
486
|
-
expect(getRoundReferenceValue(2)).toBe(
|
|
485
|
+
expect(getRoundReferenceValue(1)).toBe(5);
|
|
486
|
+
expect(getRoundReferenceValue(2)).toBe(5);
|
|
487
487
|
expect(getRoundReferenceValue(3)).toBe(5);
|
|
488
488
|
expect(getRoundReferenceValue(7)).toBe(10);
|
|
489
489
|
expect(getRoundReferenceValue(15)).toBe(25);
|
|
@@ -717,6 +717,14 @@ describe('sortStackedBars', () => {
|
|
|
717
717
|
{ dataKey: 'bar1', fill: 'blue' },
|
|
718
718
|
]);
|
|
719
719
|
});
|
|
720
|
+
it('should sort bars by legend order when stacked is true and legendOrder is provided', () => {
|
|
721
|
+
const result = sortStackedBars(bars, data, true, ['bar3', 'bar2', 'bar1']);
|
|
722
|
+
expect(result).toEqual([
|
|
723
|
+
{ dataKey: 'bar3', fill: 'green' },
|
|
724
|
+
{ dataKey: 'bar2', fill: 'red' },
|
|
725
|
+
{ dataKey: 'bar1', fill: 'blue' },
|
|
726
|
+
]);
|
|
727
|
+
});
|
|
720
728
|
it('should not sort bars when stacked is false', () => {
|
|
721
729
|
const result = sortStackedBars(bars, data, false);
|
|
722
730
|
expect(result).toEqual([
|
|
@@ -18,10 +18,14 @@ export const getRoundReferenceValue = (value: number): number => {
|
|
|
18
18
|
const normalized = value / magnitude;
|
|
19
19
|
|
|
20
20
|
// Round to nice numbers based on normalized value
|
|
21
|
-
|
|
22
|
-
if (normalized <=
|
|
23
|
-
if (normalized <= 5)
|
|
24
|
-
|
|
21
|
+
let result: number;
|
|
22
|
+
if (normalized <= 1) result = magnitude;
|
|
23
|
+
else if (normalized <= 2.5) result = 2.5 * magnitude;
|
|
24
|
+
else if (normalized <= 5) result = 5 * magnitude;
|
|
25
|
+
else result = 10 * magnitude;
|
|
26
|
+
|
|
27
|
+
// Ensure minimum value of 5 for better chart appearance
|
|
28
|
+
return Math.max(result, 5);
|
|
25
29
|
};
|
|
26
30
|
|
|
27
31
|
export const getMaxBarValue = (
|
|
@@ -46,9 +50,9 @@ export const getMaxBarValue = (
|
|
|
46
50
|
.filter((key) => key !== 'category')
|
|
47
51
|
.map((key) => Number(item[key]));
|
|
48
52
|
// Get the max value among the values in the object (corresponding to one bar)
|
|
49
|
-
return Math.max(...numberValues);
|
|
53
|
+
return Math.max(...numberValues, 0); // Ensure we don't get -Infinity
|
|
50
54
|
});
|
|
51
|
-
return Math.max(...values);
|
|
55
|
+
return Math.max(...values, 0);
|
|
52
56
|
};
|
|
53
57
|
|
|
54
58
|
/**
|
|
@@ -64,9 +68,6 @@ const generateTimeRanges = (
|
|
|
64
68
|
interval: number,
|
|
65
69
|
): { start: Date; end: Date }[] => {
|
|
66
70
|
const ranges: { start: Date; end: Date }[] = [];
|
|
67
|
-
if (!startDate || !endDate || !interval) {
|
|
68
|
-
return ranges;
|
|
69
|
-
}
|
|
70
71
|
|
|
71
72
|
let currentDate = new Date(startDate.getTime());
|
|
72
73
|
while (currentDate.getTime() <= endDate.getTime()) {
|
|
@@ -288,6 +289,7 @@ export const formatPrometheusDataToRechartsDataAndBars = <
|
|
|
288
289
|
colorSet: Record<string, ChartColors | string>,
|
|
289
290
|
stacked?: boolean,
|
|
290
291
|
defaultSort?: BarchartProps<T>['defaultSort'],
|
|
292
|
+
legendOrder?: string[],
|
|
291
293
|
): {
|
|
292
294
|
data: { [key: string]: string | number }[];
|
|
293
295
|
rechartsBars: { dataKey: string; fill: string; stackId?: string }[];
|
|
@@ -307,7 +309,12 @@ export const formatPrometheusDataToRechartsDataAndBars = <
|
|
|
307
309
|
data = applySortingToData(data, barDataKeys, defaultSort);
|
|
308
310
|
}
|
|
309
311
|
|
|
310
|
-
const sortedRechartsBars = sortStackedBars(
|
|
312
|
+
const sortedRechartsBars = sortStackedBars(
|
|
313
|
+
rechartsBars,
|
|
314
|
+
data,
|
|
315
|
+
stacked,
|
|
316
|
+
legendOrder,
|
|
317
|
+
);
|
|
311
318
|
|
|
312
319
|
return {
|
|
313
320
|
rechartsBars: sortedRechartsBars,
|
|
@@ -330,7 +337,7 @@ export const computeUnitLabelAndRoundReferenceValue = (
|
|
|
330
337
|
return { unitLabel: '', roundReferenceValue, rechartsData: data };
|
|
331
338
|
}
|
|
332
339
|
|
|
333
|
-
const { valueBase, unitLabel } = getUnitLabel(unitRange
|
|
340
|
+
const { valueBase, unitLabel } = getUnitLabel(unitRange, maxValue);
|
|
334
341
|
const topValue = Math.ceil(maxValue / valueBase / 10) * 10;
|
|
335
342
|
const roundReferenceValue = getRoundReferenceValue(topValue);
|
|
336
343
|
const rechartsData = data.map((dataPoint) => {
|
|
@@ -397,8 +404,8 @@ export function getUnitLabel(
|
|
|
397
404
|
};
|
|
398
405
|
}
|
|
399
406
|
|
|
400
|
-
// Sort stacked bars by their average values in descending order
|
|
401
|
-
// This ensures the largest bars appear at the bottom of the stack
|
|
407
|
+
// Sort stacked bars by their average values in descending order or by legend order
|
|
408
|
+
// This ensures the largest bars appear at the bottom of the stack (default) or follow legend order
|
|
402
409
|
export const sortStackedBars = (
|
|
403
410
|
rechartsBars: {
|
|
404
411
|
dataKey: string;
|
|
@@ -409,10 +416,33 @@ export const sortStackedBars = (
|
|
|
409
416
|
[key: string]: string | number;
|
|
410
417
|
}[],
|
|
411
418
|
stacked?: boolean,
|
|
419
|
+
legendOrder?: string[],
|
|
412
420
|
) => {
|
|
413
421
|
if (!stacked) {
|
|
414
422
|
return rechartsBars;
|
|
415
423
|
}
|
|
424
|
+
|
|
425
|
+
// If legend order is provided, sort by legend order
|
|
426
|
+
if (legendOrder && legendOrder.length > 0) {
|
|
427
|
+
return [...rechartsBars].sort((a, b) => {
|
|
428
|
+
const indexA = legendOrder.indexOf(a.dataKey);
|
|
429
|
+
const indexB = legendOrder.indexOf(b.dataKey);
|
|
430
|
+
|
|
431
|
+
// If both items are in legend order, sort by their position
|
|
432
|
+
if (indexA !== -1 && indexB !== -1) {
|
|
433
|
+
return indexA - indexB;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// If only one item is in legend order, prioritize it
|
|
437
|
+
if (indexA !== -1) return -1;
|
|
438
|
+
if (indexB !== -1) return 1;
|
|
439
|
+
|
|
440
|
+
// If neither is in legend order, maintain original order
|
|
441
|
+
return 0;
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// Default behavior: sort by average values
|
|
416
446
|
const barAverages = rechartsBars.map((bar) => {
|
|
417
447
|
const values = data
|
|
418
448
|
.map((item) => Number(item[bar.dataKey]) || 0)
|
|
@@ -502,14 +532,20 @@ export const useChartData = <T extends BarchartBars>(
|
|
|
502
532
|
stacked?: boolean,
|
|
503
533
|
defaultSort?: BarchartProps<T>['defaultSort'],
|
|
504
534
|
unitRange?: UnitRange,
|
|
535
|
+
stackedBarSort?: 'default' | 'legend',
|
|
505
536
|
) => {
|
|
506
|
-
const { selectedResources } = useChartLegend();
|
|
537
|
+
const { selectedResources, listResources } = useChartLegend();
|
|
538
|
+
|
|
539
|
+
// Get legend order when stackedBarSort is 'legend'
|
|
540
|
+
const legendOrder = stackedBarSort === 'legend' ? listResources() : undefined;
|
|
541
|
+
|
|
507
542
|
const { data, rechartsBars } = formatPrometheusDataToRechartsDataAndBars(
|
|
508
543
|
bars,
|
|
509
544
|
type,
|
|
510
545
|
colorSet,
|
|
511
546
|
stacked,
|
|
512
547
|
defaultSort,
|
|
548
|
+
legendOrder,
|
|
513
549
|
);
|
|
514
550
|
|
|
515
551
|
// Filter both data and bars to only include selected resources for accurate maxValue calculation
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
HealthSelector,
|
|
3
3
|
optionsDefaultConfiguration,
|
|
4
4
|
} from './HealthSelector.component';
|
|
5
|
-
import
|
|
5
|
+
import { act } from 'react';
|
|
6
6
|
import { render, screen, waitFor } from '@testing-library/react';
|
|
7
7
|
import userEvent from '@testing-library/user-event';
|
|
8
8
|
import { getWrapper } from '../../testUtils';
|
|
@@ -18,11 +18,11 @@ describe('HealthSelector', () => {
|
|
|
18
18
|
const input = screen.getByRole('textbox');
|
|
19
19
|
|
|
20
20
|
// open the menu
|
|
21
|
-
userEvent.click(input);
|
|
21
|
+
await act(() => userEvent.click(input));
|
|
22
22
|
const healthyOption = getByText(/healthy/i);
|
|
23
23
|
expect(healthyOption).toBeInTheDocument();
|
|
24
24
|
});
|
|
25
|
-
it('should call the onChange function when it change', () => {
|
|
25
|
+
it('should call the onChange function when it change', async () => {
|
|
26
26
|
const { Wrapper } = getWrapper();
|
|
27
27
|
const onChange = jest.fn();
|
|
28
28
|
const { getByText } = render(
|
|
@@ -31,12 +31,12 @@ describe('HealthSelector', () => {
|
|
|
31
31
|
</Wrapper>,
|
|
32
32
|
);
|
|
33
33
|
const input = screen.getByRole('textbox');
|
|
34
|
-
userEvent.click(input);
|
|
34
|
+
await act(() => userEvent.click(input));
|
|
35
35
|
const warningOption = getByText(/warning/i);
|
|
36
|
-
userEvent.click(warningOption);
|
|
36
|
+
await act(() => userEvent.click(warningOption));
|
|
37
37
|
expect(onChange).toHaveBeenCalledWith('warning');
|
|
38
38
|
});
|
|
39
|
-
it('should not display hidden options', () => {
|
|
39
|
+
it('should not display hidden options', async () => {
|
|
40
40
|
const { Wrapper } = getWrapper();
|
|
41
41
|
const { queryByText } = render(
|
|
42
42
|
<Wrapper>
|
|
@@ -55,7 +55,7 @@ describe('HealthSelector', () => {
|
|
|
55
55
|
|
|
56
56
|
// open the menu
|
|
57
57
|
const input = screen.getByRole('textbox');
|
|
58
|
-
userEvent.click(input);
|
|
58
|
+
await act(() => userEvent.click(input));
|
|
59
59
|
const healthyOption = queryByText(/healthy/i);
|
|
60
60
|
expect(healthyOption).not.toBeInTheDocument();
|
|
61
61
|
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import '@testing-library/jest-dom';
|
|
2
2
|
import { render } from '@testing-library/react';
|
|
3
|
-
import React from 'react';
|
|
4
3
|
import { coreUIAvailableThemes } from '../../style/theme';
|
|
5
4
|
import { CoreUiThemeProvider } from '../coreuithemeprovider/CoreUiThemeProvider';
|
|
6
5
|
import { useComputeBackgroundColor } from './InfoMessageUtils';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { PropsWithChildren } from 'react';
|
|
2
2
|
import {
|
|
3
3
|
QueryClient,
|
|
4
4
|
QueryClientProvider,
|
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
render,
|
|
12
12
|
screen,
|
|
13
13
|
waitFor,
|
|
14
|
-
waitForElementToBeRemoved,
|
|
15
14
|
within,
|
|
16
15
|
} from '@testing-library/react';
|
|
17
16
|
import userEvent from '@testing-library/user-event';
|
|
@@ -77,9 +76,10 @@ describe('InlineInput', () => {
|
|
|
77
76
|
/// Then press enter to edit the input
|
|
78
77
|
await act(() => userEvent.keyboard('{enter}'));
|
|
79
78
|
/// Then type a new value
|
|
80
|
-
await act(() => userEvent.type(document.activeElement
|
|
79
|
+
await act(() => userEvent.type(document.activeElement!, 'new value'));
|
|
81
80
|
/// Then press enter to confirm the new value
|
|
82
81
|
await act(() => userEvent.keyboard('{enter}'));
|
|
82
|
+
await waitFor(() => screen.findByText('testnew value'));
|
|
83
83
|
expect(screen.queryByRole('textbox')).not.toBeInTheDocument();
|
|
84
84
|
|
|
85
85
|
//V
|
|
@@ -115,13 +115,16 @@ describe('InlineInput', () => {
|
|
|
115
115
|
/// Then press enter to edit the input
|
|
116
116
|
await act(() => userEvent.keyboard('{enter}'));
|
|
117
117
|
/// Then type a new value
|
|
118
|
-
await act(() => userEvent.type(document.activeElement
|
|
118
|
+
await act(() => userEvent.type(document.activeElement!, 'new value'));
|
|
119
119
|
/// Then press enter to confirm the new value
|
|
120
|
+
|
|
120
121
|
await act(() => userEvent.keyboard('{enter}'));
|
|
122
|
+
await waitFor(() => screen.findByRole('dialog', { name: /Confirm/i }));
|
|
121
123
|
/// Expect the confirmation modal to be opened
|
|
122
124
|
expect(selectors.confirmationModal()).toBeInTheDocument()
|
|
123
125
|
/// Click the confirm button
|
|
124
126
|
await act(() => userEvent.click(screen.getByRole('button', { name: /confirm/i })));
|
|
127
|
+
|
|
125
128
|
/// modal should be closed
|
|
126
129
|
expect(screen.queryByRole('dialog', { name: /Confirm/i })).not.toBeInTheDocument();
|
|
127
130
|
|
|
@@ -156,7 +159,7 @@ describe('InlineInput', () => {
|
|
|
156
159
|
/// Then press enter to edit the input
|
|
157
160
|
await act(() => userEvent.keyboard('{enter}'));
|
|
158
161
|
/// Then type a new value
|
|
159
|
-
await act(() => userEvent.type(document.activeElement
|
|
162
|
+
await act(() => userEvent.type(document.activeElement!, 'new value'));
|
|
160
163
|
/// Then press escape to cancel the new value
|
|
161
164
|
await act(() => userEvent.keyboard('{esc}'));
|
|
162
165
|
|
|
@@ -191,7 +194,7 @@ describe('InlineInput', () => {
|
|
|
191
194
|
/// Then press enter to edit the input
|
|
192
195
|
await act(() => userEvent.keyboard('{enter}'));
|
|
193
196
|
/// Then type a new value
|
|
194
|
-
await act(() => userEvent.type(document.activeElement
|
|
197
|
+
await act(() => userEvent.type(document.activeElement!, 'new value'));
|
|
195
198
|
/// Then press enter to confirm the new value
|
|
196
199
|
await act(() => userEvent.keyboard('{enter}'));
|
|
197
200
|
/// Expect the confirmation modal to be opened
|
|
@@ -208,7 +211,7 @@ describe('InlineInput', () => {
|
|
|
208
211
|
//V
|
|
209
212
|
expect(mock).not.toHaveBeenCalled();
|
|
210
213
|
expect(screen.getByRole('textbox')).toBeInTheDocument();
|
|
211
|
-
expect(screen.getByRole('textbox').value).toBe('testnew value');
|
|
214
|
+
expect((screen.getByRole('textbox') as HTMLInputElement).value).toBe('testnew value');
|
|
212
215
|
});
|
|
213
216
|
});
|
|
214
217
|
});
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
1
|
import { render, screen, fireEvent, waitFor, act } from '@testing-library/react';
|
|
3
2
|
import { InputList, InputListProps } from './InputList.component';
|
|
4
3
|
import { FormSection } from '../form/Form.component';
|
|
@@ -6,7 +5,7 @@ import { FormSection } from '../form/Form.component';
|
|
|
6
5
|
describe('InputList', () => {
|
|
7
6
|
const onChangeMock = jest.fn();
|
|
8
7
|
|
|
9
|
-
const renderInputList = (props: InputListProps<string
|
|
8
|
+
const renderInputList = (props: InputListProps<string>) => {
|
|
10
9
|
render(
|
|
11
10
|
<FormSection>
|
|
12
11
|
<InputList
|
|
@@ -3,7 +3,8 @@ import {
|
|
|
3
3
|
getUnitLabel,
|
|
4
4
|
addMissingDataPoint,
|
|
5
5
|
} from './ChartUtil';
|
|
6
|
-
|
|
6
|
+
import { Serie } from './LineTemporalChart.component';
|
|
7
|
+
const series: Serie[] = [
|
|
7
8
|
{
|
|
8
9
|
resource: 'node1',
|
|
9
10
|
data: [
|
|
@@ -11,7 +12,7 @@ const series = [
|
|
|
11
12
|
[1627460952, '18.73333333333335'],
|
|
12
13
|
],
|
|
13
14
|
getTooltipLabel: (metricPrefix, resource) => {
|
|
14
|
-
return resource
|
|
15
|
+
return `${resource}`;
|
|
15
16
|
},
|
|
16
17
|
isLineDashed: false,
|
|
17
18
|
},
|
|
@@ -22,12 +23,12 @@ const series = [
|
|
|
22
23
|
[1627460952, null],
|
|
23
24
|
],
|
|
24
25
|
getTooltipLabel: (metricPrefix, resource) => {
|
|
25
|
-
return resource
|
|
26
|
+
return `${resource}`;
|
|
26
27
|
},
|
|
27
28
|
isLineDashed: false,
|
|
28
29
|
},
|
|
29
30
|
];
|
|
30
|
-
const seriesSymmetrical = [
|
|
31
|
+
const seriesSymmetrical: Serie[] = [
|
|
31
32
|
{
|
|
32
33
|
metricPrefix: 'read',
|
|
33
34
|
resource: 'node1',
|
|
@@ -2,6 +2,7 @@ import { act, screen, render as testingRender, waitFor } from '@testing-library/
|
|
|
2
2
|
import userEvent from '@testing-library/user-event';
|
|
3
3
|
import React, { useState, useRef } from 'react';
|
|
4
4
|
import { Option, Select, SelectRef } from '../selectv2/Selectv2.component';
|
|
5
|
+
import { GroupTypeBase, OptionTypeBase } from 'react-select';
|
|
5
6
|
|
|
6
7
|
const render = (args) => {
|
|
7
8
|
return testingRender(args);
|
|
@@ -66,7 +67,10 @@ describe('SelectV2', () => {
|
|
|
66
67
|
};
|
|
67
68
|
|
|
68
69
|
it('should throw error if <Option/> is outside <Select/>', () => {
|
|
70
|
+
// mock console.error as this is the only way to silent expected error thrown by the component
|
|
71
|
+
const consoleErrorFn = jest.spyOn(console, 'error').mockImplementation(() => jest.fn());
|
|
69
72
|
expect(() => render(<Option value="Option 1" />)).toThrowError();
|
|
73
|
+
consoleErrorFn.mockRestore();
|
|
70
74
|
});
|
|
71
75
|
|
|
72
76
|
it('should open/close on click', async () => {
|
|
@@ -458,7 +462,7 @@ describe('SelectV2', () => {
|
|
|
458
462
|
describe('Ref API', () => {
|
|
459
463
|
it('should expose focus method via ref', async () => {
|
|
460
464
|
const RefTestComponent = () => {
|
|
461
|
-
const selectRef = useRef<SelectRef
|
|
465
|
+
const selectRef = useRef<SelectRef<OptionTypeBase, boolean, GroupTypeBase<OptionTypeBase>>>(null);
|
|
462
466
|
const [value, setValue] = useState<string>('');
|
|
463
467
|
|
|
464
468
|
return (
|
|
@@ -486,7 +490,7 @@ describe('SelectV2', () => {
|
|
|
486
490
|
|
|
487
491
|
it('should expose openMenu and closeMenu methods via ref', async () => {
|
|
488
492
|
const RefTestComponent = () => {
|
|
489
|
-
const selectRef = useRef<SelectRef
|
|
493
|
+
const selectRef = useRef<SelectRef<OptionTypeBase, boolean, GroupTypeBase<OptionTypeBase>>>(null);
|
|
490
494
|
const [value, setValue] = useState<string>('');
|
|
491
495
|
|
|
492
496
|
return (
|
|
@@ -529,7 +533,7 @@ describe('SelectV2', () => {
|
|
|
529
533
|
|
|
530
534
|
const RefTestComponent = () => {
|
|
531
535
|
const [value, setValue] = useState('');
|
|
532
|
-
const selectRef = useRef<SelectRef
|
|
536
|
+
const selectRef = useRef<SelectRef<OptionTypeBase, boolean, GroupTypeBase<OptionTypeBase>>>(null);
|
|
533
537
|
|
|
534
538
|
return (
|
|
535
539
|
<div>
|
|
@@ -582,7 +586,7 @@ describe('SelectV2', () => {
|
|
|
582
586
|
|
|
583
587
|
it('should expose blur method via ref', async () => {
|
|
584
588
|
const RefTestComponent = () => {
|
|
585
|
-
const selectRef = useRef<SelectRef
|
|
589
|
+
const selectRef = useRef<SelectRef<OptionTypeBase, boolean, GroupTypeBase<OptionTypeBase>>>(null);
|
|
586
590
|
const [value, setValue] = useState<string>('');
|
|
587
591
|
|
|
588
592
|
return (
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
2
|
-
import React from 'react';
|
|
3
2
|
import { TableSync } from './TableSync';
|
|
4
3
|
|
|
5
4
|
describe('TableSync', () => {
|
|
6
5
|
it('should render correctly', async () => {
|
|
7
6
|
const onSync = jest.fn();
|
|
8
7
|
render(
|
|
9
|
-
<TableSync onSync={onSync} />
|
|
8
|
+
<TableSync onSync={onSync} tooltipOverlay='sync' />
|
|
10
9
|
);
|
|
11
10
|
await waitFor(() => screen.queryAllByRole('img', { hidden: true }));
|
|
12
11
|
|
|
@@ -17,7 +16,7 @@ describe('TableSync', () => {
|
|
|
17
16
|
it('should call onSync when clicked', async () => {
|
|
18
17
|
const onSync = jest.fn();
|
|
19
18
|
render(
|
|
20
|
-
<TableSync onSync={onSync} />
|
|
19
|
+
<TableSync onSync={onSync} tooltipOverlay='sync' />
|
|
21
20
|
);
|
|
22
21
|
await waitFor(() => screen.queryAllByRole('img', { hidden: true }));
|
|
23
22
|
|
|
@@ -9,9 +9,12 @@ it('should return -1 or 1 to sort the status', () => {
|
|
|
9
9
|
expect(result).toEqual(1);
|
|
10
10
|
expect(result2).toEqual(-1);
|
|
11
11
|
});
|
|
12
|
-
it('should return
|
|
13
|
-
const
|
|
14
|
-
const
|
|
12
|
+
it('should return undefined for the unknown status', () => {
|
|
13
|
+
const consoleErrorMockHandle = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
14
|
+
const result = compareHealth('invalidStatus' as any, 'healthy');
|
|
15
|
+
const result2 = compareHealth('none', 'invalidStatus' as any);
|
|
16
|
+
expect(consoleErrorMockHandle).toHaveBeenCalled();
|
|
17
|
+
consoleErrorMockHandle.mockRestore();
|
|
15
18
|
expect(result).toEqual(undefined);
|
|
16
19
|
expect(result2).toEqual(undefined);
|
|
17
20
|
});
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { Table } from './Tablev2.component';
|
|
2
|
-
import React from 'react';
|
|
1
|
+
import { Table, TableProps } from './Tablev2.component';
|
|
3
2
|
import { render, screen, waitFor } from '@testing-library/react';
|
|
4
3
|
|
|
5
4
|
jest.mock('./TableUtils', () => ({
|
|
@@ -42,7 +41,7 @@ const data = [
|
|
|
42
41
|
health: 'healthy',
|
|
43
42
|
},
|
|
44
43
|
];
|
|
45
|
-
const columns = [
|
|
44
|
+
const columns: TableProps['columns'] = [
|
|
46
45
|
{
|
|
47
46
|
Header: 'First Name',
|
|
48
47
|
accessor: 'firstName',
|
package/src/lib/next.ts
CHANGED
|
@@ -360,7 +360,7 @@ export const CapacityWithUnitRange: Story = {
|
|
|
360
360
|
|
|
361
361
|
const stackedData: BarchartProps<
|
|
362
362
|
{
|
|
363
|
-
label: 'Success' | 'Failed';
|
|
363
|
+
label: 'Success' | 'Failed' | 'Warning';
|
|
364
364
|
data: [string, number][];
|
|
365
365
|
}[]
|
|
366
366
|
>['bars'] = [
|
|
@@ -375,9 +375,17 @@ const stackedData: BarchartProps<
|
|
|
375
375
|
{
|
|
376
376
|
label: 'Failed',
|
|
377
377
|
data: [
|
|
378
|
-
['category1',
|
|
379
|
-
['category2',
|
|
380
|
-
['category3',
|
|
378
|
+
['category1', 0],
|
|
379
|
+
['category2', 0],
|
|
380
|
+
['category3', 0],
|
|
381
|
+
],
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
label: 'Warning',
|
|
385
|
+
data: [
|
|
386
|
+
['category1', 0],
|
|
387
|
+
['category2', 0],
|
|
388
|
+
['category3', 0],
|
|
381
389
|
],
|
|
382
390
|
},
|
|
383
391
|
];
|
|
@@ -390,6 +398,7 @@ export const Stacked: Story = {
|
|
|
390
398
|
colorSet={{
|
|
391
399
|
Success: theme.statusHealthy,
|
|
392
400
|
Failed: theme.statusCritical,
|
|
401
|
+
Warning: theme.statusWarning,
|
|
393
402
|
}}
|
|
394
403
|
>
|
|
395
404
|
<Stack direction="vertical" gap="r16">
|
|
@@ -736,6 +745,88 @@ export const BarchartsWithSingleLegend: Story = {
|
|
|
736
745
|
);
|
|
737
746
|
},
|
|
738
747
|
};
|
|
748
|
+
|
|
749
|
+
export const ErrorState: Story = {
|
|
750
|
+
render: () => {
|
|
751
|
+
const theme = useTheme() as CoreUITheme;
|
|
752
|
+
return (
|
|
753
|
+
<ChartLegendWrapper
|
|
754
|
+
colorSet={{
|
|
755
|
+
Success: theme.statusHealthy,
|
|
756
|
+
Failed: theme.statusCritical,
|
|
757
|
+
}}
|
|
758
|
+
>
|
|
759
|
+
<Barchart
|
|
760
|
+
type="category"
|
|
761
|
+
bars={[]}
|
|
762
|
+
isError
|
|
763
|
+
title="Error State"
|
|
764
|
+
helpTooltip="This chart data could not be loaded"
|
|
765
|
+
/>
|
|
766
|
+
<ChartLegend shape="rectangle" />
|
|
767
|
+
</ChartLegendWrapper>
|
|
768
|
+
);
|
|
769
|
+
},
|
|
770
|
+
};
|
|
771
|
+
|
|
772
|
+
export const StackedBarSort: Story = {
|
|
773
|
+
render: () => {
|
|
774
|
+
const theme = useTheme() as CoreUITheme;
|
|
775
|
+
const [sort, setSort] = useState<'default' | 'legend'>('default');
|
|
776
|
+
const statusesData = [
|
|
777
|
+
{
|
|
778
|
+
label: 'Success',
|
|
779
|
+
data: [
|
|
780
|
+
['category1', 100],
|
|
781
|
+
['category2', 80],
|
|
782
|
+
['category3', 50],
|
|
783
|
+
],
|
|
784
|
+
},
|
|
785
|
+
{
|
|
786
|
+
label: 'Warning',
|
|
787
|
+
data: [
|
|
788
|
+
['category1', 10],
|
|
789
|
+
['category2', 20],
|
|
790
|
+
['category3', 30],
|
|
791
|
+
],
|
|
792
|
+
},
|
|
793
|
+
{
|
|
794
|
+
label: 'Failed',
|
|
795
|
+
data: [
|
|
796
|
+
['category1', 30],
|
|
797
|
+
['category2', 40],
|
|
798
|
+
['category3', 50],
|
|
799
|
+
],
|
|
800
|
+
},
|
|
801
|
+
] as const;
|
|
802
|
+
return (
|
|
803
|
+
<ChartLegendWrapper
|
|
804
|
+
colorSet={{
|
|
805
|
+
Success: theme.statusHealthy,
|
|
806
|
+
Warning: theme.statusWarning,
|
|
807
|
+
Failed: theme.statusCritical,
|
|
808
|
+
}}
|
|
809
|
+
>
|
|
810
|
+
<Barchart
|
|
811
|
+
type="category"
|
|
812
|
+
bars={statusesData}
|
|
813
|
+
stacked
|
|
814
|
+
stackedBarSort={sort}
|
|
815
|
+
title="Stacked Bar Chart"
|
|
816
|
+
helpTooltip="This chart data could not be loaded"
|
|
817
|
+
rightTitle={
|
|
818
|
+
<Button
|
|
819
|
+
label={sort === 'default' ? 'Sort by Legend' : 'Sort by Default'}
|
|
820
|
+
onClick={() => setSort(sort === 'default' ? 'legend' : 'default')}
|
|
821
|
+
/>
|
|
822
|
+
}
|
|
823
|
+
/>
|
|
824
|
+
<ChartLegend shape="rectangle" />
|
|
825
|
+
</ChartLegendWrapper>
|
|
826
|
+
);
|
|
827
|
+
},
|
|
828
|
+
};
|
|
829
|
+
|
|
739
830
|
export const CompleteExample: Story = {
|
|
740
831
|
render: () => {
|
|
741
832
|
const theme = useTheme() as CoreUITheme;
|
|
@@ -809,7 +900,7 @@ export const CompleteExample: Story = {
|
|
|
809
900
|
}}
|
|
810
901
|
/>
|
|
811
902
|
}
|
|
812
|
-
bars={data
|
|
903
|
+
bars={data}
|
|
813
904
|
tooltip={customTooltip}
|
|
814
905
|
isLoading={isLoading}
|
|
815
906
|
height={200}
|