@openshift-migration-advisor/shared-components 0.27.0-ad43dbdf05ab → 0.28.0-16fc442157c1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/charts/MigrationDonutChart.d.ts.map +1 -1
- package/dist/charts/MigrationDonutChart.js +7 -0
- package/dist/report/ChartExportProvider.d.ts +1 -1
- package/dist/report/ChartExportProvider.d.ts.map +1 -1
- package/dist/report/ChartExportProvider.js +4 -1
- package/dist/report/FeatureStatusBadge.js +1 -1
- package/dist/report/captureChartElement.d.ts +6 -1
- package/dist/report/captureChartElement.d.ts.map +1 -1
- package/dist/report/captureChartElement.js +13 -4
- package/dist/report/chartExport.js +1 -1
- package/dist/report/dashboardStyles.d.ts +6 -0
- package/dist/report/dashboardStyles.d.ts.map +1 -1
- package/dist/report/dashboardStyles.js +7 -4
- package/package.json +2 -2
- package/src/charts/MigrationDonutChart.tsx +9 -0
- package/src/report/ChartExportProvider.tsx +5 -1
- package/src/report/FeatureStatusBadge.tsx +1 -1
- package/src/report/__tests__/captureChartElement.test.ts +47 -0
- package/src/report/captureChartElement.ts +18 -3
- package/src/report/chartExport.ts +1 -1
- package/src/report/dashboardStyles.ts +7 -4
|
@@ -1 +1 @@
|
|
|
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;
|
|
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;AAoFD,eAAO,MAAM,mBAAmB,EAAE,EAAE,CAAC,wBAAwB,CAoX5D,CAAC;AAEF,eAAe,mBAAmB,CAAC"}
|
|
@@ -35,6 +35,8 @@ const themedChartTooltipFlyoutPadding = {
|
|
|
35
35
|
left: 10,
|
|
36
36
|
right: 10,
|
|
37
37
|
};
|
|
38
|
+
/** Victory SVG text defaults to a serif face; match PatternFly / html2canvas export. */
|
|
39
|
+
const chartTextFontFamily = "RedHatText, Overpass, Helvetica, Arial, sans-serif";
|
|
38
40
|
const styles = {
|
|
39
41
|
legendIcon: css `
|
|
40
42
|
margin-right: 4px;
|
|
@@ -63,6 +65,7 @@ const styles = {
|
|
|
63
65
|
background: none;
|
|
64
66
|
padding: var(--pf-t--global--spacer--xs) var(--pf-t--global--spacer--ml);
|
|
65
67
|
margin: 0;
|
|
68
|
+
font-family: ${chartTextFontFamily};
|
|
66
69
|
transition: opacity var(--pf-t--global--motion--duration--short);
|
|
67
70
|
white-space: nowrap;
|
|
68
71
|
|
|
@@ -73,6 +76,7 @@ const styles = {
|
|
|
73
76
|
legendLabel: css `
|
|
74
77
|
gap: var(--pf-t--global--spacer--lg);
|
|
75
78
|
padding: var(--pf-t--global--spacer--xs) var(--pf-t--global--spacer--ml);
|
|
79
|
+
font-family: ${chartTextFontFamily};
|
|
76
80
|
white-space: nowrap;
|
|
77
81
|
`,
|
|
78
82
|
legendInner: css `
|
|
@@ -214,6 +218,7 @@ export const MigrationDonutChart = ({ data, legend, customColors, height = 260,
|
|
|
214
218
|
labels: {
|
|
215
219
|
fontSize: labelFontSize,
|
|
216
220
|
fill: "var(--pf-t--global--text--color--regular)",
|
|
221
|
+
fontFamily: chartTextFontFamily,
|
|
217
222
|
},
|
|
218
223
|
} }) }) }) }));
|
|
219
224
|
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: {
|
|
@@ -226,11 +231,13 @@ export const MigrationDonutChart = ({ data, legend, customColors, height = 260,
|
|
|
226
231
|
fill: titleColor,
|
|
227
232
|
fontSize: titleFontSize,
|
|
228
233
|
fontWeight: "bold",
|
|
234
|
+
fontFamily: chartTextFontFamily,
|
|
229
235
|
},
|
|
230
236
|
] })) : undefined, subTitleComponent: subTitle ? (_jsx(ChartLabel, { dy: showChartLegend ? 7 : undefined, style: [
|
|
231
237
|
{
|
|
232
238
|
fill: subTitleColor,
|
|
233
239
|
fontSize: subTitleFontSize,
|
|
240
|
+
fontFamily: chartTextFontFamily,
|
|
234
241
|
},
|
|
235
242
|
] })) : undefined }), onTitleClick && title && (_jsx("div", { onClick: onTitleClick, className: css `
|
|
236
243
|
position: absolute;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type FC, type ReactNode } from "react";
|
|
2
2
|
import { type ChartCaptureSource, type ChartExportFile, type PdfTextPage } from "./chartExport.js";
|
|
3
|
-
export type CaptureChartElement = (element: HTMLElement) => Promise<HTMLCanvasElement>;
|
|
3
|
+
export type CaptureChartElement = (element: HTMLElement, options?: import("./captureChartElement.js").CaptureChartElementOptions) => Promise<HTMLCanvasElement>;
|
|
4
4
|
export type ZipChartFiles = (files: ChartExportFile[]) => Promise<Blob>;
|
|
5
5
|
export type EncodeChartPng = (canvas: HTMLCanvasElement) => Promise<Blob>;
|
|
6
6
|
export type BuildChartDocument = (charts: ChartCaptureSource[], documentTitle: string) => Promise<Blob>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChartExportProvider.d.ts","sourceRoot":"","sources":["../../src/report/ChartExportProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EAAE,EACP,KAAK,SAAS,EAKf,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"ChartExportProvider.d.ts","sourceRoot":"","sources":["../../src/report/ChartExportProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,EAAE,EACP,KAAK,SAAS,EAKf,MAAM,OAAO,CAAC;AAEf,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,eAAe,EAGpB,KAAK,WAAW,EAMjB,MAAM,kBAAkB,CAAC;AAU1B,MAAM,MAAM,mBAAmB,GAAG,CAChC,OAAO,EAAE,WAAW,EACpB,OAAO,CAAC,EAAE,OAAO,0BAA0B,EAAE,0BAA0B,KACpE,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAChC,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,eAAe,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AACxE,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAC1E,MAAM,MAAM,kBAAkB,GAAG,CAC/B,MAAM,EAAE,kBAAkB,EAAE,EAC5B,aAAa,EAAE,MAAM,KAClB,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,MAAM,MAAM,gBAAgB,GAAG,CAC7B,MAAM,EAAE,kBAAkB,EAAE,EAC5B,aAAa,EAAE,MAAM,EACrB,UAAU,CAAC,EAAE,WAAW,EAAE,KACvB,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,YAAY,EACV,cAAc,EACd,iBAAiB,GAClB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3E,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,SAAS,CAAC;IACpB,OAAO,CAAC,EAAE,mBAAmB,CAAC;IAC9B,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IACtD,cAAc,CAAC,EAAE,MAAM,MAAM,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,MAAM,CAAC;IAC9B,eAAe,CAAC,EAAE,MAAM,MAAM,CAAC;CAChC;AAsED,eAAO,MAAM,mBAAmB,EAAE,EAAE,CAAC,wBAAwB,CAmQ5D,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useMemo, useRef, useState, } from "react";
|
|
3
|
+
import { CHART_CAPTURE_SCALE_SINGLE } from "./captureChartElement.js";
|
|
3
4
|
import { canvasToBlob, downloadBlob, releaseCanvas, restoreChartExportViews, sortRegisteredChartsByDocumentOrder, toBulkChartCaptureSources, } from "./chartExport.js";
|
|
4
5
|
import { ChartExportApiContext, ChartExportRegistryContext, } from "./chartExportContext.js";
|
|
5
6
|
import { getChartExportFilename } from "./chartExportFilenames.js";
|
|
@@ -108,7 +109,9 @@ export const ChartExportProvider = ({ children, capture, zipFiles, encodePng = c
|
|
|
108
109
|
throw new Error("The chart is not ready to export yet.");
|
|
109
110
|
}
|
|
110
111
|
const captureFn = await resolveCapture(capture);
|
|
111
|
-
const canvas = await captureFn(chart.element
|
|
112
|
+
const canvas = await captureFn(chart.element, {
|
|
113
|
+
scale: CHART_CAPTURE_SCALE_SINGLE,
|
|
114
|
+
});
|
|
112
115
|
try {
|
|
113
116
|
downloadFile(await encodePng(canvas), chart.filename);
|
|
114
117
|
}
|
|
@@ -8,6 +8,6 @@ export const FeatureStatusBadge = ({ status }) => {
|
|
|
8
8
|
if (status === "enabled") {
|
|
9
9
|
return (_jsx(Label, { color: "green", isCompact: true, icon: _jsx(CheckCircleIcon, {}), children: "Enabled" }));
|
|
10
10
|
}
|
|
11
|
-
return (_jsx(Label, {
|
|
11
|
+
return (_jsx(Label, { variant: "outline", isCompact: true, icon: _jsx(TimesCircleIcon, {}), children: "Disabled" }));
|
|
12
12
|
};
|
|
13
13
|
FeatureStatusBadge.displayName = "FeatureStatusBadge";
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
export declare const CHART_CAPTURE_SCALE_SINGLE = 2;
|
|
2
|
+
export type CaptureChartElementOptions = {
|
|
3
|
+
/** Higher scale sharpens text in PNG exports; keep at 1 for bulk PDF/zip. */
|
|
4
|
+
scale?: number;
|
|
5
|
+
};
|
|
1
6
|
/**
|
|
2
7
|
* Tab panels stay mounted with `hidden` / `display:none` when another tab is
|
|
3
8
|
* active. html2canvas then measures a 0×0 box. Reveal those ancestors off-screen
|
|
4
9
|
* for the duration of the capture, then restore.
|
|
5
10
|
*/
|
|
6
11
|
export declare function revealHiddenChartAncestors(element: HTMLElement): () => void;
|
|
7
|
-
export declare function captureChartElement(element: HTMLElement): Promise<HTMLCanvasElement>;
|
|
12
|
+
export declare function captureChartElement(element: HTMLElement, options?: CaptureChartElementOptions): Promise<HTMLCanvasElement>;
|
|
8
13
|
//# sourceMappingURL=captureChartElement.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"captureChartElement.d.ts","sourceRoot":"","sources":["../../src/report/captureChartElement.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"captureChartElement.d.ts","sourceRoot":"","sources":["../../src/report/captureChartElement.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,0BAA0B,IAAI,CAAC;AAG5C,MAAM,MAAM,0BAA0B,GAAG;IACvC,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAwBF;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,WAAW,GAAG,MAAM,IAAI,CAqD3E;AAwQD,wBAAsB,mBAAmB,CACvC,OAAO,EAAE,WAAW,EACpB,OAAO,GAAE,0BAA+B,GACvC,OAAO,CAAC,iBAAiB,CAAC,CA0C5B"}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { t_color_gray_20, t_color_white, t_global_text_color_regular, } from "@patternfly/react-tokens";
|
|
2
2
|
import html2canvas from "html2canvas-pro";
|
|
3
3
|
import { applyLightThemeToDocument, CHART_EXPORT_CAPTURING_ATTR, CHART_EXPORT_SCROLL_ATTR, shouldIgnoreChartExportElement, } from "./chartExport.js";
|
|
4
|
-
/**
|
|
5
|
-
const
|
|
4
|
+
/** Default scale for bulk exports; single-chart PNG uses a higher scale. */
|
|
5
|
+
const CHART_CAPTURE_SCALE_DEFAULT = 1;
|
|
6
|
+
export const CHART_CAPTURE_SCALE_SINGLE = 2;
|
|
7
|
+
const EXPORT_FONT_FAMILY = "RedHatText, Overpass, Helvetica, Arial, sans-serif";
|
|
6
8
|
const HIDDEN_LAYOUT_STYLE_KEYS = [
|
|
7
9
|
"display",
|
|
8
10
|
"position",
|
|
@@ -140,6 +142,7 @@ function applyLightThemeSvgText(root) {
|
|
|
140
142
|
}
|
|
141
143
|
node.style.fill = LIGHT_EXPORT_TEXT;
|
|
142
144
|
node.style.color = LIGHT_EXPORT_TEXT;
|
|
145
|
+
node.style.fontFamily = EXPORT_FONT_FAMILY;
|
|
143
146
|
}
|
|
144
147
|
}
|
|
145
148
|
function hideCardBeforePseudo() {
|
|
@@ -266,7 +269,8 @@ function prepareCaptureLayout(element) {
|
|
|
266
269
|
}
|
|
267
270
|
};
|
|
268
271
|
}
|
|
269
|
-
export async function captureChartElement(element) {
|
|
272
|
+
export async function captureChartElement(element, options = {}) {
|
|
273
|
+
const scale = options.scale ?? CHART_CAPTURE_SCALE_DEFAULT;
|
|
270
274
|
const restoreHidden = revealHiddenChartAncestors(element);
|
|
271
275
|
const restoreLayout = prepareCaptureLayout(element);
|
|
272
276
|
element.setAttribute(CHART_EXPORT_CAPTURING_ATTR, "");
|
|
@@ -284,14 +288,19 @@ export async function captureChartElement(element) {
|
|
|
284
288
|
useCORS: true,
|
|
285
289
|
backgroundColor: LIGHT_EXPORT_BACKGROUND,
|
|
286
290
|
logging: false,
|
|
287
|
-
scale
|
|
291
|
+
scale,
|
|
288
292
|
imageTimeout: 0,
|
|
289
293
|
ignoreElements: (node) => node instanceof Element &&
|
|
290
294
|
shouldIgnoreChartExportElement(node, element),
|
|
291
295
|
onclone: (clonedDoc, clonedElement) => {
|
|
296
|
+
// Dropping the dark-theme class off the clone root makes every
|
|
297
|
+
// PatternFly CSS-variable surface (cards, tables, labels) resolve to
|
|
298
|
+
// light automatically — html2canvas reads computed styles from this
|
|
299
|
+
// clone. Only Victory's inlined SVG text needs explicit recoloring.
|
|
292
300
|
applyLightThemeToDocument(clonedDoc);
|
|
293
301
|
applyComputedStylesToClone(element, clonedElement);
|
|
294
302
|
applyLightThemeSvgText(clonedElement);
|
|
303
|
+
clonedElement.style.fontFamily = EXPORT_FONT_FAMILY;
|
|
295
304
|
},
|
|
296
305
|
});
|
|
297
306
|
}
|
|
@@ -3,6 +3,12 @@ export declare const dashboardStyles: {
|
|
|
3
3
|
cardScroll: string;
|
|
4
4
|
equalHeightRow: string;
|
|
5
5
|
equalHeight: string;
|
|
6
|
+
/**
|
|
7
|
+
* Flex child of {@link dashboardStyles.card}. `min-height: 0` lets the body
|
|
8
|
+
* shrink inside the card max-height; `overflow: auto` scrolls tables and tall
|
|
9
|
+
* charts when needed (phantom scrollbars on donut cards are avoided by the
|
|
10
|
+
* card layout and ChartExportSurface sizing, not a second body class).
|
|
11
|
+
*/
|
|
6
12
|
cardBodyScrollable: string;
|
|
7
13
|
card: string;
|
|
8
14
|
storageCardOverflowHidden: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboardStyles.d.ts","sourceRoot":"","sources":["../../src/report/dashboardStyles.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,eAAe
|
|
1
|
+
{"version":3,"file":"dashboardStyles.d.ts","sourceRoot":"","sources":["../../src/report/dashboardStyles.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,eAAe;;;;;IA8B1B;;;;;OAKG;;;;;;;;;CAkDJ,CAAC;AAEF,eAAO,MAAM,mBAAmB,QAM/B,CAAC"}
|
|
@@ -25,6 +25,12 @@ export const dashboardStyles = {
|
|
|
25
25
|
flex: 1 1 0;
|
|
26
26
|
height: 100%;
|
|
27
27
|
`,
|
|
28
|
+
/**
|
|
29
|
+
* Flex child of {@link dashboardStyles.card}. `min-height: 0` lets the body
|
|
30
|
+
* shrink inside the card max-height; `overflow: auto` scrolls tables and tall
|
|
31
|
+
* charts when needed (phantom scrollbars on donut cards are avoided by the
|
|
32
|
+
* card layout and ChartExportSurface sizing, not a second body class).
|
|
33
|
+
*/
|
|
28
34
|
cardBodyScrollable: css `
|
|
29
35
|
overflow: auto;
|
|
30
36
|
`,
|
|
@@ -32,12 +38,9 @@ export const dashboardStyles = {
|
|
|
32
38
|
// at the same specificity, which wins in the production build due to stylesheet
|
|
33
39
|
// injection order. `!important` forces `hidden` regardless of that order.
|
|
34
40
|
card: css `
|
|
35
|
-
|
|
36
|
-
max-height: 520px;
|
|
37
|
-
height: 100%;
|
|
41
|
+
height: 520px;
|
|
38
42
|
display: flex;
|
|
39
43
|
flex-direction: column;
|
|
40
|
-
justify-content: space-between;
|
|
41
44
|
overflow: hidden !important;
|
|
42
45
|
`,
|
|
43
46
|
storageCardOverflowHidden: css `
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openshift-migration-advisor/shared-components",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0-16fc442157c1",
|
|
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",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"README.md"
|
|
47
47
|
],
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"html2canvas-pro": "^2.5.
|
|
49
|
+
"html2canvas-pro": "^2.5.2",
|
|
50
50
|
"jspdf": "^4.2.1",
|
|
51
51
|
"jszip": "^3.10.2"
|
|
52
52
|
}
|
|
@@ -108,6 +108,10 @@ const themedChartTooltipFlyoutPadding = {
|
|
|
108
108
|
right: 10,
|
|
109
109
|
} as const;
|
|
110
110
|
|
|
111
|
+
/** Victory SVG text defaults to a serif face; match PatternFly / html2canvas export. */
|
|
112
|
+
const chartTextFontFamily =
|
|
113
|
+
"RedHatText, Overpass, Helvetica, Arial, sans-serif";
|
|
114
|
+
|
|
111
115
|
const styles = {
|
|
112
116
|
legendIcon: css`
|
|
113
117
|
margin-right: 4px;
|
|
@@ -136,6 +140,7 @@ const styles = {
|
|
|
136
140
|
background: none;
|
|
137
141
|
padding: var(--pf-t--global--spacer--xs) var(--pf-t--global--spacer--ml);
|
|
138
142
|
margin: 0;
|
|
143
|
+
font-family: ${chartTextFontFamily};
|
|
139
144
|
transition: opacity var(--pf-t--global--motion--duration--short);
|
|
140
145
|
white-space: nowrap;
|
|
141
146
|
|
|
@@ -146,6 +151,7 @@ const styles = {
|
|
|
146
151
|
legendLabel: css`
|
|
147
152
|
gap: var(--pf-t--global--spacer--lg);
|
|
148
153
|
padding: var(--pf-t--global--spacer--xs) var(--pf-t--global--spacer--ml);
|
|
154
|
+
font-family: ${chartTextFontFamily};
|
|
149
155
|
white-space: nowrap;
|
|
150
156
|
`,
|
|
151
157
|
legendInner: css`
|
|
@@ -422,6 +428,7 @@ export const MigrationDonutChart: FC<MigrationDonutChartProps> = ({
|
|
|
422
428
|
labels: {
|
|
423
429
|
fontSize: labelFontSize,
|
|
424
430
|
fill: "var(--pf-t--global--text--color--regular)",
|
|
431
|
+
fontFamily: chartTextFontFamily,
|
|
425
432
|
},
|
|
426
433
|
}}
|
|
427
434
|
/>
|
|
@@ -474,6 +481,7 @@ export const MigrationDonutChart: FC<MigrationDonutChartProps> = ({
|
|
|
474
481
|
fill: titleColor,
|
|
475
482
|
fontSize: titleFontSize,
|
|
476
483
|
fontWeight: "bold",
|
|
484
|
+
fontFamily: chartTextFontFamily,
|
|
477
485
|
},
|
|
478
486
|
]}
|
|
479
487
|
/>
|
|
@@ -487,6 +495,7 @@ export const MigrationDonutChart: FC<MigrationDonutChartProps> = ({
|
|
|
487
495
|
{
|
|
488
496
|
fill: subTitleColor,
|
|
489
497
|
fontSize: subTitleFontSize,
|
|
498
|
+
fontFamily: chartTextFontFamily,
|
|
490
499
|
},
|
|
491
500
|
]}
|
|
492
501
|
/>
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
useRef,
|
|
7
7
|
useState,
|
|
8
8
|
} from "react";
|
|
9
|
+
import { CHART_CAPTURE_SCALE_SINGLE } from "./captureChartElement.js";
|
|
9
10
|
import {
|
|
10
11
|
type ChartCaptureSource,
|
|
11
12
|
type ChartExportFile,
|
|
@@ -29,6 +30,7 @@ import { getChartExportFilename } from "./chartExportFilenames.js";
|
|
|
29
30
|
|
|
30
31
|
export type CaptureChartElement = (
|
|
31
32
|
element: HTMLElement,
|
|
33
|
+
options?: import("./captureChartElement.js").CaptureChartElementOptions,
|
|
32
34
|
) => Promise<HTMLCanvasElement>;
|
|
33
35
|
export type ZipChartFiles = (files: ChartExportFile[]) => Promise<Blob>;
|
|
34
36
|
export type EncodeChartPng = (canvas: HTMLCanvasElement) => Promise<Blob>;
|
|
@@ -227,7 +229,9 @@ export const ChartExportProvider: FC<ChartExportProviderProps> = ({
|
|
|
227
229
|
throw new Error("The chart is not ready to export yet.");
|
|
228
230
|
}
|
|
229
231
|
const captureFn = await resolveCapture(capture);
|
|
230
|
-
const canvas = await captureFn(chart.element
|
|
232
|
+
const canvas = await captureFn(chart.element, {
|
|
233
|
+
scale: CHART_CAPTURE_SCALE_SINGLE,
|
|
234
|
+
});
|
|
231
235
|
try {
|
|
232
236
|
downloadFile(await encodePng(canvas), chart.filename);
|
|
233
237
|
} finally {
|
|
@@ -202,4 +202,51 @@ describe("captureChartElement", () => {
|
|
|
202
202
|
).toBe(true);
|
|
203
203
|
expect(card.style.backgroundColor).toBe("rgb(30, 30, 30)");
|
|
204
204
|
});
|
|
205
|
+
|
|
206
|
+
it("relights tables and labels via the clone cascade, not by hand-painting them", async () => {
|
|
207
|
+
document.documentElement.classList.add("pf-v6-theme-dark");
|
|
208
|
+
const root = document.createElement("div");
|
|
209
|
+
const label = document.createElement("span");
|
|
210
|
+
label.className = "pf-v6-c-label pf-m-compact pf-m-outline";
|
|
211
|
+
const labelContent = document.createElement("span");
|
|
212
|
+
labelContent.className = "pf-v6-c-label__content";
|
|
213
|
+
label.append(labelContent);
|
|
214
|
+
root.append(label);
|
|
215
|
+
document.body.append(root);
|
|
216
|
+
|
|
217
|
+
mockedHtml2Canvas.mockImplementation(async (_element, options) => {
|
|
218
|
+
const clonedDoc = document.implementation.createHTMLDocument("export");
|
|
219
|
+
clonedDoc.documentElement.classList.add("pf-v6-theme-dark");
|
|
220
|
+
const cloneRoot = clonedDoc.createElement("div");
|
|
221
|
+
const cloneTh = clonedDoc.createElement("th");
|
|
222
|
+
cloneTh.className = "pf-v6-c-table__th";
|
|
223
|
+
const cloneLabelContent = clonedDoc.createElement("span");
|
|
224
|
+
cloneLabelContent.className = "pf-v6-c-label__content";
|
|
225
|
+
cloneRoot.append(cloneTh, cloneLabelContent);
|
|
226
|
+
clonedDoc.body.append(cloneRoot);
|
|
227
|
+
|
|
228
|
+
await options?.onclone?.(clonedDoc, cloneRoot);
|
|
229
|
+
|
|
230
|
+
// Dropping the dark class off the clone root is what relights these
|
|
231
|
+
// surfaces — the cascade does the work. We must NOT hand-paint inline
|
|
232
|
+
// colors onto PatternFly table/label nodes, and the live page stays dark.
|
|
233
|
+
expect(
|
|
234
|
+
clonedDoc.documentElement.classList.contains("pf-v6-theme-dark"),
|
|
235
|
+
).toBe(false);
|
|
236
|
+
expect(cloneTh.style.backgroundColor).toBe("");
|
|
237
|
+
expect(cloneLabelContent.style.backgroundColor).toBe("");
|
|
238
|
+
|
|
239
|
+
const canvas = document.createElement("canvas");
|
|
240
|
+
canvas.width = 1;
|
|
241
|
+
canvas.height = 1;
|
|
242
|
+
return canvas;
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
await captureChartElement(root);
|
|
246
|
+
|
|
247
|
+
expect(
|
|
248
|
+
document.documentElement.classList.contains("pf-v6-theme-dark"),
|
|
249
|
+
).toBe(true);
|
|
250
|
+
expect(labelContent.style.backgroundColor).toBe("");
|
|
251
|
+
});
|
|
205
252
|
});
|
|
@@ -11,8 +11,15 @@ import {
|
|
|
11
11
|
shouldIgnoreChartExportElement,
|
|
12
12
|
} from "./chartExport.js";
|
|
13
13
|
|
|
14
|
-
/**
|
|
15
|
-
const
|
|
14
|
+
/** Default scale for bulk exports; single-chart PNG uses a higher scale. */
|
|
15
|
+
const CHART_CAPTURE_SCALE_DEFAULT = 1;
|
|
16
|
+
export const CHART_CAPTURE_SCALE_SINGLE = 2;
|
|
17
|
+
const EXPORT_FONT_FAMILY = "RedHatText, Overpass, Helvetica, Arial, sans-serif";
|
|
18
|
+
|
|
19
|
+
export type CaptureChartElementOptions = {
|
|
20
|
+
/** Higher scale sharpens text in PNG exports; keep at 1 for bulk PDF/zip. */
|
|
21
|
+
scale?: number;
|
|
22
|
+
};
|
|
16
23
|
const HIDDEN_LAYOUT_STYLE_KEYS = [
|
|
17
24
|
"display",
|
|
18
25
|
"position",
|
|
@@ -187,6 +194,7 @@ function applyLightThemeSvgText(root: HTMLElement): void {
|
|
|
187
194
|
}
|
|
188
195
|
node.style.fill = LIGHT_EXPORT_TEXT;
|
|
189
196
|
node.style.color = LIGHT_EXPORT_TEXT;
|
|
197
|
+
node.style.fontFamily = EXPORT_FONT_FAMILY;
|
|
190
198
|
}
|
|
191
199
|
}
|
|
192
200
|
|
|
@@ -359,7 +367,9 @@ function prepareCaptureLayout(element: HTMLElement): () => void {
|
|
|
359
367
|
|
|
360
368
|
export async function captureChartElement(
|
|
361
369
|
element: HTMLElement,
|
|
370
|
+
options: CaptureChartElementOptions = {},
|
|
362
371
|
): Promise<HTMLCanvasElement> {
|
|
372
|
+
const scale = options.scale ?? CHART_CAPTURE_SCALE_DEFAULT;
|
|
363
373
|
const restoreHidden = revealHiddenChartAncestors(element);
|
|
364
374
|
const restoreLayout = prepareCaptureLayout(element);
|
|
365
375
|
element.setAttribute(CHART_EXPORT_CAPTURING_ATTR, "");
|
|
@@ -378,15 +388,20 @@ export async function captureChartElement(
|
|
|
378
388
|
useCORS: true,
|
|
379
389
|
backgroundColor: LIGHT_EXPORT_BACKGROUND,
|
|
380
390
|
logging: false,
|
|
381
|
-
scale
|
|
391
|
+
scale,
|
|
382
392
|
imageTimeout: 0,
|
|
383
393
|
ignoreElements: (node) =>
|
|
384
394
|
node instanceof Element &&
|
|
385
395
|
shouldIgnoreChartExportElement(node, element),
|
|
386
396
|
onclone: (clonedDoc, clonedElement) => {
|
|
397
|
+
// Dropping the dark-theme class off the clone root makes every
|
|
398
|
+
// PatternFly CSS-variable surface (cards, tables, labels) resolve to
|
|
399
|
+
// light automatically — html2canvas reads computed styles from this
|
|
400
|
+
// clone. Only Victory's inlined SVG text needs explicit recoloring.
|
|
387
401
|
applyLightThemeToDocument(clonedDoc);
|
|
388
402
|
applyComputedStylesToClone(element, clonedElement);
|
|
389
403
|
applyLightThemeSvgText(clonedElement);
|
|
404
|
+
clonedElement.style.fontFamily = EXPORT_FONT_FAMILY;
|
|
390
405
|
},
|
|
391
406
|
});
|
|
392
407
|
} finally {
|
|
@@ -30,6 +30,12 @@ export const dashboardStyles = {
|
|
|
30
30
|
height: 100%;
|
|
31
31
|
`,
|
|
32
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Flex child of {@link dashboardStyles.card}. `min-height: 0` lets the body
|
|
35
|
+
* shrink inside the card max-height; `overflow: auto` scrolls tables and tall
|
|
36
|
+
* charts when needed (phantom scrollbars on donut cards are avoided by the
|
|
37
|
+
* card layout and ChartExportSurface sizing, not a second body class).
|
|
38
|
+
*/
|
|
33
39
|
cardBodyScrollable: css`
|
|
34
40
|
overflow: auto;
|
|
35
41
|
`,
|
|
@@ -38,12 +44,9 @@ export const dashboardStyles = {
|
|
|
38
44
|
// at the same specificity, which wins in the production build due to stylesheet
|
|
39
45
|
// injection order. `!important` forces `hidden` regardless of that order.
|
|
40
46
|
card: css`
|
|
41
|
-
|
|
42
|
-
max-height: 520px;
|
|
43
|
-
height: 100%;
|
|
47
|
+
height: 520px;
|
|
44
48
|
display: flex;
|
|
45
49
|
flex-direction: column;
|
|
46
|
-
justify-content: space-between;
|
|
47
50
|
overflow: hidden !important;
|
|
48
51
|
`,
|
|
49
52
|
|