From 2b657139e1cc22b71c543f5aaeacd9b58850de11 Mon Sep 17 00:00:00 2001 From: Krishn <1khandelwalnhk@gmail.com> Date: Thu, 6 Aug 2026 12:18:11 +0530 Subject: [PATCH] fix(plugin-chart-echarts): exclude extra metrics from stacked totals extractDataTotalValues summed every numeric column in each row, so a timeseries_limit_metric that is not rendered as a series still inflated the Only Total value. With metrics A=32, B=0 and a sort metric of 2, the total showed 34 instead of 32. Threads extraMetricLabels into extractDataTotalValues and excludes those columns from both the total and the derived threshold. Fixes #42701 Co-Authored-By: Claude Opus 5 --- .../src/Timeseries/transformProps.ts | 7 +-- .../plugin-chart-echarts/src/utils/series.ts | 15 +++++- .../test/utils/series.test.ts | 47 +++++++++++++++++++ 3 files changed, 65 insertions(+), 4 deletions(-) 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 6b6b1c4df4ba..0b853611b333 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts @@ -304,6 +304,9 @@ export default function transformProps( xAxisLabel = verboseMap[xAxisLabel]; } const isHorizontal = orientation === OrientationType.Horizontal; + const extraMetricLabels = extractExtraMetrics(chartProps.rawFormData).map( + getMetricLabel, + ); const { totalStackedValues, thresholdValues } = extractDataTotalValues( rebasedData, { @@ -311,11 +314,9 @@ export default function transformProps( percentageThreshold, xAxisCol: xAxisLabel, legendState, + extraMetricLabels, }, ); - const extraMetricLabels = extractExtraMetrics(chartProps.rawFormData).map( - getMetricLabel, - ); const isMultiSeries = groupBy.length || metrics?.length > 1; const xAxisDataType = dataTypes?.[xAxisLabel] ?? dataTypes?.[xAxisOrig]; 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 016a18bbe02e..d4c9c0519509 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts @@ -420,6 +420,7 @@ export function extractDataTotalValues( percentageThreshold: number; xAxisCol: string; legendState?: LegendState; + extraMetricLabels?: string[]; }, ): { totalStackedValues: number[]; @@ -427,13 +428,25 @@ export function extractDataTotalValues( } { const totalStackedValues: number[] = []; const thresholdValues: number[] = []; - const { stack, percentageThreshold, xAxisCol, legendState } = opts; + const { + stack, + percentageThreshold, + xAxisCol, + legendState, + extraMetricLabels = [], + } = opts; + // Extra metrics, such as a sort metric, are present in the data but are not + // rendered as series, so they must not contribute to the stacked total. + const extraMetricLabelsSet = new Set(extraMetricLabels); if (stack) { data.forEach(datum => { const values = Object.keys(datum).reduce((prev, curr) => { if (curr === xAxisCol) { return prev; } + if (extraMetricLabelsSet.has(curr)) { + return prev; + } if (legendState && !legendState[curr]) { return prev; } 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 ce67af42e178..01820c328b3b 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 @@ -28,6 +28,7 @@ import { GenericDataType } from '@apache-superset/core/common'; import { calculateLowerLogTick, dedupSeries, + extractDataTotalValues, extractGroupbyLabel, extractSeries, extractShowValueIndexes, @@ -1681,3 +1682,49 @@ test('getAreaScaledSymbolSize handles degenerate extents and bad values', () => midAreaSize, ); }); + +describe('extractDataTotalValues', () => { + const data: DataRecord[] = [ + { __timestamp: 1704067200000, A: 32, B: 0, Sort: 2 }, + ]; + + test('excludes extra metrics from the stacked total', () => { + const { totalStackedValues } = extractDataTotalValues(data, { + stack: true, + percentageThreshold: 0, + xAxisCol: '__timestamp', + extraMetricLabels: ['Sort'], + }); + expect(totalStackedValues).toEqual([32]); + }); + + test('includes every metric column when there are no extra metrics', () => { + const { totalStackedValues } = extractDataTotalValues(data, { + stack: true, + percentageThreshold: 0, + xAxisCol: '__timestamp', + }); + expect(totalStackedValues).toEqual([34]); + }); + + test('still honors legendState when excluding extra metrics', () => { + const { totalStackedValues } = extractDataTotalValues(data, { + stack: true, + percentageThreshold: 0, + xAxisCol: '__timestamp', + legendState: { A: false, B: true }, + extraMetricLabels: ['Sort'], + }); + expect(totalStackedValues).toEqual([0]); + }); + + test('derives thresholdValues from the total excluding extra metrics', () => { + const { thresholdValues } = extractDataTotalValues(data, { + stack: true, + percentageThreshold: 50, + xAxisCol: '__timestamp', + extraMetricLabels: ['Sort'], + }); + expect(thresholdValues).toEqual([16]); + }); +});