From f4c84b0b16450e2ad6fd27094ec817c19aa5327d Mon Sep 17 00:00:00 2001 From: "Mehdi Rachico (mera)" Date: Thu, 9 Jan 2025 17:23:16 +0100 Subject: [PATCH] [IMP] xlsx: support import/export of doughnut,pyramid & horizontal bar charts This commit adds support for exporting & importing the charts mentioned above. Pyramid charts are not supported in Excel, therefore, we do a best effort by exporting them as horizontal bar charts. Task: 3978237 --- src/helpers/figures/charts/pyramid_chart.ts | 33 +++++++++++++++++++- src/types/chart/chart.ts | 4 ++- src/xlsx/conversion/figure_conversion.ts | 2 ++ src/xlsx/extraction/chart_extractor.ts | 10 ++++++ src/xlsx/functions/charts.ts | 8 +++-- tests/__xlsx__/xlsx_demo_data.xlsx | Bin 95549 -> 99442 bytes tests/xlsx/xlsx_export.test.ts | 20 ++++++++++++ tests/xlsx/xlsx_import.test.ts | 22 +++++++++++++ tests/xlsx/xlsx_import_export.test.ts | 24 ++++++++++++++ 9 files changed, 119 insertions(+), 4 deletions(-) diff --git a/src/helpers/figures/charts/pyramid_chart.ts b/src/helpers/figures/charts/pyramid_chart.ts index 4dad4af4a3..ffa43dc2b1 100644 --- a/src/helpers/figures/charts/pyramid_chart.ts +++ b/src/helpers/figures/charts/pyramid_chart.ts @@ -17,19 +17,27 @@ import { CustomizedDataSet, DataSet, DatasetDesign, + ExcelChartDataset, ExcelChartDefinition, } from "../../../types/chart/chart"; import { LegendPosition } from "../../../types/chart/common_chart"; import { PyramidChartDefinition, PyramidChartRuntime } from "../../../types/chart/pyramid_chart"; +import { CellErrorType } from "../../../types/errors"; import { Validator } from "../../../types/validator"; +import { toXlsxHexColor } from "../../../xlsx/helpers/colors"; import { createValidRange } from "../../range"; import { AbstractChart } from "./abstract_chart"; import { + chartFontColor, checkDataset, checkLabelRange, createDataSets, duplicateDataSetsInDuplicatedSheet, duplicateLabelRangeInDuplicatedSheet, + getDefinedAxis, + shouldRemoveFirstLabel, + toExcelDataset, + toExcelLabelRange, transformChartDefinitionWithDataSetsWithZone, updateChartRangesWithDataSets, } from "./chart_common"; @@ -180,7 +188,30 @@ export class PyramidChart extends AbstractChart { } getDefinitionForExcel(): ExcelChartDefinition | undefined { - return undefined; + // Excel does not support pyramid charts, therefore we return a bar + // chart definition with the same data + if (this.aggregated) { + // Excel does not support aggregating labels + return undefined; + } + const dataSets: ExcelChartDataset[] = this.dataSets + .map((ds: DataSet) => toExcelDataset(this.getters, ds)) + .filter((ds) => ds.range !== "" && ds.range !== CellErrorType.InvalidReference); + const labelRange = toExcelLabelRange( + this.getters, + this.labelRange, + shouldRemoveFirstLabel(this.labelRange, this.dataSets[0], this.dataSetsHaveTitle) + ); + const definition = this.getDefinition(); + return { + ...definition, + horizontal: true, + backgroundColor: toXlsxHexColor(this.background || BACKGROUND_CHART_COLOR), + fontColor: toXlsxHexColor(chartFontColor(this.background)), + dataSets, + labelRange, + verticalAxis: getDefinedAxis(definition), + }; } updateRanges(applyChange: ApplyRangeChange): PyramidChart { diff --git a/src/types/chart/chart.ts b/src/types/chart/chart.ts index 66ccb5f743..cd6dac668a 100644 --- a/src/types/chart/chart.ts +++ b/src/types/chart/chart.ts @@ -125,7 +125,7 @@ export interface ExcelChartDataset { readonly rightYAxis?: boolean; } -export type ExcelChartType = "line" | "bar" | "pie" | "combo" | "scatter" | "radar"; +export type ExcelChartType = "line" | "bar" | "pie" | "combo" | "scatter" | "radar" | "pyramid"; export interface ExcelChartDefinition { readonly title?: TitleDesign; @@ -142,6 +142,8 @@ export interface ExcelChartDefinition { useRightAxis?: boolean; }; readonly axesDesign?: AxesDesign; + readonly isDoughnut?: boolean; + readonly horizontal?: boolean; } export interface ChartCreationContext { diff --git a/src/xlsx/conversion/figure_conversion.ts b/src/xlsx/conversion/figure_conversion.ts index ef85977c63..67f9435f65 100644 --- a/src/xlsx/conversion/figure_conversion.ts +++ b/src/xlsx/conversion/figure_conversion.ts @@ -102,6 +102,8 @@ function convertChartData(chartData: ExcelChartDefinition): ChartDefinition | un aggregated: false, cumulative: chartData.cumulative || false, labelsAsText: false, + horizontal: chartData.horizontal, + isDoughnut: chartData.isDoughnut, }; } diff --git a/src/xlsx/extraction/chart_extractor.ts b/src/xlsx/extraction/chart_extractor.ts index b4e8c902e2..934f903775 100644 --- a/src/xlsx/extraction/chart_extractor.ts +++ b/src/xlsx/extraction/chart_extractor.ts @@ -29,6 +29,14 @@ export class XlsxChartExtractor extends XlsxBaseExtractor { default: "clustered", }).asString(); + const chartDirection = this.extractChildAttr(rootChartElement, "c:barDir", "val", { + default: "col", + }).asString(); + + const chartHoleSize = this.extractChildAttr(rootChartElement, "c:holeSize", "val", { + default: "0", + }).asNum(); + return { title: { text: chartTitle }, type: CHART_TYPE_CONVERSION_MAP[chartType]!, @@ -56,6 +64,8 @@ export class XlsxChartExtractor extends XlsxBaseExtractor { ], stacked: barChartGrouping === "stacked", fontColor: "000000", + horizontal: chartDirection === "bar", + isDoughnut: chartHoleSize > 0, }; } )[0]; diff --git a/src/xlsx/functions/charts.ts b/src/xlsx/functions/charts.ts index efdd7ada9f..54d5a6f2a5 100644 --- a/src/xlsx/functions/charts.ts +++ b/src/xlsx/functions/charts.ts @@ -71,6 +71,7 @@ export function createChart( let plot = escapeXml``; switch (chart.data.type) { case "bar": + case "pyramid": plot = addBarChart(chart.data); break; case "combo": @@ -83,7 +84,9 @@ export function createChart( plot = addScatterChart(chart.data); break; case "pie": - plot = addDoughnutChart(chart.data, chartSheetIndex, data, { holeSize: 0 }); + plot = addDoughnutChart(chart.data, chartSheetIndex, data, { + holeSize: chart.data.isDoughnut ? 50 : 0, + }); break; case "radar": plot = addRadarChart(chart.data); @@ -237,6 +240,7 @@ function addBarChart(chart: ExcelChartDefinition): XMLString { // // overlap and gapWitdh seems to be by default at -20 and 20 in chart.js. // See https://www.chartjs.org/docs/latest/charts/bar.html and https://www.chartjs.org/docs/latest/charts/bar.html#barpercentage-vs-categorypercentage + const chartDirection = chart.horizontal ? "bar" : "col"; const dataSetsColors = chart.dataSets.map((ds) => ds.backgroundColor ?? ""); const colors = new ColorGenerator(chart.dataSets.length, dataSetsColors); const leftDataSetsNodes: XMLString[] = []; @@ -276,7 +280,7 @@ function addBarChart(chart: ExcelChartDefinition): XMLString { leftDataSetsNodes.length ? escapeXml/*xml*/ ` - + diff --git a/tests/__xlsx__/xlsx_demo_data.xlsx b/tests/__xlsx__/xlsx_demo_data.xlsx index 230d5e5791f33ea055f45f0d07e92586c67ef96f..6bd63b48b3c5a3c775ac1b047f5e88b4007dc688 100644 GIT binary patch delta 22604 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 diff --git a/tests/xlsx/xlsx_export.test.ts b/tests/xlsx/xlsx_export.test.ts index 1d2930674a..be8e6a488b 100644 --- a/tests/xlsx/xlsx_export.test.ts +++ b/tests/xlsx/xlsx_export.test.ts @@ -798,12 +798,32 @@ describe("Test XLSX export", () => { }, "2" ); + createChart( + model, + { + dataSets: [{ dataRange: "Sheet1!B2:B" }, { dataRange: "Sheet1!C4:4" }], + labelRange: "Sheet1!A2:A", + type: "bar", + horizontal: true, + }, + "2" + ); + createChart( + model, + { + dataSets: [{ dataRange: "Sheet1!B2:B" }, { dataRange: "Sheet1!C4:4" }], + labelRange: "Sheet1!A2:A", + type: "pie", + }, + "3" + ); createChart( model, { dataSets: [{ dataRange: "Sheet1!B2:B" }, { dataRange: "Sheet1!C4:4" }], labelRange: "Sheet1!A2:A", type: "pie", + isDoughnut: true, }, "3" ); diff --git a/tests/xlsx/xlsx_import.test.ts b/tests/xlsx/xlsx_import.test.ts index 4b761511c8..22bd73682b 100644 --- a/tests/xlsx/xlsx_import.test.ts +++ b/tests/xlsx/xlsx_import.test.ts @@ -783,6 +783,28 @@ describe("Import xlsx data", () => { } ); + test("Can import horizontal bar charts", () => { + const testSheet = getWorkbookSheet("jestCharts", convertedData)!; + const figure = testSheet.figures.find( + (figure) => figure.data.title.text === "horizontal bar chart" + )!; + const chartData = figure.data as BarChartDefinition; + expect(chartData.title.text).toEqual("horizontal bar chart"); + expect(chartData.type).toEqual("bar"); + expect(chartData.horizontal).toEqual(true); + }); + + test("Can import doughnut charts", () => { + const testSheet = getWorkbookSheet("jestCharts", convertedData)!; + const figure = testSheet.figures.find( + (figure) => figure.data.title.text === "single dataset doughnut chart" + )!; + const chartData = figure.data as PieChartDefinition; + expect(chartData.title.text).toEqual("single dataset doughnut chart"); + expect(chartData.type).toEqual("pie"); + expect(chartData.isDoughnut).toEqual(true); + }); + test.each([ [ "line chart", diff --git a/tests/xlsx/xlsx_import_export.test.ts b/tests/xlsx/xlsx_import_export.test.ts index a5c3b280b8..46c29705b5 100644 --- a/tests/xlsx/xlsx_import_export.test.ts +++ b/tests/xlsx/xlsx_import_export.test.ts @@ -305,6 +305,18 @@ describe("Export data to xlsx then import it", () => { background: "#AAAAAA", legendPosition: "bottom" as const, stacked: true, + horizontal: false, + }, + { + title: { text: "demo horizontal bar chart 2" }, + dataSets: [{ dataRange: "Sheet1!B27:B35" }, { dataRange: "Sheet1!C27:C35" }], + labelRange: "Sheet1!A27:A35", + type: "bar" as const, + dataSetsHaveTitle: false, + background: "#AAAAAA", + legendPosition: "top" as const, + stacked: true, + horizontal: true, }, { title: { text: "pie demo chart" }, @@ -315,6 +327,18 @@ describe("Export data to xlsx then import it", () => { background: "#FFFFFF", legendPosition: "right" as const, stacked: false, + isDoughnut: false, + }, + { + title: { text: "doughnut demo chart" }, + dataSets: [{ dataRange: "Sheet1!B26:B35" }, { dataRange: "Sheet1!C26:C35" }], + labelRange: "Sheet1!A27:A35", + type: "pie" as const, + dataSetsHaveTitle: false, + background: "#FFFFFF", + legendPosition: "left" as const, + stacked: false, + isDoughnut: true, }, { title: { text: "demo chart4" },