@openshift-migration-advisor/shared-components 0.21.0-8e9be07f572f → 0.22.0-aaabd4569e38

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.
@@ -15,6 +15,7 @@ export interface MigrationDonutChartDatum {
15
15
  clusterNames?: string[];
16
16
  networkNames?: string[];
17
17
  }
18
+ export type MigrationDonutChartLegendVariant = "html" | "chart";
18
19
  export interface MigrationDonutChartProps {
19
20
  data: MigrationDonutChartDatum[];
20
21
  legend?: Record<string, string>;
@@ -40,13 +41,29 @@ export interface MigrationDonutChartProps {
40
41
  percent: number;
41
42
  total: number;
42
43
  }) => string;
44
+ /** Agent-ui: slice / legend click drill-down. */
43
45
  onItemClick?: (item: MigrationDonutChartDatum) => void;
46
+ /** Agent-ui: clickable center title overlay. */
44
47
  onTitleClick?: () => void;
45
- /** When set, used for legend label instead of "name (countDisplay)". */
48
+ /** Agent-ui: custom HTML legend labels (ignored when legendVariant is "chart"). */
46
49
  legendLabelFormatter?: (item: {
47
50
  x: string;
48
51
  countDisplay?: string | number;
49
52
  }) => string;
53
+ /**
54
+ * Legend rendering mode.
55
+ * - `"html"` (default): custom clickable legend used by agent-ui
56
+ * - `"chart"`: PatternFly ChartLegend used by ui-app
57
+ */
58
+ legendVariant?: MigrationDonutChartLegendVariant;
59
+ /** ui-app ChartLegend: total legend width. */
60
+ legendWidth?: number;
61
+ /** ui-app ChartLegend: columns per row. */
62
+ itemsPerRow?: number;
63
+ /** ui-app ChartLegend: label font size. */
64
+ labelFontSize?: number;
65
+ /** Prefer themed Victory ChartTooltip (default true for chart legend). */
66
+ useThemedTooltip?: boolean;
50
67
  }
51
68
  export declare const MigrationDonutChart: FC<MigrationDonutChartProps>;
52
69
  export default MigrationDonutChart;
@@ -1 +1 @@
1
- {"version":3,"file":"MigrationDonutChart.d.ts","sourceRoot":"","sources":["../../src/charts/MigrationDonutChart.tsx"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAGhC,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,WAAW,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,wBAAwB,EAAE,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qBAAqB,CAAC,EAAE,CAAC,IAAI,EAAE;QAC7B,KAAK,EAAE;YACL,CAAC,EAAE,MAAM,CAAC;YACV,CAAC,EAAE,MAAM,CAAC;YACV,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;YAC/B,cAAc,EAAE,MAAM,CAAC;SACxB,CAAC;QACF,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;KACf,KAAK,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,wBAAwB,KAAK,IAAI,CAAC;IACvD,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,wEAAwE;IACxE,oBAAoB,CAAC,EAAE,CAAC,IAAI,EAAE;QAC5B,CAAC,EAAE,MAAM,CAAC;QACV,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;KAChC,KAAK,MAAM,CAAC;CACd;AAkDD,eAAO,MAAM,mBAAmB,EAAE,EAAE,CAAC,wBAAwB,CAkS5D,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
1
+ {"version":3,"file":"MigrationDonutChart.d.ts","sourceRoot":"","sources":["../../src/charts/MigrationDonutChart.tsx"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,EAAE,EAAgB,MAAM,OAAO,CAAC;AAG9C,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,WAAW,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,MAAM,gCAAgC,GAAG,MAAM,GAAG,OAAO,CAAC;AAEhE,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,wBAAwB,EAAE,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qBAAqB,CAAC,EAAE,CAAC,IAAI,EAAE;QAC7B,KAAK,EAAE;YACL,CAAC,EAAE,MAAM,CAAC;YACV,CAAC,EAAE,MAAM,CAAC;YACV,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;YAC/B,cAAc,EAAE,MAAM,CAAC;SACxB,CAAC;QACF,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;KACf,KAAK,MAAM,CAAC;IACb,iDAAiD;IACjD,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,wBAAwB,KAAK,IAAI,CAAC;IACvD,gDAAgD;IAChD,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,mFAAmF;IACnF,oBAAoB,CAAC,EAAE,CAAC,IAAI,EAAE;QAC5B,CAAC,EAAE,MAAM,CAAC;QACV,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;KAChC,KAAK,MAAM,CAAC;IACb;;;;OAIG;IACH,aAAa,CAAC,EAAE,gCAAgC,CAAC;IACjD,8CAA8C;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0EAA0E;IAC1E,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AA8ED,eAAO,MAAM,mBAAmB,EAAE,EAAE,CAAC,wBAAwB,CAiX5D,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
@@ -1,10 +1,40 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { css } from "@emotion/css";
3
- import { ChartDonut, ChartLabel } from "@patternfly/react-charts/victory";
4
- import { EmptyState, EmptyStateVariant, Flex, FlexItem, } from "@patternfly/react-core";
5
- import { InboxIcon } from "@patternfly/react-icons";
3
+ // Workaround
4
+ // Shared-components ships as strict ESM ("type": "module"), so webpack
5
+ // enforces fully-specified imports in consuming apps. The documented
6
+ // "@patternfly/react-charts/victory" subpath has no exports map entry and
7
+ // resolves to a directory, which webpack refuses under fully-specified —
8
+ // breaking the ui-app federated build.
9
+ import { ChartDonut, ChartLabel, ChartLegend, ChartTooltip, } from "@patternfly/react-charts/dist/esm/victory/index.js";
10
+ import { Flex, FlexItem } from "@patternfly/react-core";
6
11
  import { useCallback, useMemo } from "react";
7
- const legendColors = ["#0066cc", "#5e40be", "#b6a6e9", "#b98412"];
12
+ const legendColors = [
13
+ "#0066cc",
14
+ "#5e40be",
15
+ "#009596",
16
+ "#4cb140",
17
+ "#f0ab00",
18
+ "#b98412",
19
+ "#a30000",
20
+ "#b6a6e9",
21
+ ];
22
+ /** Victory chart tooltips that respect PatternFly light/dark themes. */
23
+ const themedChartTooltipStyle = {
24
+ fontSize: 9,
25
+ fill: "var(--pf-t--global--text--color--regular)",
26
+ };
27
+ const themedChartTooltipFlyoutStyle = {
28
+ stroke: "var(--pf-t--global--border--color--default)",
29
+ strokeWidth: 1,
30
+ fill: "var(--pf-t--global--background--color--floating--default)",
31
+ };
32
+ const themedChartTooltipFlyoutPadding = {
33
+ top: 6,
34
+ bottom: 6,
35
+ left: 10,
36
+ right: 10,
37
+ };
8
38
  const styles = {
9
39
  legendIcon: css `
10
40
  margin-right: 4px;
@@ -50,21 +80,25 @@ const styles = {
50
80
  padding: var(--pf-t--global--spacer--ml);
51
81
  `,
52
82
  };
53
- export const MigrationDonutChart = ({ data, legend, customColors, height = 260, width = 420, title, subTitle, titleColor = "#000000", subTitleColor = "#000000", marginLeft = "0%", titleFontSize = 28, subTitleFontSize = 14, donutThickness = 45, padAngle = 1, tooltipLabelFormatter, onItemClick, onTitleClick, legendLabelFormatter, }) => {
83
+ export const MigrationDonutChart = ({ data, legend, customColors, height = 260, width = 420, title, subTitle, titleColor = "var(--pf-t--global--text--color--regular)", subTitleColor = "var(--pf-t--global--text--color--subtle)", marginLeft = "0%", titleFontSize = 28, subTitleFontSize = 14, donutThickness = 45, padAngle = 1, tooltipLabelFormatter, onItemClick, onTitleClick, legendLabelFormatter, legendVariant = "html", legendWidth, itemsPerRow = 1, labelFontSize = 25, useThemedTooltip, }) => {
84
+ const showChartLegend = legendVariant === "chart";
85
+ const themedTooltipEnabled = useThemedTooltip ?? showChartLegend;
54
86
  const dynamicLegend = useMemo(() => {
55
- return data.reduce((acc, current) => {
56
- const key = `${current.legendCategory}`;
57
- if (!acc.seen.has(key)) {
58
- acc.seen.add(key);
59
- const color = customColors?.[key] ??
60
- legendColors[(acc.seen.size - 1) % legendColors.length];
61
- acc.result.push({ [key]: color });
87
+ const legendMap = {};
88
+ const seen = new Set();
89
+ for (const item of data) {
90
+ const key = item.legendCategory;
91
+ if (!seen.has(key)) {
92
+ seen.add(key);
93
+ legendMap[key] =
94
+ customColors?.[key] ??
95
+ legendColors[(seen.size - 1) % legendColors.length];
62
96
  }
63
- return acc;
64
- }, { seen: new Set(), result: [] }).result;
97
+ }
98
+ return legendMap;
65
99
  }, [data, customColors]);
66
- const chartLegend = legend ? legend : Object.assign({}, ...dynamicLegend);
67
- const getColor = useCallback((name) => chartLegend[name], [chartLegend]);
100
+ const chartLegend = legend ?? dynamicLegend;
101
+ const getColor = useCallback((name) => chartLegend[name] ?? legendColors[0], [chartLegend]);
68
102
  const chartData = useMemo(() => {
69
103
  return data.map((item) => ({
70
104
  x: item.name,
@@ -76,6 +110,29 @@ export const MigrationDonutChart = ({ data, legend, customColors, height = 260,
76
110
  const colorScale = useMemo(() => {
77
111
  return chartData.map((item) => getColor(item.legendCategory));
78
112
  }, [chartData, getColor]);
113
+ const legendData = useMemo(() => {
114
+ return chartData.map((item) => ({
115
+ name: `${item.x} (${item.countDisplay})`,
116
+ symbol: { fill: getColor(item.legendCategory) },
117
+ }));
118
+ }, [chartData, getColor]);
119
+ const legendWidthValue = legendWidth ?? 800;
120
+ const legendX = useMemo(() => {
121
+ const symbolAndGap = 34;
122
+ const charWidth = labelFontSize * 0.55;
123
+ const itemWidths = legendData.map((d) => symbolAndGap + d.name.length * charWidth);
124
+ const numCols = Math.min(legendData.length, itemsPerRow);
125
+ const gutter = 16;
126
+ let contentWidth = (numCols - 1) * gutter;
127
+ for (let c = 0; c < numCols; c++) {
128
+ let maxW = 0;
129
+ for (let i = c; i < itemWidths.length; i += itemsPerRow) {
130
+ maxW = Math.max(maxW, itemWidths[i] ?? 0);
131
+ }
132
+ contentWidth += maxW;
133
+ }
134
+ return Math.max(0, (legendWidthValue - contentWidth) / 2);
135
+ }, [legendData, itemsPerRow, legendWidthValue, labelFontSize]);
79
136
  const innerRadius = useMemo(() => {
80
137
  const outerApprox = Math.min(width, height) / 2;
81
138
  const computed = outerApprox - donutThickness;
@@ -124,24 +181,42 @@ export const MigrationDonutChart = ({ data, legend, customColors, height = 260,
124
181
  },
125
182
  ];
126
183
  }, [onItemClick, handleClick]);
184
+ const donutTooltip = useMemo(() => themedTooltipEnabled ? (_jsx(ChartTooltip, { style: themedChartTooltipStyle, flyoutStyle: themedChartTooltipFlyoutStyle, flyoutPadding: themedChartTooltipFlyoutPadding })) : undefined, [themedTooltipEnabled]);
185
+ const formatLabel = useCallback((datum) => {
186
+ const percent = totalY > 0 ? (Number(datum.y) / totalY) * 100 : 0;
187
+ return tooltipLabelFormatter
188
+ ? tooltipLabelFormatter({
189
+ datum: {
190
+ x: datum.x,
191
+ y: Number(datum.y),
192
+ countDisplay: datum.countDisplay,
193
+ legendCategory: datum.legendCategory,
194
+ },
195
+ percent,
196
+ total: totalY,
197
+ })
198
+ : `${datum.x}: ${datum.countDisplay ?? datum.y}`;
199
+ }, [tooltipLabelFormatter, totalY]);
127
200
  if (!data || data.length === 0) {
128
- return (_jsx(EmptyState, { headingLevel: "h3", titleText: "No data available", icon: InboxIcon, variant: EmptyStateVariant.xs }));
201
+ return null;
129
202
  }
130
- return (_jsxs(Flex, { direction: { default: "column" }, alignItems: { default: "alignItemsCenter" }, className: `${onItemClick ? styles.cursorPointer : styles.cursorDefault} ${styles.chartContainer}`, children: [_jsxs("div", { className: styles.donutWrapper, children: [_jsx(ChartDonut, { ariaDesc: "Migration data donut chart", data: chartData, events: chartEvents, labels: ({ datum, }) => {
131
- const percent = totalY > 0 ? (Number(datum.y) / totalY) * 100 : 0;
132
- return tooltipLabelFormatter
133
- ? tooltipLabelFormatter({
134
- datum: {
135
- x: datum.x,
136
- y: Number(datum.y),
137
- countDisplay: datum.countDisplay,
138
- legendCategory: datum.legendCategory,
139
- },
140
- percent,
141
- total: totalY,
142
- })
143
- : `${datum.x}: ${datum.countDisplay ?? datum.y}`;
144
- }, colorScale: colorScale, constrainToVisibleArea: true, innerRadius: innerRadius, padAngle: padAngle, title: title, subTitle: subTitle, height: height, width: width, padding: {
203
+ const htmlLegend = (_jsx(Flex, { className: `${styles.legendContainer} ${css `margin-left: ${marginLeft};`}`, justifyContent: { default: "justifyContentCenter" }, alignItems: { default: "alignItemsFlexStart" }, children: _jsx(Flex, { className: styles.legendInner, spaceItems: { default: "spaceItemsMd" }, justifyContent: { default: "justifyContentCenter" }, alignItems: { default: "alignItemsCenter" }, flexWrap: { default: "wrap" }, children: data.map((item) => {
204
+ const label = legendLabelFormatter
205
+ ? legendLabelFormatter({
206
+ x: item.name,
207
+ countDisplay: item.countDisplay,
208
+ })
209
+ : item.name;
210
+ const content = (_jsxs(_Fragment, { children: [_jsxs("svg", { width: "10", height: "10", "aria-hidden": "true", className: styles.legendIcon, children: [_jsx("title", { children: "Legend color indicator" }), _jsx("rect", { width: "10", height: "10", fill: getColor(item.legendCategory) })] }), _jsx("span", { children: label })] }));
211
+ return (_jsx(FlexItem, { children: onItemClick ? (_jsx("button", { type: "button", onClick: () => onItemClick(item), className: styles.legendButton, children: content })) : (_jsx("span", { className: styles.legendLabel, children: content })) }, `${item.legendCategory}-${item.name}`));
212
+ }) }) }));
213
+ const chartLegendElement = (_jsx(FlexItem, { flex: { default: "flex_1" }, children: _jsx(Flex, { justifyContent: { default: "justifyContentCenter" }, alignItems: { default: "alignItemsCenter" }, children: _jsx(FlexItem, { children: _jsx(ChartLegend, { data: legendData, orientation: "horizontal", height: 200, width: legendWidthValue, x: legendX, itemsPerRow: itemsPerRow, style: {
214
+ labels: {
215
+ fontSize: labelFontSize,
216
+ fill: "var(--pf-t--global--text--color--regular)",
217
+ },
218
+ } }) }) }) }));
219
+ return (_jsxs(Flex, { direction: { default: "column" }, alignItems: { default: "alignItemsCenter" }, className: `${onItemClick ? styles.cursorPointer : styles.cursorDefault} ${styles.chartContainer}`, children: [_jsxs("div", { className: styles.donutWrapper, children: [_jsx(ChartDonut, { ariaDesc: "Migration data donut chart", data: chartData, events: chartEvents, labelComponent: donutTooltip, labels: ({ datum, }) => formatLabel(datum), colorScale: colorScale, constrainToVisibleArea: true, innerRadius: innerRadius, padAngle: padAngle, title: title, subTitle: subTitle, height: height, width: width, padding: {
145
220
  bottom: 5,
146
221
  left: 20,
147
222
  right: 20,
@@ -152,7 +227,7 @@ export const MigrationDonutChart = ({ data, legend, customColors, height = 260,
152
227
  fontSize: titleFontSize,
153
228
  fontWeight: "bold",
154
229
  },
155
- ] })) : undefined, subTitleComponent: subTitle ? (_jsx(ChartLabel, { style: [
230
+ ] })) : undefined, subTitleComponent: subTitle ? (_jsx(ChartLabel, { dy: showChartLegend ? 7 : undefined, style: [
156
231
  {
157
232
  fill: subTitleColor,
158
233
  fontSize: subTitleFontSize,
@@ -172,15 +247,6 @@ export const MigrationDonutChart = ({ data, legend, customColors, height = 260,
172
247
  e.preventDefault();
173
248
  onTitleClick();
174
249
  }
175
- } }))] }), _jsx(Flex, { className: `${styles.legendContainer} ${css `margin-left: ${marginLeft};`}`, justifyContent: { default: "justifyContentCenter" }, alignItems: { default: "alignItemsFlexStart" }, children: _jsx(Flex, { className: styles.legendInner, spaceItems: { default: "spaceItemsMd" }, justifyContent: { default: "justifyContentCenter" }, alignItems: { default: "alignItemsCenter" }, flexWrap: { default: "wrap" }, children: data.map((item) => {
176
- const label = legendLabelFormatter
177
- ? legendLabelFormatter({
178
- x: item.name,
179
- countDisplay: item.countDisplay,
180
- })
181
- : item.name;
182
- const content = (_jsxs(_Fragment, { children: [_jsxs("svg", { width: "10", height: "10", "aria-hidden": "true", className: styles.legendIcon, children: [_jsx("title", { children: "Legend color indicator" }), _jsx("rect", { width: "10", height: "10", fill: getColor(item.legendCategory) })] }), _jsx("span", { children: label })] }));
183
- return (_jsx(FlexItem, { children: onItemClick ? (_jsx("button", { type: "button", onClick: () => onItemClick(item), className: styles.legendButton, children: content })) : (_jsx("span", { className: styles.legendLabel, children: content })) }, `${item.legendCategory}-${item.name}`));
184
- }) }) })] }));
250
+ } }))] }), showChartLegend ? chartLegendElement : htmlLegend] }));
185
251
  };
186
252
  export default MigrationDonutChart;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export * from "./charts/index.js";
2
- export * from "./forms/index.js";
3
- export * from "./report/index.js";
1
+ export * from "./charts";
2
+ export * from "./forms";
3
+ export * from "./report";
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,SAAS,CAAC;AACxB,cAAc,UAAU,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,3 @@
1
- export * from "./charts/index.js";
2
- export * from "./forms/index.js";
3
- export * from "./report/index.js";
1
+ export * from "./charts";
2
+ export * from "./forms";
3
+ export * from "./report";
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/report/constants.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,WAA6B,CAAC;AAC5D,eAAO,MAAM,iBAAiB,WAA+B,CAAC;AAE9D,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;CAcjC,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/report/constants.ts"],"names":[],"mappings":"AAKA;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,WAA6B,CAAC;AAC5D,eAAO,MAAM,iBAAiB,WAA+B,CAAC;AAE9D,eAAO,MAAM,8BAA8B;;;;;;;;;;;;;;CAcjC,CAAC"}
@@ -1,5 +1,4 @@
1
- import { chart_color_blue_300 } from "@patternfly/react-tokens/dist/esm/chart_color_blue_300";
2
- import { chart_color_yellow_300 } from "@patternfly/react-tokens/dist/esm/chart_color_yellow_300";
1
+ import { chart_color_blue_300, chart_color_yellow_300, } from "@patternfly/react-tokens";
3
2
  /**
4
3
  * PatternFly chart color semantics (https://www.patternfly.org/charts/colors-for-charts/design-guidelines/#best-practices):
5
4
  * Blue → success / migratable / supported
@@ -1 +1 @@
1
- {"version":3,"file":"osSupportTier.d.ts","sourceRoot":"","sources":["../../src/report/osSupportTier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAUzD;;;GAGG;AACH,eAAO,MAAM,WAAW;;;;;CAKd,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,OAAO,WAAW,CAAC,CAAC;AAEzE,eAAO,MAAM,2BAA2B,+CACM,CAAC;AAE/C,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AASD,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAK3D,CAAC;AAEF,0DAA0D;AAC1D,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAShE,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,WAAW,EAK9C,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAC5C,WAAW,EACX,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAMjC,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACpC,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,4FAA4F;AAC5F,eAAO,MAAM,gCAAgC,EAAE,MAAM,CACnD,WAAW,EACX,qBAAqB,CAkBtB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,WAAW,GAAG,SAAS,EACpC,SAAS,EAAE,OAAO,GACjB,WAAW,CAMb;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,GAC1C,OAAO,CAeT;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAEjE;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAEnE;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAElE;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,WAAW,GAChB,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAElC;AAED,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,WAAW,GAChB,qBAAqB,CAEvB"}
1
+ {"version":3,"file":"osSupportTier.d.ts","sourceRoot":"","sources":["../../src/report/osSupportTier.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAYzD;;;GAGG;AACH,eAAO,MAAM,WAAW;;;;;CAKd,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,WAAW,CAAC,CAAC,MAAM,OAAO,WAAW,CAAC,CAAC;AAEzE,eAAO,MAAM,2BAA2B,+CACM,CAAC;AAE/C,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AASD,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAK3D,CAAC;AAEF,0DAA0D;AAC1D,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAShE,CAAC;AAEF,eAAO,MAAM,qBAAqB,EAAE,WAAW,EAK9C,CAAC;AAEF,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAC5C,WAAW,EACX,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAMjC,CAAC;AAEF,MAAM,WAAW,qBAAqB;IACpC,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,4FAA4F;AAC5F,eAAO,MAAM,gCAAgC,EAAE,MAAM,CACnD,WAAW,EACX,qBAAqB,CAkBtB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,WAAW,EAAE,WAAW,GAAG,SAAS,EACpC,SAAS,EAAE,OAAO,GACjB,WAAW,CAMb;AAED,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,GAC1C,OAAO,CAeT;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAEjE;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAEnE;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAElE;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,WAAW,GAChB,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAElC;AAED,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,WAAW,GAChB,qBAAqB,CAEvB"}
@@ -1,11 +1,4 @@
1
- import { t_color_blue_20 } from "@patternfly/react-tokens/dist/esm/t_color_blue_20";
2
- import { t_color_blue_60 } from "@patternfly/react-tokens/dist/esm/t_color_blue_60";
3
- import { t_color_green_20 } from "@patternfly/react-tokens/dist/esm/t_color_green_20";
4
- import { t_color_green_60 } from "@patternfly/react-tokens/dist/esm/t_color_green_60";
5
- import { t_color_orange_20 } from "@patternfly/react-tokens/dist/esm/t_color_orange_20";
6
- import { t_color_orange_60 } from "@patternfly/react-tokens/dist/esm/t_color_orange_60";
7
- import { t_color_yellow_20 } from "@patternfly/react-tokens/dist/esm/t_color_yellow_20";
8
- import { t_color_yellow_60 } from "@patternfly/react-tokens/dist/esm/t_color_yellow_60";
1
+ import { t_color_blue_20, t_color_blue_60, t_color_green_20, t_color_green_60, t_color_orange_20, t_color_orange_60, t_color_yellow_20, t_color_yellow_60, } from "@patternfly/react-tokens";
9
2
  /**
10
3
  * Guest OS support tiers (matches agent-sdk / planner-sdk `OsInfoSupportTierEnum` values).
11
4
  * Defined locally so this package stays SDK-agnostic.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openshift-migration-advisor/shared-components",
3
- "version": "0.21.0-8e9be07f572f",
3
+ "version": "0.22.0-aaabd4569e38",
4
4
  "description": "Shared React UI components for Migration Advisor apps (report charts, forms, OS support tiers)",
5
5
  "author": "Montse Ortega <mortegag@redhat.com>",
6
6
  "license": "Apache-2.0",
@@ -35,7 +35,8 @@
35
35
  "clean": "rm -rf node_modules dist .tmp",
36
36
  "check": "yarn run -T biome check .",
37
37
  "check:fix": "yarn run -T biome check --write .",
38
- "test": "echo 'No tests configured for this package'",
38
+ "test": "yarn run -T vitest run",
39
+ "test:watch": "yarn run -T vitest",
39
40
  "typecheck": "yarn run -T tsc -b --noEmit"
40
41
  },
41
42
  "files": [
@@ -1,13 +1,18 @@
1
1
  import { css } from "@emotion/css";
2
- import { ChartDonut, ChartLabel } from "@patternfly/react-charts/victory";
2
+ // Workaround
3
+ // Shared-components ships as strict ESM ("type": "module"), so webpack
4
+ // enforces fully-specified imports in consuming apps. The documented
5
+ // "@patternfly/react-charts/victory" subpath has no exports map entry and
6
+ // resolves to a directory, which webpack refuses under fully-specified —
7
+ // breaking the ui-app federated build.
3
8
  import {
4
- EmptyState,
5
- EmptyStateVariant,
6
- Flex,
7
- FlexItem,
8
- } from "@patternfly/react-core";
9
- import { InboxIcon } from "@patternfly/react-icons";
10
- import type { FC } from "react";
9
+ ChartDonut,
10
+ ChartLabel,
11
+ ChartLegend,
12
+ ChartTooltip,
13
+ } from "@patternfly/react-charts/dist/esm/victory/index.js";
14
+ import { Flex, FlexItem } from "@patternfly/react-core";
15
+ import type { FC, ReactElement } from "react";
11
16
  import { useCallback, useMemo } from "react";
12
17
 
13
18
  export interface MigrationDonutChartDatum {
@@ -21,6 +26,8 @@ export interface MigrationDonutChartDatum {
21
26
  networkNames?: string[];
22
27
  }
23
28
 
29
+ export type MigrationDonutChartLegendVariant = "html" | "chart";
30
+
24
31
  export interface MigrationDonutChartProps {
25
32
  data: MigrationDonutChartDatum[];
26
33
  legend?: Record<string, string>;
@@ -46,16 +53,60 @@ export interface MigrationDonutChartProps {
46
53
  percent: number;
47
54
  total: number;
48
55
  }) => string;
56
+ /** Agent-ui: slice / legend click drill-down. */
49
57
  onItemClick?: (item: MigrationDonutChartDatum) => void;
58
+ /** Agent-ui: clickable center title overlay. */
50
59
  onTitleClick?: () => void;
51
- /** When set, used for legend label instead of "name (countDisplay)". */
60
+ /** Agent-ui: custom HTML legend labels (ignored when legendVariant is "chart"). */
52
61
  legendLabelFormatter?: (item: {
53
62
  x: string;
54
63
  countDisplay?: string | number;
55
64
  }) => string;
65
+ /**
66
+ * Legend rendering mode.
67
+ * - `"html"` (default): custom clickable legend used by agent-ui
68
+ * - `"chart"`: PatternFly ChartLegend used by ui-app
69
+ */
70
+ legendVariant?: MigrationDonutChartLegendVariant;
71
+ /** ui-app ChartLegend: total legend width. */
72
+ legendWidth?: number;
73
+ /** ui-app ChartLegend: columns per row. */
74
+ itemsPerRow?: number;
75
+ /** ui-app ChartLegend: label font size. */
76
+ labelFontSize?: number;
77
+ /** Prefer themed Victory ChartTooltip (default true for chart legend). */
78
+ useThemedTooltip?: boolean;
56
79
  }
57
80
 
58
- const legendColors = ["#0066cc", "#5e40be", "#b6a6e9", "#b98412"];
81
+ const legendColors = [
82
+ "#0066cc",
83
+ "#5e40be",
84
+ "#009596",
85
+ "#4cb140",
86
+ "#f0ab00",
87
+ "#b98412",
88
+ "#a30000",
89
+ "#b6a6e9",
90
+ ];
91
+
92
+ /** Victory chart tooltips that respect PatternFly light/dark themes. */
93
+ const themedChartTooltipStyle = {
94
+ fontSize: 9,
95
+ fill: "var(--pf-t--global--text--color--regular)",
96
+ } as const;
97
+
98
+ const themedChartTooltipFlyoutStyle = {
99
+ stroke: "var(--pf-t--global--border--color--default)",
100
+ strokeWidth: 1,
101
+ fill: "var(--pf-t--global--background--color--floating--default)",
102
+ } as const;
103
+
104
+ const themedChartTooltipFlyoutPadding = {
105
+ top: 6,
106
+ bottom: 6,
107
+ left: 10,
108
+ right: 10,
109
+ } as const;
59
110
 
60
111
  const styles = {
61
112
  legendIcon: css`
@@ -111,8 +162,8 @@ export const MigrationDonutChart: FC<MigrationDonutChartProps> = ({
111
162
  width = 420,
112
163
  title,
113
164
  subTitle,
114
- titleColor = "#000000",
115
- subTitleColor = "#000000",
165
+ titleColor = "var(--pf-t--global--text--color--regular)",
166
+ subTitleColor = "var(--pf-t--global--text--color--subtle)",
116
167
  marginLeft = "0%",
117
168
  titleFontSize = 28,
118
169
  subTitleFontSize = 14,
@@ -122,30 +173,35 @@ export const MigrationDonutChart: FC<MigrationDonutChartProps> = ({
122
173
  onItemClick,
123
174
  onTitleClick,
124
175
  legendLabelFormatter,
176
+ legendVariant = "html",
177
+ legendWidth,
178
+ itemsPerRow = 1,
179
+ labelFontSize = 25,
180
+ useThemedTooltip,
125
181
  }) => {
182
+ const showChartLegend = legendVariant === "chart";
183
+ const themedTooltipEnabled = useThemedTooltip ?? showChartLegend;
184
+
126
185
  const dynamicLegend = useMemo(() => {
127
- return data.reduce(
128
- (acc, current) => {
129
- const key = `${current.legendCategory}`;
130
- if (!acc.seen.has(key)) {
131
- acc.seen.add(key);
132
- const color =
133
- customColors?.[key] ??
134
- legendColors[(acc.seen.size - 1) % legendColors.length];
135
- acc.result.push({ [key]: color });
136
- }
137
- return acc;
138
- },
139
- { seen: new Set(), result: [] } as {
140
- seen: Set<string>;
141
- result: Record<string, string>[];
142
- },
143
- ).result;
186
+ const legendMap: Record<string, string> = {};
187
+ const seen = new Set<string>();
188
+
189
+ for (const item of data) {
190
+ const key = item.legendCategory;
191
+ if (!seen.has(key)) {
192
+ seen.add(key);
193
+ legendMap[key] =
194
+ customColors?.[key] ??
195
+ legendColors[(seen.size - 1) % legendColors.length];
196
+ }
197
+ }
198
+
199
+ return legendMap;
144
200
  }, [data, customColors]);
145
201
 
146
- const chartLegend = legend ? legend : Object.assign({}, ...dynamicLegend);
202
+ const chartLegend = legend ?? dynamicLegend;
147
203
  const getColor = useCallback(
148
- (name: string): string => chartLegend[name],
204
+ (name: string): string => chartLegend[name] ?? legendColors[0],
149
205
  [chartLegend],
150
206
  );
151
207
 
@@ -162,6 +218,33 @@ export const MigrationDonutChart: FC<MigrationDonutChartProps> = ({
162
218
  return chartData.map((item) => getColor(item.legendCategory));
163
219
  }, [chartData, getColor]);
164
220
 
221
+ const legendData = useMemo(() => {
222
+ return chartData.map((item) => ({
223
+ name: `${item.x} (${item.countDisplay})`,
224
+ symbol: { fill: getColor(item.legendCategory) },
225
+ }));
226
+ }, [chartData, getColor]);
227
+
228
+ const legendWidthValue = legendWidth ?? 800;
229
+ const legendX = useMemo(() => {
230
+ const symbolAndGap = 34;
231
+ const charWidth = labelFontSize * 0.55;
232
+ const itemWidths = legendData.map(
233
+ (d) => symbolAndGap + d.name.length * charWidth,
234
+ );
235
+ const numCols = Math.min(legendData.length, itemsPerRow);
236
+ const gutter = 16;
237
+ let contentWidth = (numCols - 1) * gutter;
238
+ for (let c = 0; c < numCols; c++) {
239
+ let maxW = 0;
240
+ for (let i = c; i < itemWidths.length; i += itemsPerRow) {
241
+ maxW = Math.max(maxW, itemWidths[i] ?? 0);
242
+ }
243
+ contentWidth += maxW;
244
+ }
245
+ return Math.max(0, (legendWidthValue - contentWidth) / 2);
246
+ }, [legendData, itemsPerRow, legendWidthValue, labelFontSize]);
247
+
165
248
  const innerRadius = useMemo(() => {
166
249
  const outerApprox = Math.min(width, height) / 2;
167
250
  const computed = outerApprox - donutThickness;
@@ -221,17 +304,132 @@ export const MigrationDonutChart: FC<MigrationDonutChartProps> = ({
221
304
  ];
222
305
  }, [onItemClick, handleClick]);
223
306
 
307
+ const donutTooltip = useMemo(
308
+ () =>
309
+ themedTooltipEnabled ? (
310
+ <ChartTooltip
311
+ style={themedChartTooltipStyle}
312
+ flyoutStyle={themedChartTooltipFlyoutStyle}
313
+ flyoutPadding={themedChartTooltipFlyoutPadding}
314
+ />
315
+ ) : undefined,
316
+ [themedTooltipEnabled],
317
+ );
318
+
319
+ const formatLabel = useCallback(
320
+ (datum: {
321
+ x: string;
322
+ y: number;
323
+ legendCategory: string;
324
+ countDisplay?: string | number;
325
+ }) => {
326
+ const percent = totalY > 0 ? (Number(datum.y) / totalY) * 100 : 0;
327
+ return tooltipLabelFormatter
328
+ ? tooltipLabelFormatter({
329
+ datum: {
330
+ x: datum.x,
331
+ y: Number(datum.y),
332
+ countDisplay: datum.countDisplay,
333
+ legendCategory: datum.legendCategory,
334
+ },
335
+ percent,
336
+ total: totalY,
337
+ })
338
+ : `${datum.x}: ${datum.countDisplay ?? datum.y}`;
339
+ },
340
+ [tooltipLabelFormatter, totalY],
341
+ );
342
+
224
343
  if (!data || data.length === 0) {
225
- return (
226
- <EmptyState
227
- headingLevel="h3"
228
- titleText="No data available"
229
- icon={InboxIcon}
230
- variant={EmptyStateVariant.xs}
231
- />
232
- );
344
+ return null;
233
345
  }
234
346
 
347
+ const htmlLegend: ReactElement = (
348
+ <Flex
349
+ className={`${styles.legendContainer} ${css`margin-left: ${marginLeft};`}`}
350
+ justifyContent={{ default: "justifyContentCenter" }}
351
+ alignItems={{ default: "alignItemsFlexStart" }}
352
+ >
353
+ <Flex
354
+ className={styles.legendInner}
355
+ spaceItems={{ default: "spaceItemsMd" }}
356
+ justifyContent={{ default: "justifyContentCenter" }}
357
+ alignItems={{ default: "alignItemsCenter" }}
358
+ flexWrap={{ default: "wrap" }}
359
+ >
360
+ {data.map((item) => {
361
+ const label = legendLabelFormatter
362
+ ? legendLabelFormatter({
363
+ x: item.name,
364
+ countDisplay: item.countDisplay,
365
+ })
366
+ : item.name;
367
+
368
+ const content = (
369
+ <>
370
+ <svg
371
+ width="10"
372
+ height="10"
373
+ aria-hidden="true"
374
+ className={styles.legendIcon}
375
+ >
376
+ <title>Legend color indicator</title>
377
+ <rect
378
+ width="10"
379
+ height="10"
380
+ fill={getColor(item.legendCategory)}
381
+ />
382
+ </svg>
383
+ <span>{label}</span>
384
+ </>
385
+ );
386
+
387
+ return (
388
+ <FlexItem key={`${item.legendCategory}-${item.name}`}>
389
+ {onItemClick ? (
390
+ <button
391
+ type="button"
392
+ onClick={() => onItemClick(item)}
393
+ className={styles.legendButton}
394
+ >
395
+ {content}
396
+ </button>
397
+ ) : (
398
+ <span className={styles.legendLabel}>{content}</span>
399
+ )}
400
+ </FlexItem>
401
+ );
402
+ })}
403
+ </Flex>
404
+ </Flex>
405
+ );
406
+
407
+ const chartLegendElement: ReactElement = (
408
+ <FlexItem flex={{ default: "flex_1" }}>
409
+ <Flex
410
+ justifyContent={{ default: "justifyContentCenter" }}
411
+ alignItems={{ default: "alignItemsCenter" }}
412
+ >
413
+ <FlexItem>
414
+ <ChartLegend
415
+ data={legendData}
416
+ orientation="horizontal"
417
+ height={200}
418
+ width={legendWidthValue}
419
+ x={legendX}
420
+ itemsPerRow={itemsPerRow}
421
+ style={{
422
+ labels: {
423
+ fontSize: labelFontSize,
424
+ fill: "var(--pf-t--global--text--color--regular)",
425
+ },
426
+ }}
427
+ />
428
+ </FlexItem>
429
+ </Flex>
430
+ </FlexItem>
431
+ );
432
+
235
433
  return (
236
434
  <Flex
237
435
  direction={{ default: "column" }}
@@ -243,6 +441,7 @@ export const MigrationDonutChart: FC<MigrationDonutChartProps> = ({
243
441
  ariaDesc="Migration data donut chart"
244
442
  data={chartData}
245
443
  events={chartEvents}
444
+ labelComponent={donutTooltip}
246
445
  labels={({
247
446
  datum,
248
447
  }: {
@@ -252,21 +451,7 @@ export const MigrationDonutChart: FC<MigrationDonutChartProps> = ({
252
451
  legendCategory: string;
253
452
  countDisplay?: string | number;
254
453
  };
255
- }) => {
256
- const percent = totalY > 0 ? (Number(datum.y) / totalY) * 100 : 0;
257
- return tooltipLabelFormatter
258
- ? tooltipLabelFormatter({
259
- datum: {
260
- x: datum.x,
261
- y: Number(datum.y),
262
- countDisplay: datum.countDisplay,
263
- legendCategory: datum.legendCategory,
264
- },
265
- percent,
266
- total: totalY,
267
- })
268
- : `${datum.x}: ${datum.countDisplay ?? datum.y}`;
269
- }}
454
+ }) => formatLabel(datum)}
270
455
  colorScale={colorScale}
271
456
  constrainToVisibleArea
272
457
  innerRadius={innerRadius}
@@ -297,6 +482,7 @@ export const MigrationDonutChart: FC<MigrationDonutChartProps> = ({
297
482
  subTitleComponent={
298
483
  subTitle ? (
299
484
  <ChartLabel
485
+ dy={showChartLegend ? 7 : undefined}
300
486
  style={[
301
487
  {
302
488
  fill: subTitleColor,
@@ -334,63 +520,7 @@ export const MigrationDonutChart: FC<MigrationDonutChartProps> = ({
334
520
  />
335
521
  )}
336
522
  </div>
337
- <Flex
338
- className={`${styles.legendContainer} ${css`margin-left: ${marginLeft};`}`}
339
- justifyContent={{ default: "justifyContentCenter" }}
340
- alignItems={{ default: "alignItemsFlexStart" }}
341
- >
342
- <Flex
343
- className={styles.legendInner}
344
- spaceItems={{ default: "spaceItemsMd" }}
345
- justifyContent={{ default: "justifyContentCenter" }}
346
- alignItems={{ default: "alignItemsCenter" }}
347
- flexWrap={{ default: "wrap" }}
348
- >
349
- {data.map((item) => {
350
- const label = legendLabelFormatter
351
- ? legendLabelFormatter({
352
- x: item.name,
353
- countDisplay: item.countDisplay,
354
- })
355
- : item.name;
356
-
357
- const content = (
358
- <>
359
- <svg
360
- width="10"
361
- height="10"
362
- aria-hidden="true"
363
- className={styles.legendIcon}
364
- >
365
- <title>Legend color indicator</title>
366
- <rect
367
- width="10"
368
- height="10"
369
- fill={getColor(item.legendCategory)}
370
- />
371
- </svg>
372
- <span>{label}</span>
373
- </>
374
- );
375
-
376
- return (
377
- <FlexItem key={`${item.legendCategory}-${item.name}`}>
378
- {onItemClick ? (
379
- <button
380
- type="button"
381
- onClick={() => onItemClick(item)}
382
- className={styles.legendButton}
383
- >
384
- {content}
385
- </button>
386
- ) : (
387
- <span className={styles.legendLabel}>{content}</span>
388
- )}
389
- </FlexItem>
390
- );
391
- })}
392
- </Flex>
393
- </Flex>
523
+ {showChartLegend ? chartLegendElement : htmlLegend}
394
524
  </Flex>
395
525
  );
396
526
  };
package/src/index.ts CHANGED
@@ -1,3 +1,3 @@
1
- export * from "./charts/index.js";
2
- export * from "./forms/index.js";
3
- export * from "./report/index.js";
1
+ export * from "./charts";
2
+ export * from "./forms";
3
+ export * from "./report";
@@ -1,5 +1,7 @@
1
- import { chart_color_blue_300 } from "@patternfly/react-tokens/dist/esm/chart_color_blue_300";
2
- import { chart_color_yellow_300 } from "@patternfly/react-tokens/dist/esm/chart_color_yellow_300";
1
+ import {
2
+ chart_color_blue_300,
3
+ chart_color_yellow_300,
4
+ } from "@patternfly/react-tokens";
3
5
 
4
6
  /**
5
7
  * PatternFly chart color semantics (https://www.patternfly.org/charts/colors-for-charts/design-guidelines/#best-practices):
@@ -1,12 +1,14 @@
1
1
  import type { LabelProps } from "@patternfly/react-core";
2
- import { t_color_blue_20 } from "@patternfly/react-tokens/dist/esm/t_color_blue_20";
3
- import { t_color_blue_60 } from "@patternfly/react-tokens/dist/esm/t_color_blue_60";
4
- import { t_color_green_20 } from "@patternfly/react-tokens/dist/esm/t_color_green_20";
5
- import { t_color_green_60 } from "@patternfly/react-tokens/dist/esm/t_color_green_60";
6
- import { t_color_orange_20 } from "@patternfly/react-tokens/dist/esm/t_color_orange_20";
7
- import { t_color_orange_60 } from "@patternfly/react-tokens/dist/esm/t_color_orange_60";
8
- import { t_color_yellow_20 } from "@patternfly/react-tokens/dist/esm/t_color_yellow_20";
9
- import { t_color_yellow_60 } from "@patternfly/react-tokens/dist/esm/t_color_yellow_60";
2
+ import {
3
+ t_color_blue_20,
4
+ t_color_blue_60,
5
+ t_color_green_20,
6
+ t_color_green_60,
7
+ t_color_orange_20,
8
+ t_color_orange_60,
9
+ t_color_yellow_20,
10
+ t_color_yellow_60,
11
+ } from "@patternfly/react-tokens";
10
12
 
11
13
  /**
12
14
  * Guest OS support tiers (matches agent-sdk / planner-sdk `OsInfoSupportTierEnum` values).