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 d9954b537a13..8cd97a0b3648 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts @@ -318,6 +318,9 @@ export default function transformProps( rebaseToPercentChange(forecastRebasedData, xAxisLabel || DTTM_ALIAS) : forecastRebasedData; const isHorizontal = orientation === OrientationType.Horizontal; + const extraMetricLabels = extractExtraMetrics(chartProps.rawFormData).map( + getMetricLabel, + ); const { totalStackedValues, thresholdValues } = extractDataTotalValues( rebasedData, { @@ -325,11 +328,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 88631b48686a..af86c026bde4 100644 --- a/superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts +++ b/superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts @@ -387,6 +387,7 @@ export function extractDataTotalValues( percentageThreshold: number; xAxisCol: string; legendState?: LegendState; + extraMetricLabels?: string[]; }, ): { totalStackedValues: number[]; @@ -394,13 +395,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 13f36dddb531..73e83c175fac 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, @@ -1749,3 +1750,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]); + }); +});