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

(superset) 01/01: test(dashboard): prove filters with defaults apply without touching every filter (#34617)

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

rusackas pushed a commit to branch test/issue-34617-filterbar-apply-defaults
in repository https://gitbox.apache.org/repos/asf/superset.git

commit daa51139270f03f1ccb89b39841eec9f24b253de
Author: Claude Code <no...@anthropic.com>
AuthorDate: Mon Jul 6 17:15:46 2026 -0700

    test(dashboard): prove filters with defaults apply without touching every filter (#34617)
    
    Co-Authored-By: Claude Fable 5 <no...@anthropic.com>
---
 .../nativeFilters/FilterBar/FilterBar.test.tsx     | 110 +++++++++++++++++++++
 1 file changed, 110 insertions(+)

diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBar.test.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBar.test.tsx
index e7b8506c090..b97d4893d8c 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBar.test.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterBar.test.tsx
@@ -23,6 +23,7 @@ import {
   render,
   screen,
   userEvent,
+  waitFor,
 } from 'spec/helpers/testing-library';
 import { stateWithoutNativeFilters } from 'spec/fixtures/mockStore';
 import { testWithId } from 'src/utils/testUtils';
@@ -1037,6 +1038,115 @@ test('FilterBar with orientation=Horizontal and no filters shows empty state alo
   expect(screen.getByTestId('filterbar-action-buttons')).toBeInTheDocument();
 });
 
+test('required filter with a default value auto-applies on load without touching other filters', async () => {
+  // Regression proof for #34617: a dashboard with a required filter that has
+  // a default value used to leave the default un-applied (and Apply blocked
+  // by a stale validateStatus) until the user touched every filter. Since the
+  // auto-apply logic introduced by #36927, FilterBar dispatches the derived
+  // dataMask itself as soon as the filter control finishes loading — the user
+  // never has to touch the other filters, or the filter bar at all.
+  const requiredId = 'NATIVE_FILTER-required-with-default';
+  const untouchedId = 'NATIVE_FILTER-untouched';
+  const updateDataMaskSpy = jest.spyOn(dataMaskActions, 'updateDataMask');
+
+  fetchMock.post(
+    'glob:*/api/v1/chart/data',
+    {
+      result: [
+        {
+          data: [{ region: 'East' }, { region: 'West' }],
+          colnames: ['region'],
+          coltypes: [1],
+          applied_filters: [],
+        },
+      ],
+    },
+    { name: 'chart-data-issue-34617' },
+  );
+
+  const requiredFilter = createFilter({
+    id: requiredId,
+    name: 'Required Region',
+    filterType: 'filter_select',
+    targets: [{ datasetId: 7, column: { name: 'region' } }],
+    controlValues: { enableEmptyFilter: true },
+    defaultDataMask: { filterState: { value: ['East'] }, extraFormData: {} },
+    chartsInScope: [18],
+  });
+  const untouchedFilter = createFilter({
+    id: untouchedId,
+    name: 'Untouched Color',
+    filterType: 'filter_select',
+    targets: [{ datasetId: 7, column: { name: 'color' } }],
+    chartsInScope: [18],
+  });
+
+  const state = {
+    ...stateWithoutNativeFilters,
+    dashboardInfo: {
+      id: 1,
+      dash_edit_perm: true,
+      filterBarOrientation: FilterBarOrientation.Vertical,
+      metadata: {
+        native_filter_configuration: [requiredFilter, untouchedFilter],
+        chart_configuration: {},
+      },
+    },
+    dashboardState: {
+      ...stateWithoutNativeFilters.dashboardState,
+      activeTabs: ['ROOT_ID'],
+    },
+    // The default value has been hydrated into the applied dataMask, but its
+    // extraFormData has not been derived yet — the state right after a
+    // dashboard with default filter values loads.
+    dataMask: {
+      [requiredId]: createDataMask(requiredId, ['East'], {}),
+    },
+    nativeFilters: {
+      filters: {
+        [requiredId]: requiredFilter,
+        [untouchedId]: untouchedFilter,
+      },
+      filtersState: {},
+    },
+  };
+
+  const props = createOpenedBarProps();
+  renderFilterBar(props, state);
+
+  // Flush the filter control's data fetch and the plugin's initialization
+  // effects (same timer pattern as the other tests in this file).
+  await act(async () => {
+    jest.advanceTimersByTime(1000);
+  });
+
+  // Once the filter control loads its values and emits the dataMask derived
+  // from the default value, FilterBar auto-applies it to Redux instead of
+  // holding it hostage behind a disabled Apply button.
+  await waitFor(() => {
+    expect(updateDataMaskSpy).toHaveBeenCalledWith(
+      requiredId,
+      expect.objectContaining({
+        extraFormData: {
+          filters: [{ col: 'region', op: 'IN', val: ['East'] }],
+        },
+        filterState: expect.objectContaining({ value: ['East'] }),
+      }),
+    );
+  });
+
+  // The other filter was never touched, and no dispatch was needed for it.
+  expect(
+    updateDataMaskSpy.mock.calls.every(([id]) => id === requiredId),
+  ).toBe(true);
+
+  // Nothing is left pending: the default value is already applied, so the
+  // Apply button is not blocking on untouched filters.
+  expect(screen.getByTestId(getTestId('apply-button'))).toBeDisabled();
+
+  updateDataMaskSpy.mockRestore();
+});
+
 test('FilterBar with orientation=Vertical renders Vertical layout (sanity counterpart to the horizontal routing test)', () => {
   // Paired control for the routing test above: with Vertical orientation,
   // the settings gear must NOT be present (Vertical.tsx does not render