diff --git a/addons/spreadsheet/static/src/chart/odoo_chart/odoo_line_chart.js b/addons/spreadsheet/static/src/chart/odoo_chart/odoo_line_chart.js index bbf1fba8ba4..cf0af30def1 100644 --- a/addons/spreadsheet/static/src/chart/odoo_chart/odoo_line_chart.js +++ b/addons/spreadsheet/static/src/chart/odoo_chart/odoo_line_chart.js @@ -3,21 +3,31 @@ import spreadsheet from "@spreadsheet/o_spreadsheet/o_spreadsheet_extended"; import { _t } from "@web/core/l10n/translation"; import { OdooChart } from "./odoo_chart"; +import { LINE_FILL_TRANSPARENCY } from "@web/views/graph/graph_renderer"; const { chartRegistry } = spreadsheet.registries; -const { getDefaultChartJsRuntime, chartFontColor, ChartColors } = spreadsheet.helpers; +const { + getDefaultChartJsRuntime, + chartFontColor, + ChartColors, + getFillingMode, + colorToRGBA, + rgbaToHex, +} = spreadsheet.helpers; export class OdooLineChart extends OdooChart { constructor(definition, sheetId, getters) { super(definition, sheetId, getters); this.verticalAxisPosition = definition.verticalAxisPosition; + this.stacked = definition.stacked; } getDefinition() { return { ...super.getDefinition(), verticalAxisPosition: this.verticalAxisPosition, + stacked: this.stacked, }; } } @@ -38,14 +48,22 @@ function createOdooChartRuntime(chart, getters) { const { datasets, labels } = chart.dataSource.getData(); const chartJsConfig = getLineConfiguration(chart, labels); const colors = new ChartColors(); - for (const { label, data } of datasets) { + for (const [index, { label, data }] of datasets.entries()) { const color = colors.next(); + const backgroundRGBA = colorToRGBA(color); + if (chart.stacked) { + // use the transparency of Odoo to keep consistency + backgroundRGBA.a = LINE_FILL_TRANSPARENCY; + } + const backgroundColor = rgbaToHex(backgroundRGBA); const dataset = { label, data, lineTension: 0, borderColor: color, - backgroundColor: color, + backgroundColor, + pointBackgroundColor: color, + fill: chart.stacked ? getFillingMode(index) : false, }; chartJsConfig.data.datasets.push(dataset); } @@ -59,7 +77,17 @@ function getLineConfiguration(chart, labels) { const legend = { ...config.options.legend, display: chart.legendPosition !== "none", - labels: { fontColor }, + labels: { + fontColor, + generateLabels(chart) { + const { data } = chart; + const labels = window.Chart.defaults.global.legend.labels.generateLabels(chart); + for (const [index, label] of labels.entries()) { + label.fillStyle = data.datasets[index].borderColor; + } + return labels; + }, + }, }; legend.position = chart.legendPosition; config.options.legend = legend; @@ -90,5 +118,8 @@ function getLineConfiguration(chart, labels) { }, ], }; + if (chart.stacked) { + config.options.scales.yAxes[0].stacked = true; + } return config; } diff --git a/addons/spreadsheet/static/tests/charts/model/odoo_chart_plugin_test.js b/addons/spreadsheet/static/tests/charts/model/odoo_chart_plugin_test.js index 2f08b93c9c8..35c9ae5f0d3 100644 --- a/addons/spreadsheet/static/tests/charts/model/odoo_chart_plugin_test.js +++ b/addons/spreadsheet/static/tests/charts/model/odoo_chart_plugin_test.js @@ -133,11 +133,13 @@ QUnit.module("spreadsheet > odoo chart plugin", {}, () => { assert.deepEqual(model.getters.getChartRuntime(chartId).chartJsConfig.data, { datasets: [ { - backgroundColor: "rgb(31,119,180)", + backgroundColor: "#1f77b466", borderColor: "rgb(31,119,180)", data: [1, 3], label: "Count", lineTension: 0, + fill: "origin", + pointBackgroundColor: "rgb(31,119,180)", }, ], labels: ["false", "true"], @@ -362,4 +364,39 @@ QUnit.module("spreadsheet > odoo chart plugin", {}, () => { "The datasource is also duplicated" ); }); + + QUnit.test("Line chart with stacked attribute is supported", async (assert) => { + const { model } = await createSpreadsheetWithGraph({ type: "odoo_line" }); + const sheetId = model.getters.getActiveSheetId(); + const chartId = model.getters.getChartIds(sheetId)[0]; + const definition = model.getters.getChartDefinition(chartId); + model.dispatch("UPDATE_CHART", { + definition: { + ...definition, + stacked: true, + }, + id: chartId, + sheetId, + }); + assert.notOk( + model.getters.getChartRuntime(chartId).chartJsConfig.options.scales.xAxes[0].stacked + ); + assert.ok( + model.getters.getChartRuntime(chartId).chartJsConfig.options.scales.yAxes[0].stacked + ); + model.dispatch("UPDATE_CHART", { + definition: { + ...definition, + stacked: false, + }, + id: chartId, + sheetId, + }); + assert.notOk( + model.getters.getChartRuntime(chartId).chartJsConfig.options.scales.xAxes[0].stacked + ); + assert.notOk( + model.getters.getChartRuntime(chartId).chartJsConfig.options.scales.yAxes[0].stacked + ); + }); }); diff --git a/addons/web/static/src/views/graph/graph_renderer.js b/addons/web/static/src/views/graph/graph_renderer.js index 6b26d84b887..c6976611ef0 100644 --- a/addons/web/static/src/views/graph/graph_renderer.js +++ b/addons/web/static/src/views/graph/graph_renderer.js @@ -12,6 +12,8 @@ const { Component, onWillUnmount, useEffect, useRef, onWillStart } = owl; const NO_DATA = _lt("No data"); +export const LINE_FILL_TRANSPARENCY = 0.4; + /** * @param {Object} chartArea * @returns {string} @@ -23,7 +25,7 @@ function getMaxWidth(chartArea) { /** * Used to avoid too long legend items. - * @param {string|Strin} label + * @param {string} label * @returns {string} shortened version of the input label */ function shortenLabel(label) { @@ -325,7 +327,7 @@ export class GraphRenderer extends Component { if (groupBy.length <= 1 && domains.length > 1) { if (dataset.originIndex === 0) { dataset.fill = "origin"; - dataset.backgroundColor = hexToRGBA(getColor(0), 0.4); + dataset.backgroundColor = hexToRGBA(getColor(0), LINE_FILL_TRANSPARENCY); dataset.borderColor = getColor(0); } else if (dataset.originIndex === 1) { dataset.borderColor = getColor(1); @@ -346,7 +348,7 @@ export class GraphRenderer extends Component { dataset.pointBackgroundColor = dataset.borderColor; dataset.pointBorderColor = "rgba(0,0,0,0.2)"; if (stacked) { - dataset.backgroundColor = hexToRGBA(dataset.borderColor, 0.4); + dataset.backgroundColor = hexToRGBA(dataset.borderColor, LINE_FILL_TRANSPARENCY); } if (cumulated) { let accumulator = 0; @@ -359,7 +361,7 @@ export class GraphRenderer extends Component { if (data.datasets.length === 1 && data.datasets[0].originIndex === 0) { const dataset = data.datasets[0]; dataset.fill = "origin"; - dataset.backgroundColor = hexToRGBA(getColor(0), 0.4); + dataset.backgroundColor = hexToRGBA(getColor(0), LINE_FILL_TRANSPARENCY); } // center the points in the chart (without that code they are put // on the left and the graph seems empty)