diff --git a/src/components/figures/chart/chartJs/chartjs.ts b/src/components/figures/chart/chartJs/chartjs.ts index 24e2b9689..3b0c86f9e 100644 --- a/src/components/figures/chart/chartJs/chartjs.ts +++ b/src/components/figures/chart/chartJs/chartjs.ts @@ -1,5 +1,6 @@ import { Component, onMounted, useEffect, useRef } from "@odoo/owl"; import type { Chart, ChartConfiguration } from "chart.js"; +import { deepCopy, deepEquals } from "../../../../helpers"; import { Figure, SpreadsheetChildEnv } from "../../../../types"; import { ChartJSRuntime } from "../../../../types/chart/chart"; import { GaugeChartConfiguration, GaugeChartOptions } from "../../../../types/chart/gauge_chart"; @@ -13,6 +14,7 @@ export class ChartJsComponent extends Component { private canvas = useRef("graphContainer"); private chart?: Chart; + private currentRuntime!: ChartJSRuntime; get background(): string { return this.chartRuntime.background; @@ -33,12 +35,17 @@ export class ChartJsComponent extends Component { setup() { onMounted(() => { const runtime = this.chartRuntime; - this.createChart(runtime.chartJsConfig); + this.currentRuntime = runtime; + // Note: chartJS modify the runtime in place, so it's important to give it a copy + this.createChart(deepCopy(runtime.chartJsConfig)); + }); + useEffect(() => { + const runtime = this.chartRuntime; + if (!deepEquals(runtime, this.currentRuntime, "ignoreFunctions")) { + this.currentRuntime = runtime; + this.updateChartJs(deepCopy(runtime)); + } }); - useEffect( - () => this.updateChartJs(this.chartRuntime), - () => [this.chartRuntime] - ); } private createChart(chartData: ChartConfiguration | GaugeChartConfiguration) { diff --git a/src/helpers/misc.ts b/src/helpers/misc.ts index a478bd603..1c2dfab1b 100644 --- a/src/helpers/misc.ts +++ b/src/helpers/misc.ts @@ -350,7 +350,7 @@ export function getAddHeaderStartIndex(position: "before" | "after", base: numbe /** * Compares two objects. */ -export function deepEquals(o1: any, o2: any): boolean { +export function deepEquals(o1: any, o2: any, ignoreFunctions?: "ignoreFunctions"): boolean { if (o1 === o2) return true; if ((o1 && !o2) || (o2 && !o1)) return false; if (typeof o1 !== typeof o2) return false; @@ -364,10 +364,12 @@ export function deepEquals(o1: any, o2: any): boolean { } for (const key in o1) { - if (typeof o1[key] !== typeof o2[key]) return false; - if (typeof o1[key] === "object") { - if (!deepEquals(o1[key], o2[key])) return false; + const typeOfO1Key = typeof o1[key]; + if (typeOfO1Key !== typeof o2[key]) return false; + if (typeOfO1Key === "object") { + if (!deepEquals(o1[key], o2[key], ignoreFunctions)) return false; } else { + if (ignoreFunctions && typeOfO1Key === "function") return true; if (o1[key] !== o2[key]) return false; } } diff --git a/tests/figures/chart/charts_component.test.ts b/tests/figures/chart/charts_component.test.ts index 2034b8d65..1bad7fe68 100644 --- a/tests/figures/chart/charts_component.test.ts +++ b/tests/figures/chart/charts_component.test.ts @@ -1125,6 +1125,15 @@ describe("charts", () => { await keyDown({ key: "Z", ctrlKey: true }); expect(getCellContent(model, "D6")).toEqual(""); }); + + test("Chart is not re-rendered if its runtime do not change", async () => { + const updateChart = jest.spyOn((window as any).Chart.prototype, "update"); + createTestChart("basicChart"); + await nextTick(); + setCellContent(model, "C3", "value"); + await nextTick(); + expect(updateChart).not.toHaveBeenCalled(); + }); }); describe("charts with multiple sheets", () => { diff --git a/tests/helpers/misc_helpers.test.ts b/tests/helpers/misc_helpers.test.ts index 01e441124..30e8b5fec 100644 --- a/tests/helpers/misc_helpers.test.ts +++ b/tests/helpers/misc_helpers.test.ts @@ -233,6 +233,13 @@ test.each([ expect(deepEquals(o2, o1)).toEqual(expectedResult); }); +test("deepEquals with argument ignoring functions", () => { + const o1 = { a: 1, b: () => 2 }; + const o2 = { a: 1, b: () => 2 }; + expect(deepEquals(o1, o2)).toEqual(false); + expect(deepEquals(o1, o2, "ignoreFunctions")).toEqual(true); +}); + describe("isConsecutive", () => { test("consecutive", () => { expect(isConsecutive([2, 3, 1])).toBeTruthy(); diff --git a/tests/test_helpers/helpers.ts b/tests/test_helpers/helpers.ts index aecc00fe6..8e5177142 100644 --- a/tests/test_helpers/helpers.ts +++ b/tests/test_helpers/helpers.ts @@ -625,7 +625,7 @@ export const mockChart = () => { } toBase64Image = () => ""; destroy = () => {}; - update = () => {}; + update() {} options = mockChartData.options; config = mockChartData; }