From 495176bc7d466a69cb1fa3d6d3a6ec70be01117d Mon Sep 17 00:00:00 2001 From: "Adrien Minne (adrm)" Date: Thu, 9 Oct 2025 14:25:17 +0200 Subject: [PATCH 1/3] [FIX] charts: correctly humanize decimal numbers We have an `humanizeNumbers` option for the chart to make large numbers more readable. The option is enabled by default on every chart, but it was not correctly handling decimal numbers. The function was always rounding them to the nearest integer, which does not make sense for small numbers (eg. 0.15 should not be displayed as 0). Task: 5155591 --- src/helpers/format/format.ts | 22 +++++++++++++--------- tests/figures/chart/chart_plugin.test.ts | 8 ++++---- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/helpers/format/format.ts b/src/helpers/format/format.ts index e17c64f7fa..c89400f187 100644 --- a/src/helpers/format/format.ts +++ b/src/helpers/format/format.ts @@ -1,4 +1,4 @@ -import { toNumber } from "../../functions/helpers"; +import { toNumber, tryToNumber } from "../../functions/helpers"; import { _t } from "../../translation"; import { CellValue, @@ -682,14 +682,18 @@ function _roundFormat(internalFormat: T): T { } export function humanizeNumber({ value, format }: FunctionResultObject, locale: Locale): string { - const numberFormat = formatLargeNumber( - { - value, - format, - }, - undefined, - locale - ); + const numberValue = tryToNumber(value, locale); + if (numberValue === undefined) { + return ""; + } + let numberFormat: Format | undefined = format; + if (Math.abs(numberValue) < 1000) { + const hasDecimal = numberValue % 1 !== 0; + numberFormat = !format && hasDecimal ? "0.####" : format; + } else { + numberFormat = formatLargeNumber({ value, format }, undefined, locale); + } + return formatValue(value, { format: numberFormat, locale }); } diff --git a/tests/figures/chart/chart_plugin.test.ts b/tests/figures/chart/chart_plugin.test.ts index 6e0ac46682..97bd1dda76 100644 --- a/tests/figures/chart/chart_plugin.test.ts +++ b/tests/figures/chart/chart_plugin.test.ts @@ -3874,12 +3874,12 @@ describe("Can make numbers human-readable", () => { "1" ); let axis = getChartConfiguration(model, "1").options.scales.y; - const valuesBefore = [1e3, 1e6].map(axis.ticks.callback); - expect(valuesBefore).toEqual(["1,000", "1,000,000"]); + const valuesBefore = [1e3, 1e6, 0.1, 0.123456789].map(axis.ticks.callback); + expect(valuesBefore).toEqual(["1,000", "1,000,000", "0.1", "0.123456789"]); updateChart(model, "1", { humanize: true }); axis = getChartConfiguration(model, "1").options.scales.y; - const valuesAfter = [1e3, 1e6].map(axis.ticks.callback); - expect(valuesAfter).toEqual(["1,000", "1,000k"]); + const valuesAfter = [1e3, 1e6, 0.1, 0.123456789].map(axis.ticks.callback); + expect(valuesAfter).toEqual(["1,000", "1,000k", "0.1", "0.1235"]); } ); From 995c92756d5971dd74a5bf5bfc0bb9498eba2e90 Mon Sep 17 00:00:00 2001 From: "Adrien Minne (adrm)" Date: Thu, 9 Oct 2025 14:58:31 +0200 Subject: [PATCH 2/3] [FIX] charts: missing `humanizeNumbers` checkbox in some panels The `humanizeNumbers` checkbox was missing in the design panels of sunburst, treemap and bar charts. Task: 5155591 --- .../sunburst_chart/sunburst_chart_design_panel.ts | 2 ++ .../sunburst_chart/sunburst_chart_design_panel.xml | 3 +++ .../treemap_chart/treemap_chart_design_panel.ts | 2 ++ .../treemap_chart/treemap_chart_design_panel.xml | 3 +++ .../chart/zoomable_chart/design_panel.ts | 2 ++ .../chart/zoomable_chart/design_panel.xml | 3 +++ tests/figures/chart/charts_component.test.ts | 14 ++++++++++++++ 7 files changed, 29 insertions(+) diff --git a/src/components/side_panel/chart/sunburst_chart/sunburst_chart_design_panel.ts b/src/components/side_panel/chart/sunburst_chart/sunburst_chart_design_panel.ts index 0bd2df0f03..b2299c74d5 100644 --- a/src/components/side_panel/chart/sunburst_chart/sunburst_chart_design_panel.ts +++ b/src/components/side_panel/chart/sunburst_chart/sunburst_chart_design_panel.ts @@ -12,6 +12,7 @@ import { SidePanelCollapsible } from "../../components/collapsible/side_panel_co import { RoundColorPicker } from "../../components/round_color_picker/round_color_picker"; import { Section } from "../../components/section/section"; import { GeneralDesignEditor } from "../building_blocks/general_design/general_design_editor"; +import { ChartHumanizeNumbers } from "../building_blocks/humanize_numbers/humanize_numbers"; import { ChartLegend } from "../building_blocks/legend/legend"; import { PieHoleSize } from "../building_blocks/pie_hole_size/pie_hole_size"; import { ChartShowValues } from "../building_blocks/show_values/show_values"; @@ -36,6 +37,7 @@ export class SunburstChartDesignPanel extends Component +
+ +
diff --git a/src/components/side_panel/chart/treemap_chart/treemap_chart_design_panel.ts b/src/components/side_panel/chart/treemap_chart/treemap_chart_design_panel.ts index 502669accb..1247475759 100644 --- a/src/components/side_panel/chart/treemap_chart/treemap_chart_design_panel.ts +++ b/src/components/side_panel/chart/treemap_chart/treemap_chart_design_panel.ts @@ -13,6 +13,7 @@ import { SidePanelCollapsible } from "../../components/collapsible/side_panel_co import { RoundColorPicker } from "../../components/round_color_picker/round_color_picker"; import { Section } from "../../components/section/section"; import { GeneralDesignEditor } from "../building_blocks/general_design/general_design_editor"; +import { ChartHumanizeNumbers } from "../building_blocks/humanize_numbers/humanize_numbers"; import { ChartShowValues } from "../building_blocks/show_values/show_values"; import { TextStyler } from "../building_blocks/text_styler/text_styler"; import { TreeMapCategoryColors } from "./treemap_category_color/treemap_category_color"; @@ -51,6 +52,7 @@ export class TreeMapChartDesignPanel extends Component +
+ +
diff --git a/src/components/side_panel/chart/zoomable_chart/design_panel.ts b/src/components/side_panel/chart/zoomable_chart/design_panel.ts index 0e9e8389ff..ea618fcdc0 100644 --- a/src/components/side_panel/chart/zoomable_chart/design_panel.ts +++ b/src/components/side_panel/chart/zoomable_chart/design_panel.ts @@ -5,6 +5,7 @@ import { ZoomableChartDefinition, } from "../../../../types/index"; import { Checkbox } from "../../components/checkbox/checkbox"; +import { ChartHumanizeNumbers } from "../building_blocks/humanize_numbers/humanize_numbers"; import { ChartWithAxisDesignPanel } from "../chart_with_axis/design_panel"; interface Props { @@ -27,6 +28,7 @@ export class GenericZoomableChartDesignPanel< static components = { ...ChartWithAxisDesignPanel.components, Checkbox, + ChartHumanizeNumbers, }; onToggleZoom(zoomable: boolean) { diff --git a/src/components/side_panel/chart/zoomable_chart/design_panel.xml b/src/components/side_panel/chart/zoomable_chart/design_panel.xml index a5a11a3032..abc39c5602 100644 --- a/src/components/side_panel/chart/zoomable_chart/design_panel.xml +++ b/src/components/side_panel/chart/zoomable_chart/design_panel.xml @@ -15,6 +15,9 @@ className="'mb-2'" /> +
+ +
diff --git a/tests/figures/chart/charts_component.test.ts b/tests/figures/chart/charts_component.test.ts index 3a1ab18102..5506831adb 100644 --- a/tests/figures/chart/charts_component.test.ts +++ b/tests/figures/chart/charts_component.test.ts @@ -1851,6 +1851,20 @@ describe("charts", () => { } ); + test.each(["bar", "line", "waterfall", "treemap", "sunburst"])( + "humanizeNumbers checkbox updates the chart", + async (type: ChartType) => { + createTestChart(type); + await mountChartSidePanel(); + await openChartDesignSidePanel(model, env, fixture, chartId); + + expect(model.getters.getChartDefinition(chartId).humanize).toBe(true); + + await simulateClick("input[name='humanizeNumbers']"); + expect(model.getters.getChartDefinition(chartId).humanize).toBe(false); + } + ); + describe("aggregate", () => { test.each(["bar", "pie", "line", "scatter", "combo"] as const)( "aggregate checkbox is checked for string-count charts", From f9535f8d384add2035fd8d2c1d9f6efb924af333 Mon Sep 17 00:00:00 2001 From: "Adrien Minne (adrm)" Date: Thu, 9 Oct 2025 15:02:07 +0200 Subject: [PATCH 3/3] [FIX] charts: wrong padding for humanize number section Task: 5155591 --- .../building_blocks/humanize_numbers/humanize_numbers.ts | 9 +++++++++ .../humanize_numbers/humanize_numbers.xml | 3 ++- .../chart/combo_chart/combo_chart_design_panel.xml | 2 +- .../funnel_chart_panel/funnel_chart_design_panel.xml | 2 +- .../chart/gauge_chart_panel/gauge_chart_design_panel.xml | 2 +- .../chart/geo_chart_panel/geo_chart_design_panel.xml | 2 +- .../chart/line_chart/line_chart_design_panel.xml | 2 +- .../chart/pie_chart/pie_chart_design_panel.xml | 2 +- .../chart/radar_chart/radar_chart_design_panel.xml | 2 +- .../scorecard_chart_design_panel.xml | 2 +- .../waterfall_chart/waterfall_chart_design_panel.xml | 2 +- .../side_panel/chart/zoomable_chart/design_panel.ts | 2 -- 12 files changed, 20 insertions(+), 12 deletions(-) diff --git a/src/components/side_panel/chart/building_blocks/humanize_numbers/humanize_numbers.ts b/src/components/side_panel/chart/building_blocks/humanize_numbers/humanize_numbers.ts index 0767e90096..cfaa8a7c17 100644 --- a/src/components/side_panel/chart/building_blocks/humanize_numbers/humanize_numbers.ts +++ b/src/components/side_panel/chart/building_blocks/humanize_numbers/humanize_numbers.ts @@ -1,4 +1,6 @@ import { Component } from "@odoo/owl"; +import { formatLargeNumber, formatValue } from "../../../../../helpers"; +import { _t } from "../../../../../translation"; import { ChartWithDataSetDefinition, DispatchResult, @@ -25,4 +27,11 @@ export class ChartHumanizeNumbers extends Component updateChart: Function, canUpdateChart: Function, }; + + get title() { + const locale = this.env.model.getters.getLocale(); + const format = formatLargeNumber({ value: 1234567 }, undefined, locale); + const value = formatValue(1234567, { format, locale }); + return _t("E.g. 1234567 -> %(value)s", { value }); + } } diff --git a/src/components/side_panel/chart/building_blocks/humanize_numbers/humanize_numbers.xml b/src/components/side_panel/chart/building_blocks/humanize_numbers/humanize_numbers.xml index 911d41d55f..5998d59c00 100644 --- a/src/components/side_panel/chart/building_blocks/humanize_numbers/humanize_numbers.xml +++ b/src/components/side_panel/chart/building_blocks/humanize_numbers/humanize_numbers.xml @@ -2,7 +2,8 @@ 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 index 226dcd5b0f..07b2452ed3 100644 --- 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 @@ -16,7 +16,7 @@ className="'mb-2'" /> -
+
diff --git a/src/components/side_panel/chart/funnel_chart_panel/funnel_chart_design_panel.xml b/src/components/side_panel/chart/funnel_chart_panel/funnel_chart_design_panel.xml index 5e5d0e19f3..1415cc8a0b 100644 --- a/src/components/side_panel/chart/funnel_chart_panel/funnel_chart_design_panel.xml +++ b/src/components/side_panel/chart/funnel_chart_panel/funnel_chart_design_panel.xml @@ -5,7 +5,7 @@
-
+
diff --git a/src/components/side_panel/chart/gauge_chart_panel/gauge_chart_design_panel.xml b/src/components/side_panel/chart/gauge_chart_panel/gauge_chart_design_panel.xml index b86621f7a9..8a533f55c0 100644 --- a/src/components/side_panel/chart/gauge_chart_panel/gauge_chart_design_panel.xml +++ b/src/components/side_panel/chart/gauge_chart_panel/gauge_chart_design_panel.xml @@ -2,7 +2,7 @@ -
+
diff --git a/src/components/side_panel/chart/geo_chart_panel/geo_chart_design_panel.xml b/src/components/side_panel/chart/geo_chart_panel/geo_chart_design_panel.xml index af0c389f84..2b450fa0ca 100644 --- a/src/components/side_panel/chart/geo_chart_panel/geo_chart_design_panel.xml +++ b/src/components/side_panel/chart/geo_chart_panel/geo_chart_design_panel.xml @@ -14,7 +14,7 @@
-
+
diff --git a/src/components/side_panel/chart/line_chart/line_chart_design_panel.xml b/src/components/side_panel/chart/line_chart/line_chart_design_panel.xml index 928742fcf2..c94d9fa758 100644 --- a/src/components/side_panel/chart/line_chart/line_chart_design_panel.xml +++ b/src/components/side_panel/chart/line_chart/line_chart_design_panel.xml @@ -16,7 +16,7 @@ className="'mb-2'" />
-
+
diff --git a/src/components/side_panel/chart/pie_chart/pie_chart_design_panel.xml b/src/components/side_panel/chart/pie_chart/pie_chart_design_panel.xml index c8a3bb0947..7201dcb750 100644 --- a/src/components/side_panel/chart/pie_chart/pie_chart_design_panel.xml +++ b/src/components/side_panel/chart/pie_chart/pie_chart_design_panel.xml @@ -6,7 +6,7 @@
-
+
-
+
diff --git a/src/components/side_panel/chart/scorecard_chart_panel/scorecard_chart_design_panel.xml b/src/components/side_panel/chart/scorecard_chart_panel/scorecard_chart_design_panel.xml index 6dce6520c4..bfb4f61268 100644 --- a/src/components/side_panel/chart/scorecard_chart_panel/scorecard_chart_design_panel.xml +++ b/src/components/side_panel/chart/scorecard_chart_panel/scorecard_chart_design_panel.xml @@ -4,7 +4,7 @@ Color Down -
+
diff --git a/src/components/side_panel/chart/waterfall_chart/waterfall_chart_design_panel.xml b/src/components/side_panel/chart/waterfall_chart/waterfall_chart_design_panel.xml index 25acac3491..9e3d85dff2 100644 --- a/src/components/side_panel/chart/waterfall_chart/waterfall_chart_design_panel.xml +++ b/src/components/side_panel/chart/waterfall_chart/waterfall_chart_design_panel.xml @@ -23,7 +23,7 @@ className="'mb-2'" />
-
+
diff --git a/src/components/side_panel/chart/zoomable_chart/design_panel.ts b/src/components/side_panel/chart/zoomable_chart/design_panel.ts index ea618fcdc0..0e9e8389ff 100644 --- a/src/components/side_panel/chart/zoomable_chart/design_panel.ts +++ b/src/components/side_panel/chart/zoomable_chart/design_panel.ts @@ -5,7 +5,6 @@ import { ZoomableChartDefinition, } from "../../../../types/index"; import { Checkbox } from "../../components/checkbox/checkbox"; -import { ChartHumanizeNumbers } from "../building_blocks/humanize_numbers/humanize_numbers"; import { ChartWithAxisDesignPanel } from "../chart_with_axis/design_panel"; interface Props { @@ -28,7 +27,6 @@ export class GenericZoomableChartDesignPanel< static components = { ...ChartWithAxisDesignPanel.components, Checkbox, - ChartHumanizeNumbers, }; onToggleZoom(zoomable: boolean) {