From 171414f16535e18406c98ce2da2a77bac68767bb Mon Sep 17 00:00:00 2001 From: Jean Massucatto Date: Wed, 29 Apr 2026 04:41:19 -0300 Subject: [PATCH] fix(chart): use categorical axis for bar charts with numeric x-axis (#39141) Co-authored-by: Enzo Martellucci <52219496+EnxDev@users.noreply.github.com> --- .../src/MixedTimeseries/transformProps.ts | 10 +++++++++- .../src/Timeseries/transformProps.ts | 7 ++++++- .../plugin-chart-echarts/src/utils/series.ts | 7 ++++++- .../test/utils/series.test.ts | 19 +++++++++++++++++++ 4 files changed, 40 insertions(+), 3 deletions(-) diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts index 71dc91183a9..33df597f923 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/MixedTimeseries/transformProps.ts @@ -258,7 +258,15 @@ export default function transformProps( const dataTypes = getColtypesMapping(queriesData[0]); const xAxisDataType = dataTypes?.[xAxisLabel] ?? dataTypes?.[xAxisOrig]; - const xAxisType = getAxisType(stack, xAxisForceCategorical, xAxisDataType); + const xAxisType = getAxisType( + stack, + xAxisForceCategorical, + xAxisDataType, + seriesType === EchartsTimeseriesSeriesType.Bar || + seriesTypeB === EchartsTimeseriesSeriesType.Bar + ? EchartsTimeseriesSeriesType.Bar + : seriesType, + ); const [rawSeriesA, sortedTotalValuesA] = extractSeries(rebasedDataA, { fillNeighborValue: stack ? 0 : undefined, diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts index 114c374d659..8bf0909489a 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts @@ -314,7 +314,12 @@ export default function transformProps( const isMultiSeries = groupBy.length || metrics?.length > 1; const xAxisDataType = dataTypes?.[xAxisLabel] ?? dataTypes?.[xAxisOrig]; - const xAxisType = getAxisType(stack, xAxisForceCategorical, xAxisDataType); + const xAxisType = getAxisType( + stack, + xAxisForceCategorical, + xAxisDataType, + seriesType, + ); const [rawSeries, sortedTotalValues, minPositiveValue] = extractSeries( rebasedData, diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts b/superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts index 8e243167474..a8c80303ced 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts @@ -934,6 +934,7 @@ export function getAxisType( stack: StackType, forceCategorical?: boolean, dataType?: GenericDataType, + seriesType?: EchartsTimeseriesSeriesType, ): AxisType { if (forceCategorical) { return AxisType.Category; @@ -941,7 +942,11 @@ export function getAxisType( if (dataType === GenericDataType.Temporal) { return AxisType.Time; } - if (dataType === GenericDataType.Numeric && !stack) { + if ( + dataType === GenericDataType.Numeric && + !stack && + seriesType !== EchartsTimeseriesSeriesType.Bar + ) { return AxisType.Value; } return AxisType.Category; diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/utils/series.test.ts b/superset-frontend/plugins/plugin-chart-echarts/test/utils/series.test.ts index f50b1933d5d..5bf971229e7 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/test/utils/series.test.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/test/utils/series.test.ts @@ -1400,6 +1400,25 @@ test('getAxisType with forced categorical', () => { ); }); +test('getAxisType treats numeric as category for bar charts', () => { + expect( + getAxisType( + false, + false, + GenericDataType.Numeric, + EchartsTimeseriesSeriesType.Bar, + ), + ).toEqual(AxisType.Category); + expect( + getAxisType( + false, + false, + GenericDataType.Numeric, + EchartsTimeseriesSeriesType.Line, + ), + ).toEqual(AxisType.Value); +}); + test('getMinAndMaxFromBounds returns empty object when not truncating', () => { expect( getMinAndMaxFromBounds(