@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.
Files changed (28) hide show
  1. package/dist/components/barchartv2/Barchart.component.d.ts +9 -1
  2. package/dist/components/barchartv2/Barchart.component.d.ts.map +1 -1
  3. package/dist/components/barchartv2/Barchart.component.js +13 -6
  4. package/dist/components/barchartv2/utils.d.ts +3 -3
  5. package/dist/components/barchartv2/utils.d.ts.map +1 -1
  6. package/dist/components/barchartv2/utils.js +42 -20
  7. package/dist/next.d.ts +1 -0
  8. package/dist/next.d.ts.map +1 -1
  9. package/dist/next.js +1 -0
  10. package/package.json +1 -1
  11. package/src/lib/components/accordion/Accordion.test.tsx +1 -1
  12. package/src/lib/components/barchartv2/Barchart.component.test.tsx +39 -14
  13. package/src/lib/components/barchartv2/Barchart.component.tsx +41 -6
  14. package/src/lib/components/barchartv2/utils.test.ts +10 -2
  15. package/src/lib/components/barchartv2/utils.ts +50 -14
  16. package/src/lib/components/healthselectorv2/HealthSelector.component.test.tsx +7 -7
  17. package/src/lib/components/infomessage/InfoMessageUtils.test.tsx +0 -1
  18. package/src/lib/components/inlineinput/InlineInput.test.tsx +10 -7
  19. package/src/lib/components/inputlist/InputList.test.tsx +1 -2
  20. package/src/lib/components/linetemporalchart/ChartUtil.test.ts +5 -4
  21. package/src/lib/components/selectv2/selectv2.test.tsx +8 -4
  22. package/src/lib/components/tablev2/TableSync.test.tsx +2 -3
  23. package/src/lib/components/tablev2/TableUtils.test.ts +6 -3
  24. package/src/lib/components/tablev2/Tablev2.test.tsx +2 -3
  25. package/src/lib/components/toggle/Toggle.test.tsx +1 -1
  26. package/src/lib/next.ts +1 -0
  27. package/stories/BarChart/barchart.stories.tsx +96 -5
  28. 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: T;
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;IACR,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,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;CACrB,CAAC;AAiHF,eAAO,MAAM,QAAQ,GAAI,CAAC,SAAS,YAAY,SAAS,aAAa,CAAC,CAAC,CAAC,4CA4HvE,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: 1,
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,MActD,CAAC;AAEF,eAAO,MAAM,cAAc,SACnB;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;CAAE,EAAE,YAChC,OAAO,WAuBlB,CAAC;AA8EF;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAAI,CAAC,SAAS,YAAY,QAChD,CAAC,QACD;IACJ,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,eACY,MAAM,EAAE;;GA0CtB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAI,CAAC,SAAS,YAAY,QACpD,CAAC,eACM,MAAM,EAAE;;GA2BtB,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAAI,CAAC,SAAS,YAAY,QACjD;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;CAAE,EAAE,eAC7B,MAAM,EAAE,eACR,aAAa,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC;;GAqB7C,CAAC;AA6BF;;;;;GAKG;AACH,eAAO,MAAM,yCAAyC,GACpD,CAAC,SAAS,YAAY,QAEhB,CAAC,QACD,aAAa,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,YACpB,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,YACpC,OAAO,gBACH,aAAa,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,KAC5C;IACD,IAAI,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;KAAE,EAAE,CAAC;IAC3C,YAAY,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAuBrE,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf,EAAE,CAAC;AAEJ,eAAO,MAAM,sCAAsC,SAC3C,GAAG,YACC,MAAM,aACL,SAAS,GAAG,SAAS;;;;CAoBjC,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE;IACT,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf,EAAE,EACH,QAAQ,EAAE,MAAM,GACf;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB,CAmCA;AAID,eAAO,MAAM,eAAe,iBACZ;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,EAAE,QACG;IACJ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;CAChC,EAAE,YACO,OAAO;aAPN,MAAM;UACT,MAAM;cACF,MAAM;GAuBnB,CAAC;AAEF,eAAO,MAAM,oBAAoB,GAAI,CAAC,SAAS,YAAY,SAClD,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,WACjC,iBAAiB,CAAC,CAAC,CAAC,GAAG,SAAS,gBAC3B,MAAM,GAAG,SAAS,8BAwBjC,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,uCAAuC,SAC5C;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAA;CAAE,EAAE,gBAC5B;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,qBAChD,MAAM,EAAE;;;;;iBADF,MAAM;cAAQ,MAAM;kBAAY,MAAM;;CA2BhE,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,CAAC,SAAS,YAAY,QAC3C,CAAC,QACD,aAAa,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,YACpB,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CAAC,YACpC,OAAO,gBACH,aAAa,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,cACjC,SAAS;;iBAnCI,MAAM;cAAQ,MAAM;kBAAY,MAAM;;;;;CAiEhE,CAAC"}
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
- return magnitude;
14
- if (normalized <= 2.5)
15
- return 2.5 * magnitude;
16
- if (normalized <= 5)
17
- return 5 * magnitude;
18
- return 10 * magnitude;
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 !== null && unitRange !== void 0 ? unitRange : [], maxValue);
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
- const { data, rechartsBars } = formatPrometheusDataToRechartsDataAndBars(bars, type, colorSet, stacked, defaultSort);
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
@@ -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,6 +1,6 @@
1
1
  {
2
2
  "name": "@scality/core-ui",
3
- "version": "0.163.0",
3
+ "version": "0.164.0",
4
4
  "description": "Scality common React component library",
5
5
  "author": "Scality Engineering",
6
6
  "license": "SEE LICENSE IN LICENSE",
@@ -1,5 +1,5 @@
1
1
  import { render, screen, waitFor } from '@testing-library/react';
2
- import React, { useState } from 'react';
2
+ import { useState } from 'react';
3
3
  import { Accordion } from './Accordion.component';
4
4
  import userEvent from '@testing-library/user-event';
5
5
 
@@ -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
- width={800}
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: 1,
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: T;
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.reduce(
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(bars, type, colorSet, stacked, defaultSort, unitRange);
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(1);
486
- expect(getRoundReferenceValue(2)).toBe(2.5);
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
- if (normalized <= 1) return magnitude;
22
- if (normalized <= 2.5) return 2.5 * magnitude;
23
- if (normalized <= 5) return 5 * magnitude;
24
- return 10 * magnitude;
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(rechartsBars, data, stacked);
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 ?? [], maxValue);
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 React from 'react';
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 React, { PropsWithChildren } from 'react';
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, 'new value'));
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, 'new value'));
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, 'new value'));
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, 'new value'));
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
- const series = [
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>(null);
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>(null);
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>(null);
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>(null);
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 undefine for the unknown status', () => {
13
- const result = compareHealth('invalidStatus', 'healthy');
14
- const result2 = compareHealth('none', 'invalidStatus');
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',
@@ -1,5 +1,5 @@
1
1
  import { render, screen } from '@testing-library/react';
2
- import React, { useState } from 'react';
2
+ import { useState } from 'react';
3
3
  import { Props, Toggle } from './Toggle.component';
4
4
  import userEvent from '@testing-library/user-event';
5
5
 
package/src/lib/next.ts CHANGED
@@ -22,3 +22,4 @@ export {
22
22
  BarchartTooltipFn,
23
23
  } from './components/barchartv2/Barchart.component';
24
24
  export { ChartLegendWrapper } from './components/chartlegend/ChartLegendWrapper';
25
+ export { ChartLegend } from './components/chartlegend/ChartLegend';
@@ -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', 8],
379
- ['category2', 10],
380
- ['category3', 25],
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}
package/tsconfig.json CHANGED
@@ -15,7 +15,6 @@
15
15
  "strictFunctionTypes": true,
16
16
  "noImplicitAny": false, //For now it is too costly to enable this one
17
17
  "esModuleInterop": true,
18
- "allowSyntheticDefaultImports": true,
19
18
  "forceConsistentCasingInFileNames": true
20
19
  }
21
20
  }