You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ar...@apache.org on 2024/03/22 07:24:44 UTC

(superset) branch master updated: feat(bar_chart): Stacked Bar chart with Time comparison in separated stacks (#27589)

This is an automated email from the ASF dual-hosted git repository.

arivero pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 8a715cc1b5 feat(bar_chart): Stacked Bar chart with Time comparison in separated stacks (#27589)
8a715cc1b5 is described below

commit 8a715cc1b5fd687acda48443f18c93358e4a92e0
Author: Antonio Rivero <38...@users.noreply.github.com>
AuthorDate: Fri Mar 22 08:24:38 2024 +0100

    feat(bar_chart): Stacked Bar chart with Time comparison in separated stacks (#27589)
---
 .../src/Timeseries/transformProps.ts               |  1 +
 .../src/Timeseries/transformers.ts                 |  8 +++--
 .../plugin-chart-echarts/src/utils/series.ts       | 38 +++++++++++++++++++++-
 .../plugin-chart-echarts/test/utils/series.test.ts | 31 ++++++++++++++++++
 4 files changed, 74 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 8b4998ded0..0ee54f4577 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts
@@ -308,6 +308,7 @@ export default function transformProps(
         sliceId,
         isHorizontal,
         lineStyle,
+        timeCompare: array,
       },
     );
     if (transformedSeries) {
diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformers.ts b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformers.ts
index be89fdfc74..b5ff791fbe 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformers.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformers.ts
@@ -62,7 +62,7 @@ import {
   formatAnnotationLabel,
   parseAnnotationOpacity,
 } from '../utils/annotation';
-import { getChartPadding } from '../utils/series';
+import { getChartPadding, getTimeCompareStackId } from '../utils/series';
 import {
   OpacityEnum,
   StackControlsValue,
@@ -164,6 +164,7 @@ export function transformSeries(
     isHorizontal?: boolean;
     lineStyle?: LineStyleOption;
     queryIndex?: number;
+    timeCompare?: string[];
   },
 ): SeriesOption | undefined {
   const { name } = series;
@@ -188,6 +189,7 @@ export function transformSeries(
     sliceId,
     isHorizontal = false,
     queryIndex = 0,
+    timeCompare = [],
   } = opts;
   const contexts = seriesContexts[name || ''] || [];
   const hasForecast =
@@ -217,9 +219,9 @@ export function transformSeries(
   } else if (stack && isObservation) {
     // the suffix of the observation series is '' (falsy), which disables
     // stacking. Therefore we need to set something that is truthy.
-    stackId = 'obs';
+    stackId = getTimeCompareStackId('obs', timeCompare, name);
   } else if (stack && isTrend) {
-    stackId = forecastSeries.type;
+    stackId = getTimeCompareStackId(forecastSeries.type, timeCompare, name);
   }
   let plotType;
   if (
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 cebfe374d9..cb97dff93a 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/utils/series.ts
@@ -35,7 +35,7 @@ import {
 } from '@superset-ui/core';
 import { SortSeriesType } from '@superset-ui/chart-controls';
 import { format, LegendComponentOption, SeriesOption } from 'echarts';
-import { maxBy, meanBy, minBy, orderBy, sumBy } from 'lodash';
+import { isEmpty, maxBy, meanBy, minBy, orderBy, sumBy } from 'lodash';
 import {
   NULL_STRING,
   StackControlsValue,
@@ -604,3 +604,39 @@ export function getMinAndMaxFromBounds(
   }
   return {};
 }
+
+/**
+ * Returns the stackId used in stacked series.
+ * It will return the defaultId if the chart is not using time comparison.
+ * If time comparison is used, it will return the time comparison value as the stackId
+ * if the name includes the time comparison value.
+ *
+ * @param {string} defaultId The default stackId.
+ * @param {string[]} timeCompare The time comparison values.
+ * @param {string | number} name The name of the serie.
+ *
+ * @returns {string} The stackId.
+ */
+export function getTimeCompareStackId(
+  defaultId: string,
+  timeCompare: string[],
+  name?: string | number,
+): string {
+  if (isEmpty(timeCompare)) {
+    return defaultId;
+  }
+  // Each timeCompare is its own stack so it doesn't stack on top of original ones
+  return (
+    timeCompare.find(value => {
+      if (typeof name === 'string') {
+        // offset is represented as <offset>, group by list
+        return (
+          name.includes(`${value},`) ||
+          // offset is represented as <metric>__<offset>
+          name.includes(`__${value}`)
+        );
+      }
+      return name?.toString().includes(value);
+    }) || defaultId
+  );
+}
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 6e4848643e..efc0ac745a 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
@@ -40,6 +40,7 @@ import {
   sanitizeHtml,
   sortAndFilterSeries,
   sortRows,
+  getTimeCompareStackId,
 } from '../../src/utils/series';
 import {
   EchartsTimeseriesSeriesType,
@@ -1041,3 +1042,33 @@ test('getMinAndMaxFromBounds returns automatic lower bound when truncating', ()
     scale: true,
   });
 });
+
+describe('getTimeCompareStackId', () => {
+  it('returns the defaultId when timeCompare is empty', () => {
+    const result = getTimeCompareStackId('default', []);
+    expect(result).toEqual('default');
+  });
+
+  it('returns the defaultId when no value in timeCompare is included in name', () => {
+    const result = getTimeCompareStackId(
+      'default',
+      ['compare1', 'compare2'],
+      'test__name',
+    );
+    expect(result).toEqual('default');
+  });
+
+  it('returns the first value in timeCompare that is included in name', () => {
+    const result = getTimeCompareStackId(
+      'default',
+      ['compare1', 'compare2'],
+      'test__compare1',
+    );
+    expect(result).toEqual('compare1');
+  });
+
+  it('handles name being a number', () => {
+    const result = getTimeCompareStackId('default', ['123', '456'], 123);
+    expect(result).toEqual('123');
+  });
+});