[FIX] spreadsheet: Introducing stacked line charts
Allow users to use stacked line charts. The chart.js configuration is mimicking the one inside Odoo community. task 2888350 closes odoo/odoo#102312 X-original-commit: 1ccb2cd685c60eecd767eb15967d4b4c25262ded Related: odoo/enterprise#32345 Signed-off-by: Lucas Lefèvre (lul) <lul@odoo.com> Signed-off-by: Rémi Rahir (rar) <rar@odoo.com>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user