You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by vi...@apache.org on 2026/10/16 22:12:07 UTC

(superset) 17/17: fix(dashboard): apply temporal x-axis cross-filters to target charts (#45310)

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

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

commit 76a20ae554ecd21799efe50b499e9da635437221
Author: Madhushree <ma...@gmail.com>
AuthorDate: Fri Oct 16 12:30:36 2026 -0700

    fix(dashboard): apply temporal x-axis cross-filters to target charts (#45310)
    
    Co-authored-by: madhushree agarwal <ma...@apple.com>
    (cherry picked from commit 853f207c668717c13514b2033fffcf5dd3805b98)
---
 .../src/Timeseries/EchartsTimeseries.tsx           |  4 ++
 .../test/Timeseries/EchartsTimeseries.test.tsx     |  6 +++
 .../util/charts/getFormDataWithExtraFilters.ts     |  7 +++-
 .../util/getFormDataWithExtraFilters.test.ts       | 49 ++++++++++++++++++++++
 4 files changed, 65 insertions(+), 1 deletion(-)

diff --git a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx
index d8db68da7ba..09d8ed2db7e 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/EchartsTimeseries.tsx
@@ -471,6 +471,10 @@ export default function EchartsTimeseries({
                       val: timeRange,
                     },
                   ],
+            // The backend rewrites every TEMPORAL_RANGE filter's value with the
+            // query's time_range, so without this override target charts would
+            // keep their own saved range and ignore the clicked bucket.
+            ...(values.length ? { time_range: timeRange } : {}),
           },
           filterState: {
             label: values.length ? values : undefined,
diff --git a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/EchartsTimeseries.test.tsx b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/EchartsTimeseries.test.tsx
index 72104b286e5..bbfa155be36 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/EchartsTimeseries.test.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/test/Timeseries/EchartsTimeseries.test.tsx
@@ -523,6 +523,7 @@ test('emits TEMPORAL_RANGE cross-filter from time axis label click on day bucket
           val: '2021-01-01T00:00:00 : 2021-01-02T00:00:00',
         },
       ],
+      time_range: '2021-01-01T00:00:00 : 2021-01-02T00:00:00',
     },
     filterState: {
       label: ['2021-01-01T00:00:00 : 2021-01-02T00:00:00'],
@@ -568,6 +569,11 @@ test('emits upper-exclusive TEMPORAL_RANGE from time point click on month bucket
       val: '2021-01-01T00:00:00 : 2021-02-01T00:00:00',
     },
   ]);
+  // Target charts' own TEMPORAL_RANGE filters are rewritten from time_range on
+  // the backend, so the clicked bucket must be sent as time_range as well.
+  expect(setDataMask.mock.calls[0][0].extraFormData.time_range).toBe(
+    '2021-01-01T00:00:00 : 2021-02-01T00:00:00',
+  );
 });
 
 test('uses resolved time grain for temporal point-click cross-filter', () => {
diff --git a/superset-frontend/src/dashboard/util/charts/getFormDataWithExtraFilters.ts b/superset-frontend/src/dashboard/util/charts/getFormDataWithExtraFilters.ts
index 71c399aa0cf..676ff087179 100644
--- a/superset-frontend/src/dashboard/util/charts/getFormDataWithExtraFilters.ts
+++ b/superset-frontend/src/dashboard/util/charts/getFormDataWithExtraFilters.ts
@@ -473,9 +473,14 @@ export default function getFormDataWithExtraFilters({
     });
 
   let extraData: JsonObject = {};
+  // Cross-filter ids are chart ids, so plain key order would put them before
+  // native filter ids and let a native filter's override keys (e.g. a Time
+  // Range filter's time_range) replace the cross-filter's. Merge native
+  // filters first so a cross-filter selection takes precedence.
   const filterIdsAppliedOnChart = Object.entries(activeFilters)
     .filter(([, activeFilter]) => activeFilter?.scope?.includes(chart.id))
-    .map(([filterId]) => filterId);
+    .map(([filterId]) => filterId)
+    .sort((a, b) => Number(!nativeFilters?.[a]) - Number(!nativeFilters?.[b]));
 
   if (filterIdsAppliedOnChart.length) {
     const aggregatedFormData = getExtraFormData(
diff --git a/superset-frontend/src/dashboard/util/getFormDataWithExtraFilters.test.ts b/superset-frontend/src/dashboard/util/getFormDataWithExtraFilters.test.ts
index aca21a5f166..9b2b727880c 100644
--- a/superset-frontend/src/dashboard/util/getFormDataWithExtraFilters.test.ts
+++ b/superset-frontend/src/dashboard/util/getFormDataWithExtraFilters.test.ts
@@ -276,6 +276,55 @@ test('should merge both filters and customization extraFormData', () => {
   expect(result.extra_form_data).toBeDefined();
 });
 
+test('cross-filter time_range takes precedence over a native time range filter', () => {
+  const nativeTimeFilterId = 'NATIVE_FILTER-time';
+  const crossFilterId = '42';
+  const nativeRange = 'Last week';
+  const clickedRange = '2021-01-01T00:00:00 : 2021-01-02T00:00:00';
+  const result = getFormDataWithExtraFilters({
+    ...mockArgs,
+    nativeFilters: {
+      [nativeTimeFilterId]: {
+        id: nativeTimeFilterId,
+        chartsInScope: [chartId],
+      },
+    } as unknown as GetFormDataWithExtraFiltersArguments['nativeFilters'],
+    activeFilters: {
+      [nativeTimeFilterId]: {
+        targets: [],
+        scope: [chartId],
+        values: { time_range: nativeRange },
+      },
+      [crossFilterId]: {
+        targets: [],
+        scope: [chartId],
+        values: { time_range: clickedRange },
+      },
+    },
+    dataMask: {
+      [nativeTimeFilterId]: {
+        id: nativeTimeFilterId,
+        extraFormData: { time_range: nativeRange },
+        filterState: { value: nativeRange },
+        ownState: {},
+      },
+      [crossFilterId]: {
+        id: crossFilterId,
+        extraFormData: {
+          filters: [{ col: 'ds', op: 'TEMPORAL_RANGE', val: clickedRange }],
+          time_range: clickedRange,
+        },
+        filterState: { value: [clickedRange] },
+        ownState: {},
+      },
+    },
+  });
+
+  expect(result.extra_form_data).toEqual(
+    expect.objectContaining({ time_range: clickedRange }),
+  );
+});
+
 const makeGroupByArgs = (
   selectedValue: string | string[],
   baseGroupby: string[] = [],