From 449782abe4e6a5da824b63efcaeb4faaada22480 Mon Sep 17 00:00:00 2001 From: Anthony Hendrickx Date: Fri, 6 Sep 2024 12:15:07 +0200 Subject: [PATCH] [IMP] charts: handles multiple bars in combo chart MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Task Description This task adds the possibility to choose which series are bar and line in a combo chart, make the user able to change this for each data series in the design pannel. When there is no bar series in the set, the first one is always considered as a bar chart, to stay consistent with the previous behavior. Related Task closes odoo/o-spreadsheet#4957 Task: 4164614 Signed-off-by: Lucas Lefèvre (lul) --- .../chart/chart_with_axis/design_panel.ts | 7 +- .../combo_chart/combo_chart_design_panel.ts | 39 +++++ .../combo_chart/combo_chart_design_panel.xml | 147 ++++++++++++++++++ src/components/side_panel/chart/index.ts | 3 +- src/helpers/figures/charts/combo_chart.ts | 24 ++- src/types/chart/combo_chart.ts | 4 + tests/figures/chart/chart_plugin.test.ts | 10 +- tests/figures/chart/charts_component.test.ts | 81 +++++----- .../chart/combo_chart_component.test.ts | 78 ++++++++++ .../figures/chart/combo_chart_plugin.test.ts | 38 ++++- .../waterfall_panel_component.test.ts | 21 +-- tests/test_helpers/chart_helpers.ts | 22 ++- 12 files changed, 400 insertions(+), 74 deletions(-) create mode 100644 src/components/side_panel/chart/combo_chart/combo_chart_design_panel.ts create mode 100644 src/components/side_panel/chart/combo_chart/combo_chart_design_panel.xml create mode 100644 tests/figures/chart/combo_chart_component.test.ts diff --git a/src/components/side_panel/chart/chart_with_axis/design_panel.ts b/src/components/side_panel/chart/chart_with_axis/design_panel.ts index b6b24053a3..0108a87533 100644 --- a/src/components/side_panel/chart/chart_with_axis/design_panel.ts +++ b/src/components/side_panel/chart/chart_with_axis/design_panel.ts @@ -29,7 +29,10 @@ interface Props { updateChart: (figureId: UID, definition: Partial) => DispatchResult; } -export class ChartWithAxisDesignPanel extends Component { +export class ChartWithAxisDesignPanel

extends Component< + P, + SpreadsheetChildEnv +> { static template = "o-spreadsheet-ChartWithAxisDesignPanel"; static components = { GeneralDesignEditor, @@ -49,7 +52,7 @@ export class ChartWithAxisDesignPanel extends Component) => DispatchResult; + updateChart: (figureId: UID, definition: Partial) => DispatchResult; +} + +export class ComboChartDesignPanel extends ChartWithAxisDesignPanel { + static template = "o-spreadsheet-ComboChartDesignPanel"; + seriesTypeChoices = [ + { value: "bar", label: _t("Bar") }, + { value: "line", label: _t("Line") }, + ]; + + updateDataSeriesType(type: "bar" | "line") { + const dataSets = [...this.props.definition.dataSets]; + if (!dataSets?.[this.state.index]) { + return; + } + dataSets[this.state.index] = { + ...dataSets[this.state.index], + type, + }; + this.props.updateChart(this.props.figureId, { dataSets }); + } + + getDataSeriesType() { + const dataSets = this.props.definition.dataSets; + if (!dataSets?.[this.state.index]) { + return "bar"; + } + return dataSets[this.state.index].type ?? "line"; + } +} diff --git a/src/components/side_panel/chart/combo_chart/combo_chart_design_panel.xml b/src/components/side_panel/chart/combo_chart/combo_chart_design_panel.xml new file mode 100644 index 0000000000..c41ab4adb4 --- /dev/null +++ b/src/components/side_panel/chart/combo_chart/combo_chart_design_panel.xml @@ -0,0 +1,147 @@ + + + + +

+ Legend position + +
+
+ Values + +
+ + + + Data series + +
+ +
+
+ Series color + +
+
+
+ Vertical axis + +
+
+ Serie type + +
+
+ Series name + +
+
+ Trend line + Show trend line + + + +
+
+
+ Type + +
+
+ Degree + +
+
+
+ Trend line color + +
+
+
+
+
+
+ + Axes + + + + + + diff --git a/src/components/side_panel/chart/index.ts b/src/components/side_panel/chart/index.ts index bfe1bf829c..4c29b031d8 100644 --- a/src/components/side_panel/chart/index.ts +++ b/src/components/side_panel/chart/index.ts @@ -3,6 +3,7 @@ import { Registry } from "../../../registries/registry"; import { BarConfigPanel } from "./bar_chart/bar_chart_config_panel"; import { GenericChartConfigPanel } from "./building_blocks/generic_side_panel/config_panel"; import { ChartWithAxisDesignPanel } from "./chart_with_axis/design_panel"; +import { ComboChartDesignPanel } from "./combo_chart/combo_chart_design_panel"; import { GaugeChartConfigPanel } from "./gauge_chart_panel/gauge_chart_config_panel"; import { GaugeChartDesignPanel } from "./gauge_chart_panel/gauge_chart_design_panel"; import { LineConfigPanel } from "./line_chart/line_chart_config_panel"; @@ -43,7 +44,7 @@ chartSidePanelComponentRegistry }) .add("combo", { configuration: GenericChartConfigPanel, - design: ChartWithAxisDesignPanel, + design: ComboChartDesignPanel, }) .add("pie", { configuration: GenericChartConfigPanel, diff --git a/src/helpers/figures/charts/combo_chart.ts b/src/helpers/figures/charts/combo_chart.ts index 6c271114d5..2034bb6c40 100644 --- a/src/helpers/figures/charts/combo_chart.ts +++ b/src/helpers/figures/charts/combo_chart.ts @@ -18,11 +18,14 @@ import { import { AxesDesign, CustomizedDataSet, - DatasetDesign, ExcelChartDataset, LegendPosition, } from "../../../types/chart"; -import { ComboChartDefinition, ComboChartRuntime } from "../../../types/chart/combo_chart"; +import { + ComboChartDataSet, + ComboChartDefinition, + ComboChartRuntime, +} from "../../../types/chart/combo_chart"; import { CellErrorType } from "../../../types/errors"; import { Validator } from "../../../types/validator"; import { toXlsxHexColor } from "../../../xlsx/helpers/colors"; @@ -64,7 +67,7 @@ export class ComboChart extends AbstractChart { readonly legendPosition: LegendPosition; readonly aggregated?: boolean; readonly dataSetsHaveTitle: boolean; - readonly dataSetDesign?: DatasetDesign[]; + readonly dataSetDesign?: ComboChartDataSet[]; readonly axesDesign?: AxesDesign; readonly type = "combo"; readonly showValues?: boolean; @@ -127,11 +130,12 @@ export class ComboChart extends AbstractChart { labelRange: Range | undefined, targetSheetId?: UID ): ComboChartDefinition { - const ranges: CustomizedDataSet[] = []; + const ranges: ComboChartDataSet[] = []; for (const [i, dataSet] of dataSets.entries()) { ranges.push({ ...this.dataSetDesign?.[i], dataRange: this.getters.getRangeString(dataSet.dataRange, targetSheetId || this.sheetId), + type: this.dataSetDesign?.[i]?.type ?? (i ? "line" : "bar"), }); } return { @@ -189,9 +193,13 @@ export class ComboChart extends AbstractChart { } static getDefinitionFromContextCreation(context: ChartCreationContext): ComboChartDefinition { + const dataSets: ComboChartDataSet[] = (context.range ?? []).map((ds, index) => ({ + ...ds, + type: index ? "line" : "bar", + })); return { background: context.background, - dataSets: context.range ?? [], + dataSets, dataSetsHaveTitle: context.dataSetsHaveTitle ?? false, aggregated: context.aggregated, legendPosition: context.legendPosition ?? "top", @@ -249,7 +257,6 @@ export function createComboChartRuntime(chart: ComboChart, getters: Getters): Co const config = getDefaultChartJsRuntime(chart, labels, fontColor, localeFormat); const legend: DeepPartial> = { labels: { color: fontColor }, - reverse: true, }; if (chart.legendPosition === "none") { legend.display = false; @@ -320,14 +327,15 @@ export function createComboChartRuntime(chart: ComboChart, getters: Getters): Co for (let [index, { label, data }] of dataSetsValues.entries()) { const design = definition.dataSets[index]; const color = colors.next(); + const type = design?.type ?? "line"; const dataset: ChartDataset<"bar" | "line", number[]> = { label: design?.label ?? label, data, borderColor: color, backgroundColor: color, yAxisID: design?.yAxisId ?? "y", - type: index === 0 ? "bar" : "line", - order: -index, + type, + order: type === "bar" ? dataSetsValues.length + index : index, }; config.data.datasets.push(dataset); diff --git a/src/types/chart/combo_chart.ts b/src/types/chart/combo_chart.ts index 815b633b4c..14f67ed156 100644 --- a/src/types/chart/combo_chart.ts +++ b/src/types/chart/combo_chart.ts @@ -1,11 +1,15 @@ import { ChartConfiguration } from "chart.js"; import { Color } from "../misc"; +import { CustomizedDataSet } from "./chart"; import { ComboBarChartDefinition } from "./common_bar_combo"; export interface ComboChartDefinition extends ComboBarChartDefinition { + readonly dataSets: ComboChartDataSet[]; readonly type: "combo"; } +export type ComboChartDataSet = CustomizedDataSet & { type?: "bar" | "line" }; + export type ComboChartRuntime = { chartJsConfig: ChartConfiguration; background: Color; diff --git a/tests/figures/chart/chart_plugin.test.ts b/tests/figures/chart/chart_plugin.test.ts index a62737b759..1917013576 100644 --- a/tests/figures/chart/chart_plugin.test.ts +++ b/tests/figures/chart/chart_plugin.test.ts @@ -1644,7 +1644,7 @@ describe("Chart without labels", () => { expect(getChartConfiguration(model, "44").data?.labels).toEqual(["B1", "B2"]); }); - test("Combo chart has both line and bar", () => { + test("Combo chart has bar if the type is set to bar and line else", () => { setCellContent(model, "A1", "1"); setCellContent(model, "A2", "2"); setCellContent(model, "A3", "3"); @@ -1654,7 +1654,13 @@ describe("Chart without labels", () => { createComboChart( model, - { dataSets: [{ dataRange: "A1:A2" }, { dataRange: "A3:A4" }, { dataRange: "A5:A6" }] }, + { + dataSets: [ + { dataRange: "A1:A2", type: "bar" }, + { dataRange: "A3:A4" }, + { dataRange: "A5:A6" }, + ], + }, "43" ); const dataSets = getChartConfiguration(model, "43").data.datasets; diff --git a/tests/figures/chart/charts_component.test.ts b/tests/figures/chart/charts_component.test.ts index 62d0d40017..0fb5f4f5f4 100644 --- a/tests/figures/chart/charts_component.test.ts +++ b/tests/figures/chart/charts_component.test.ts @@ -13,7 +13,11 @@ import { } from "../../../src/types"; import { BarChartDefinition, BarChartRuntime } from "../../../src/types/chart/bar_chart"; import { LineChartDefinition } from "../../../src/types/chart/line_chart"; -import { getChartConfiguration } from "../../test_helpers/chart_helpers"; +import { + getChartConfiguration, + openChartConfigSidePanel, + openChartDesignSidePanel, +} from "../../test_helpers/chart_helpers"; import { copy, createChart, @@ -79,19 +83,6 @@ function errorMessages(): string[] { return textContentAll(".o-validation-error"); } -async function openChartConfigSidePanel(id = chartId) { - model.dispatch("SELECT_FIGURE", { id }); - env.openSidePanel("ChartPanel"); - await nextTick(); -} - -async function openChartDesignSidePanel(id = chartId) { - if (!fixture.querySelector(".o-chart")) { - await openChartConfigSidePanel(id); - } - await simulateClick(".o-panel-element.inactive"); -} - async function changeChartType(type: string) { triggerMouseEvent(".o-type-selector", "pointerdown"); await nextTick(); @@ -152,7 +143,7 @@ describe("charts", () => { test.each(CHART_TYPES)("Can open a chart sidePanel", async (chartType) => { await mountSpreadsheet(); createTestChart(chartType); - await openChartConfigSidePanel(); + await openChartConfigSidePanel(model, env, chartId); expect(fixture.querySelector(".o-figure")).toBeTruthy(); }); @@ -323,7 +314,7 @@ describe("charts", () => { chartId ); await mountChartSidePanel(); - await openChartDesignSidePanel(chartId); + await openChartDesignSidePanel(model, env, fixture, chartId); const color_menu = fixture.querySelectorAll( ".o-chart-title-designer > .o-color-picker-widget > .o-color-picker-button" @@ -351,7 +342,7 @@ describe("charts", () => { chartId ); await mountChartSidePanel(); - await openChartDesignSidePanel(chartId); + await openChartDesignSidePanel(model, env, fixture, chartId); const alignment_menu = fixture.querySelectorAll( ".o-chart-title-designer > .o-menu-item-button[title='Horizontal alignment']" )[0]; @@ -377,7 +368,7 @@ describe("charts", () => { chartId ); await mountChartSidePanel(); - await openChartDesignSidePanel(chartId); + await openChartDesignSidePanel(model, env, fixture, chartId); const bold_element = fixture.querySelectorAll( ".o-chart-title-designer > .o-menu-item-button[title='Bold']" @@ -410,7 +401,7 @@ describe("charts", () => { chartId ); await mountChartSidePanel(); - await openChartDesignSidePanel(chartId); + await openChartDesignSidePanel(model, env, fixture, chartId); const color_menu = fixture.querySelectorAll( ".o-chart-title-designer > .o-color-picker-widget > .o-color-picker-button" @@ -440,7 +431,7 @@ describe("charts", () => { chartId ); await mountChartSidePanel(); - await openChartDesignSidePanel(chartId); + await openChartDesignSidePanel(model, env, fixture, chartId); const alignment_menu = fixture.querySelectorAll( ".o-chart-title-designer > .o-menu-item-button[title='Horizontal alignment']" )[1]; @@ -468,7 +459,7 @@ describe("charts", () => { chartId ); await mountChartSidePanel(); - await openChartDesignSidePanel(chartId); + await openChartDesignSidePanel(model, env, fixture, chartId); const bold_element = fixture.querySelectorAll( ".o-chart-title-designer > .o-menu-item-button[title='Bold']" @@ -506,7 +497,7 @@ describe("charts", () => { chartId ); await mountChartSidePanel(); - await openChartDesignSidePanel(chartId); + await openChartDesignSidePanel(model, env, fixture, chartId); const bold_element = fixture.querySelectorAll( ".o-chart-title-designer > .o-menu-item-button[title='Bold']" @@ -553,7 +544,7 @@ describe("charts", () => { chartId ); await mountChartSidePanel(); - await openChartDesignSidePanel(chartId); + await openChartDesignSidePanel(model, env, fixture, chartId); let color_menu = fixture.querySelectorAll(".o-round-color-picker-button")[1]; @@ -604,7 +595,7 @@ describe("charts", () => { chartId ); await mountChartSidePanel(); - await openChartDesignSidePanel(); + await openChartDesignSidePanel(model, env, fixture, chartId); await click(fixture, ".o-vertical-axis-selection input[value=right]"); //@ts-ignore @@ -627,7 +618,7 @@ describe("charts", () => { chartId ); await mountChartSidePanel(); - await openChartDesignSidePanel(); + await openChartDesignSidePanel(model, env, fixture, chartId); setInputValueAndTrigger(".o-serie-label-editor", "coucou"); //@ts-ignore @@ -646,7 +637,7 @@ describe("charts", () => { chartId ); await mountChartSidePanel(chartId); - await openChartDesignSidePanel(chartId); + await openChartDesignSidePanel(model, env, fixture, chartId); expect(1).toBe(1); }); @@ -672,7 +663,7 @@ describe("charts", () => { "2" ); await mountSpreadsheet(); - await openChartDesignSidePanel("1"); + await openChartDesignSidePanel(model, env, fixture, "1"); await simulateClick(".o-chart-title input"); setInputValueAndTrigger(".o-chart-title input", "first_title", "onlyInput"); @@ -705,7 +696,7 @@ describe("charts", () => { "2" ); await mountSpreadsheet(); - await openChartDesignSidePanel("1"); + await openChartDesignSidePanel(model, env, fixture, "1"); const figures = fixture.querySelectorAll(".o-figure"); await simulateClick(figures[1] as HTMLElement); @@ -721,7 +712,7 @@ describe("charts", () => { async (chartType) => { createTestChart(chartType); await mountSpreadsheet(); - await openChartDesignSidePanel(); + await openChartDesignSidePanel(model, env, fixture, chartId); await simulateClick(".o-chart-title input"); const chartTitle = document.querySelector(".o-chart-title input") as HTMLInputElement; @@ -738,7 +729,7 @@ describe("charts", () => { createTestChart(chartType); await mountSpreadsheet(); const dispatch = spyModelDispatch(model); - await openChartDesignSidePanel(); + await openChartDesignSidePanel(model, env, fixture, chartId); expect(fixture.querySelector(".o-chart")).toBeTruthy(); await simulateClick(".o-round-color-picker-button"); @@ -770,7 +761,7 @@ describe("charts", () => { async (chartType) => { createTestChart(chartType); await mountChartSidePanel(); - await openChartDesignSidePanel(); + await openChartDesignSidePanel(model, env, fixture, chartId); await simulateClick(".o-color-picker-widget .o-color-picker-button"); expect(fixture.querySelector(".o-color-picker")).toBeTruthy(); @@ -804,7 +795,7 @@ describe("charts", () => { test("drawing of chart will receive new data after update", async () => { createTestChart("basicChart"); await mountSpreadsheet(); - await openChartConfigSidePanel(); + await openChartConfigSidePanel(model, env, chartId); const dataSeries = fixture.querySelectorAll(".o-chart .o-data-series")[0] as HTMLInputElement; const dataSeriesValues = dataSeries.querySelector("input"); @@ -863,7 +854,7 @@ describe("charts", () => { test("Deleting a chart with active selection input does not produce a traceback", async () => { createTestChart("basicChart"); await mountSpreadsheet(); - await openChartConfigSidePanel(); + await openChartConfigSidePanel(model, env, chartId); await simulateClick(".o-data-series .o-add-selection"); const element = document.querySelectorAll(".o-data-series input")[0]; @@ -877,7 +868,7 @@ describe("charts", () => { test("Undo a chart insertion will close the chart side panel", async () => { createTestChart("basicChart"); await mountSpreadsheet(); - await openChartConfigSidePanel(); + await openChartConfigSidePanel(model, env, chartId); undo(model); await nextTick(); expect(fixture.querySelector(".o-chart")).toBeFalsy(); @@ -910,7 +901,7 @@ describe("charts", () => { "secondChartId" ); await mountSpreadsheet(); - await openChartConfigSidePanel(); + await openChartConfigSidePanel(model, env, chartId); expect(fixture.querySelector(".o-chart")).toBeTruthy(); const figures = fixture.querySelectorAll(".o-figure"); @@ -1133,7 +1124,7 @@ describe("charts", () => { test("Can change gauge inflection operator", async () => { createTestChart("gauge"); await mountChartSidePanel(); - await openChartDesignSidePanel(); + await openChartDesignSidePanel(model, env, fixture, chartId); expect(model.getters.getChartDefinition(chartId)).toMatchObject({ sectionRule: { @@ -1165,7 +1156,7 @@ describe("charts", () => { beforeEach(async () => { createTestChart("gauge"); await mountChartSidePanel(); - await openChartDesignSidePanel(); + await openChartDesignSidePanel(model, env, fixture, chartId); }); test("empty rangeMin", async () => { @@ -1322,7 +1313,7 @@ describe("charts", () => { createTestChart(chartType); updateChart(model, chartId, { keyValue: undefined, dataRange: undefined, dataSets: [] }); await mountSpreadsheet(); - await openChartConfigSidePanel(); + await openChartConfigSidePanel(model, env, chartId); const input = fixture.querySelector(".o-selection input"); await simulateClick(input); @@ -1335,7 +1326,7 @@ describe("charts", () => { createTestChart("scorecard"); const dispatch = spyModelDispatch(model); await mountChartSidePanel(); - await openChartDesignSidePanel(); + await openChartDesignSidePanel(model, env, fixture, chartId); // Change color of "up" value of baseline const colorpickerUpButton = fixture.querySelectorAll( @@ -1538,7 +1529,7 @@ describe("charts", () => { dataSets: [{ dataRange: "B2:B4" }], }); await mountChartSidePanel(); - await openChartDesignSidePanel(); + await openChartDesignSidePanel(model, env, fixture, chartId); expect( (model.getters.getChartDefinition(chartId) as LineChartDefinition).showValues @@ -1642,7 +1633,7 @@ describe("charts", () => { sheetId ); await mountChartSidePanel(chartId); - await openChartDesignSidePanel(chartId); + await openChartDesignSidePanel(model, env, fixture, chartId); const checkbox = document.querySelector("input[name='showTrendLine']") as HTMLInputElement; expect(checkbox.checked).toBe(false); @@ -1688,7 +1679,7 @@ describe("charts", () => { sheetId ); await mountChartSidePanel(chartId); - await openChartDesignSidePanel(chartId); + await openChartDesignSidePanel(model, env, fixture, chartId); let definition = model.getters.getChartDefinition(chartId) as ChartWithAxisDefinition; expect(definition.dataSets[0].trend).toEqual({ @@ -1727,7 +1718,7 @@ describe("charts", () => { sheetId ); await mountChartSidePanel(chartId); - await openChartDesignSidePanel(chartId); + await openChartDesignSidePanel(model, env, fixture, chartId); let definition = model.getters.getChartDefinition(chartId) as ChartWithAxisDefinition; expect(definition.dataSets[0].trend).toEqual({ @@ -1763,7 +1754,7 @@ describe("charts", () => { sheetId ); await mountChartSidePanel(chartId); - await openChartDesignSidePanel(chartId); + await openChartDesignSidePanel(model, env, fixture, chartId); let runtime = model.getters.getChartRuntime(chartId) as BarChartRuntime; expect(runtime.chartJsConfig.data.datasets[1].backgroundColor).toBe("#FF8080"); @@ -1831,7 +1822,7 @@ describe("charts", () => { test("Cannot change series axis on horizontal bar chart", async () => { createChart(model, { type: "bar", horizontal: true }, chartId); await mountChartSidePanel(); - await openChartDesignSidePanel(); + await openChartDesignSidePanel(model, env, fixture, chartId); expect(fixture.querySelector(".o-vertical-axis-selection ")).toBeNull(); }); }); diff --git a/tests/figures/chart/combo_chart_component.test.ts b/tests/figures/chart/combo_chart_component.test.ts new file mode 100644 index 0000000000..c5f1240ce9 --- /dev/null +++ b/tests/figures/chart/combo_chart_component.test.ts @@ -0,0 +1,78 @@ +import { Model } from "../../../src"; +import { ChartPanel } from "../../../src/components/side_panel/chart/main_chart_panel/main_chart_panel"; +import { SpreadsheetChildEnv } from "../../../src/types"; +import { openChartDesignSidePanel } from "../../test_helpers/chart_helpers"; +import { createChart } from "../../test_helpers/commands_helpers"; +import { click } from "../../test_helpers/dom_helper"; +import { mountComponentWithPortalTarget } from "../../test_helpers/helpers"; + +async function mountChartSidePanel(figureId = chartId) { + const props = { figureId, onCloseSidePanel: () => {} }; + ({ fixture, env } = await mountComponentWithPortalTarget(ChartPanel, { props, model })); +} + +let fixture: HTMLElement; +let model: Model; +const chartId = "someuuid"; +let sheetId: string; + +let env: SpreadsheetChildEnv; + +describe("combo charts", () => { + beforeEach(async () => { + sheetId = "Sheet1"; + const data = { + sheets: [ + { + name: sheetId, + colNumber: 10, + rowNumber: 10, + rows: {}, + cells: { + B1: { content: "first column dataset" }, + C1: { content: "second column dataset" }, + B2: { content: "10" }, + B3: { content: "11" }, + B4: { content: "12" }, + B5: { content: "13" }, + C2: { content: "20" }, + C3: { content: "19" }, + C4: { content: "18" }, + A2: { content: "P1" }, + A3: { content: "P2" }, + A4: { content: "P3" }, + A5: { content: "P4" }, + }, + }, + ], + }; + model = new Model(data); + }); + + test("can edit chart data series type", async () => { + createChart( + model, + { + dataSets: [{ dataRange: "B1:B4" }, { dataRange: "C1:C4", type: "bar" }], + labelRange: "A2:A4", + type: "combo", + }, + chartId + ); + await mountChartSidePanel(); + await openChartDesignSidePanel(model, env, fixture, chartId); + await click(fixture, ".o-series-type-selection input[value=bar]"); + //@ts-ignore + expect(model.getters.getChartDefinition(chartId).dataSets[0]).toEqual({ + dataRange: "B1:B4", + type: "bar", + }); + + await click(fixture, ".o-series-type-selection input[value=line]"); + //@ts-ignore + expect(model.getters.getChartDefinition(chartId).dataSets[0]).toEqual({ + dataRange: "B1:B4", + type: "line", + }); + }); +}); diff --git a/tests/figures/chart/combo_chart_plugin.test.ts b/tests/figures/chart/combo_chart_plugin.test.ts index 6033e5df11..a8a10e932d 100644 --- a/tests/figures/chart/combo_chart_plugin.test.ts +++ b/tests/figures/chart/combo_chart_plugin.test.ts @@ -1,6 +1,11 @@ import { ChartCreationContext, Model } from "../../../src"; import { ComboChartRuntime } from "../../../src/types/chart/combo_chart"; -import { createChart, setCellContent, setCellFormat } from "../../test_helpers/commands_helpers"; +import { + createChart, + setCellContent, + setCellFormat, + updateChart, +} from "../../test_helpers/commands_helpers"; import { ComboChart } from "./../../../src/helpers/figures/charts/combo_chart"; describe("combo chart", () => { @@ -28,7 +33,7 @@ describe("combo chart", () => { type: "combo", background: "#123456", title: { text: "hello there" }, - dataSets: [{ dataRange: "Sheet1!B1:B4", yAxisId: "y1" }], + dataSets: [{ dataRange: "Sheet1!B1:B4", yAxisId: "y1", type: "bar" }], labelRange: "Sheet1!A1:A4", legendPosition: "bottom", dataSetsHaveTitle: true, @@ -78,4 +83,33 @@ describe("combo chart", () => { runtime.chartJsConfig.options?.scales?.y1?.ticks?.callback?.apply(null, [1, index, ticks]) ).toBe("1.00$"); }); + + test("Can edit the type of the series", () => { + const model = new Model(); + + setCellContent(model, "A1", "Alice"); + setCellContent(model, "A2", "Bob"); + setCellContent(model, "B1", "1"); + setCellContent(model, "B2", "2"); + setCellContent(model, "C1", "10"); + setCellContent(model, "C2", "20"); + + createChart( + model, + { + type: "combo", + labelRange: "A1:A2", + dataSets: [{ dataRange: "B1:B2" }, { dataRange: "C1:C2" }], + dataSetsHaveTitle: false, + }, + "1" + ); + let runtime = model.getters.getChartRuntime("1") as ComboChartRuntime; + expect(runtime.chartJsConfig.data?.datasets?.[1].type).toBe("line"); + updateChart(model, "1", { + dataSets: [{ dataRange: "B1:B2" }, { dataRange: "C1:C2", type: "bar" }], + }); + runtime = model.getters.getChartRuntime("1") as ComboChartRuntime; + expect(runtime.chartJsConfig.data?.datasets?.[1].type).toBe("bar"); + }); }); diff --git a/tests/figures/chart/waterfall/waterfall_panel_component.test.ts b/tests/figures/chart/waterfall/waterfall_panel_component.test.ts index dba8b85165..6000d15ab3 100644 --- a/tests/figures/chart/waterfall/waterfall_panel_component.test.ts +++ b/tests/figures/chart/waterfall/waterfall_panel_component.test.ts @@ -12,18 +12,13 @@ import { setInputValueAndTrigger, simulateClick, } from "../../../test_helpers"; -import { mountComponentWithPortalTarget, nextTick } from "../../../test_helpers/helpers"; +import { openChartConfigSidePanel } from "../../../test_helpers/chart_helpers"; +import { mountComponentWithPortalTarget } from "../../../test_helpers/helpers"; let model: Model; let fixture: HTMLElement; let env: SpreadsheetChildEnv; -async function openChartConfigSidePanel(chartId: UID) { - model.dispatch("SELECT_FIGURE", { id: chartId }); - env.openSidePanel("ChartPanel"); - await nextTick(); -} - function getWaterfallDefinition(chartId: UID): WaterfallChartDefinition { return model.getters.getChartDefinition(chartId) as WaterfallChartDefinition; } @@ -58,7 +53,7 @@ describe("Waterfall chart side panel", () => { dataSetsHaveTitle: true, aggregated: true, }); - await openChartConfigSidePanel(chartId); + await openChartConfigSidePanel(model, env, chartId); expect(getHTMLInputValue(".o-data-series input")).toEqual("A1:A3"); expect(getHTMLInputValue(".o-data-labels input")).toEqual("B1:B3"); @@ -73,7 +68,7 @@ describe("Waterfall chart side panel", () => { dataSetsHaveTitle: true, aggregated: true, }); - await openChartConfigSidePanel(chartId); + await openChartConfigSidePanel(model, env, chartId); await setInputValueAndTrigger(".o-data-labels input", "C1:C3"); await simulateClick(".o-data-labels .o-selection-ok"); @@ -105,7 +100,7 @@ describe("Waterfall chart side panel", () => { background: "#00FF00", firstValueAsSubtotal: true, }); - await openChartConfigSidePanel(chartId); + await openChartConfigSidePanel(model, env, chartId); await click(fixture, ".o-panel-design"); expect(getHTMLInputValue(".o-chart-title input")).toEqual("My Waterfall chart"); @@ -123,7 +118,7 @@ describe("Waterfall chart side panel", () => { test("Can change basic chart options", async () => { const chartId = createWaterfallChart(model, {}); - await openChartConfigSidePanel(chartId); + await openChartConfigSidePanel(model, env, chartId); await click(fixture, ".o-panel-design"); await setInputValueAndTrigger(".o-chart-title input", "My Waterfall chart"); @@ -143,7 +138,7 @@ describe("Waterfall chart side panel", () => { showConnectorLines: true, firstValueAsSubtotal: true, }); - await openChartConfigSidePanel(chartId); + await openChartConfigSidePanel(model, env, chartId); await click(fixture, ".o-panel-design"); await simulateClick('input[name="showSubTotals"]'); @@ -157,7 +152,7 @@ describe("Waterfall chart side panel", () => { test("Can change waterfall chart colors", async () => { const chartId = createWaterfallChart(model); - await openChartConfigSidePanel(chartId); + await openChartConfigSidePanel(model, env, chartId); await click(fixture, ".o-panel-design"); await changeChartColor(".o-chart-background-color", "#A64D79"); diff --git a/tests/test_helpers/chart_helpers.ts b/tests/test_helpers/chart_helpers.ts index e819987679..b971fb94c4 100644 --- a/tests/test_helpers/chart_helpers.ts +++ b/tests/test_helpers/chart_helpers.ts @@ -1,4 +1,6 @@ -import { Model, UID } from "../../src"; +import { Model, SpreadsheetChildEnv, UID } from "../../src"; +import { simulateClick } from "./dom_helper"; +import { nextTick } from "./helpers"; export function isChartAxisStacked(model: Model, chartId: UID, axis: "x" | "y"): boolean { return getChartConfiguration(model, chartId).options?.scales?.[axis]?.stacked; @@ -8,3 +10,21 @@ export function getChartConfiguration(model: Model, chartId: UID) { const runtime = model.getters.getChartRuntime(chartId) as any; return runtime.chartJsConfig; } + +export async function openChartConfigSidePanel(model: Model, env: SpreadsheetChildEnv, id: UID) { + model.dispatch("SELECT_FIGURE", { id }); + env.openSidePanel("ChartPanel"); + await nextTick(); +} + +export async function openChartDesignSidePanel( + model: Model, + env: SpreadsheetChildEnv, + fixture: HTMLElement, + id: UID +) { + if (!fixture.querySelector(".o-chart")) { + await openChartConfigSidePanel(model, env, id); + } + await simulateClick(".o-panel-element.inactive"); +}