This is an automated email from the ASF dual-hosted git repository. sadpandajoe pushed a commit to branch explore-rtl-control-deps in repository https://gitbox.apache.org/repos/asf/superset.git
commit 42a53a24f34a2102218aa03e321d998c41761108 Author: Joe Li <[email protected]> AuthorDate: Wed Sep 30 17:02:48 2026 -0700 test(explore): tighten viz switch assertions and pin object-form control filtering Assert the reducer builds the target viz's control state, cover formDataOverrides receiving standardized metrics and columns, and pin that getSectionsToRender only filters string control references. Co-Authored-By: Claude Opus 5.5 <[email protected]> --- .../components/ControlPanelsContainer.test.tsx | 50 +++++++++++++++++- .../controlUtils/getSectionsToRender.test.ts | 60 +++++++++++++++++++++- 2 files changed, 107 insertions(+), 3 deletions(-) diff --git a/superset-frontend/src/explore/components/ControlPanelsContainer.test.tsx b/superset-frontend/src/explore/components/ControlPanelsContainer.test.tsx index 1e0cd5e630d..75eef10905d 100644 --- a/superset-frontend/src/explore/components/ControlPanelsContainer.test.tsx +++ b/superset-frontend/src/explore/components/ControlPanelsContainer.test.tsx @@ -27,11 +27,13 @@ import { } from 'spec/helpers/testing-library'; import reducerIndex from 'spec/helpers/reducerIndex'; import { t } from '@apache-superset/core/translation'; +import { getStandardizedControls } from '@superset-ui/chart-controls'; import { DatasourceType, getChartControlPanelRegistry, isFeatureEnabled, FeatureFlag, + QueryFormData, } from '@superset-ui/core'; import { defaultControls, @@ -153,8 +155,30 @@ describe('ControlPanelsContainer', () => { ], }; + // Consumes the standardized metrics/columns under field names the source viz + // never had, so values can only arrive through the StandardizedFormData + // transform and not by copying same-named form_data keys. + const switchMappedConfig = { + controlPanelSections: [ + { + label: t('Query'), + expanded: true, + controlSetRows: [['size'], ['series_columns']], + }, + ], + formDataOverrides: (formData: QueryFormData) => ({ + ...formData, + size: getStandardizedControls().shiftMetric(), + series_columns: getStandardizedControls().popAllColumns(), + }), + }; + beforeEach(() => { getChartControlPanelRegistry().registerValue('table', defaultTableConfig); + getChartControlPanelRegistry().registerValue( + 'switch-mapped', + switchMappedConfig, + ); getChartControlPanelRegistry().registerValue( 'switch-target', switchTargetConfig, @@ -167,6 +191,7 @@ describe('ControlPanelsContainer', () => { afterEach(() => { getChartControlPanelRegistry().remove('table'); getChartControlPanelRegistry().remove('switch-target'); + getChartControlPanelRegistry().remove('switch-mapped'); jest.clearAllMocks(); }); @@ -737,19 +762,24 @@ describe('ControlPanelsContainer', () => { await screen.findByRole('button', { name: /group by/i }), ).toBeInTheDocument(); expect(screen.queryByText(/series limit/i)).not.toBeInTheDocument(); + expect( + (store.getState() as ExplorePageState).explore.controls.limit, + ).toBeUndefined(); act(() => { store.dispatch(setControlValue('viz_type', 'switch-target')); }); - // `limit` exists only in the target viz, so its control is rendered from - // the control state the reducer built for that viz. expect( (await screen.findAllByText(/series limit/i)).length, ).toBeGreaterThan(0); expect( screen.queryByRole('button', { name: /group by/i }), ).not.toBeInTheDocument(); + // The label above comes from the registered panel config, so also check the + // reducer built control state for `limit`, which only the target viz has. + const { controls } = (store.getState() as ExplorePageState).explore; + expect(controls.limit).toMatchObject({ label: 'Series limit' }); }); test('switching viz_type carries shared metrics and groupby over and drops controls the new viz lacks', async () => { @@ -775,4 +805,20 @@ describe('ControlPanelsContainer', () => { expect(controls.all_columns).toBeUndefined(); expect(formData.all_columns).toBeUndefined(); }); + + test('switching viz_type hands standardized metrics and columns to the target formDataOverrides', async () => { + const store = createStoreBackedExplore(); + render(<StoreBackedControlPanelsContainer />, { store }); + await screen.findByRole('button', { name: /group by/i }); + + act(() => { + store.dispatch(setControlValue('viz_type', 'switch-mapped')); + }); + + const { form_data: formData } = (store.getState() as ExplorePageState) + .explore; + expect(formData.viz_type).toBe('switch-mapped'); + expect(formData.size).toBe('count'); + expect(formData.series_columns).toEqual(['name', 'gender']); + }); }); diff --git a/superset-frontend/src/explore/controlUtils/getSectionsToRender.test.ts b/superset-frontend/src/explore/controlUtils/getSectionsToRender.test.ts index fba9e0af1f2..f2a5c0dfd46 100644 --- a/superset-frontend/src/explore/controlUtils/getSectionsToRender.test.ts +++ b/superset-frontend/src/explore/controlUtils/getSectionsToRender.test.ts @@ -20,7 +20,10 @@ import { DatasourceType, getChartControlPanelRegistry, } from '@superset-ui/core'; -import { ControlPanelConfig } from '@superset-ui/chart-controls'; +import { + ControlPanelConfig, + sharedControls, +} from '@superset-ui/chart-controls'; import { getSectionsToRender } from './getSectionsToRender'; const VIZ_TYPE = 'sections-to-render-test-viz'; @@ -55,6 +58,31 @@ function namesInSection(datasourceType: DatasourceType): string[] { .filter((name): name is string => Boolean(name)); } +const OBJECT_FORM_SECTION_LABEL = 'Object form controls'; + +// Mix of string references (filtered by datasource type) and the object form +// that real chart plugins use for the same controls. +const objectFormConfig: ControlPanelConfig = { + controlPanelSections: [ + { + label: OBJECT_FORM_SECTION_LABEL, + expanded: true, + controlSetRows: [ + ['granularity'], + ['granularity_sqla'], + [{ name: 'granularity', config: sharedControls.granularity }], + [ + { + name: 'granularity_sqla', + config: sharedControls.granularity_sqla, + }, + { name: 'time_grain_sqla', config: sharedControls.time_grain_sqla }, + ], + ], + }, + ], +}; + beforeEach(() => { getChartControlPanelRegistry().registerValue(VIZ_TYPE, controlConfig); }); @@ -119,3 +147,33 @@ test('returns only the default sections for an unregistered viz type', () => { expect(sections.map(section => section?.label)).not.toContain(SECTION_LABEL); expect(sections.length).toBeGreaterThan(0); }); + +test.each([ + [ + DatasourceType.Table, + ['granularity_sqla', 'granularity', 'granularity_sqla', 'time_grain_sqla'], + ], + [ + DatasourceType.SemanticView, + ['granularity_sqla', 'granularity', 'granularity_sqla', 'time_grain_sqla'], + ], + [ + DatasourceType.Dataset, + ['granularity', 'granularity', 'granularity_sqla', 'time_grain_sqla'], + ], +])( + 'documents that only string control references are filtered, object-form controls pass through, for %s datasources', + (datasourceType, expectedNames) => { + getChartControlPanelRegistry().registerValue(VIZ_TYPE, objectFormConfig); + + const section = getSectionsToRender(VIZ_TYPE, datasourceType).find( + item => item?.label === OBJECT_FORM_SECTION_LABEL, + ); + const rows = (section?.controlSetRows ?? []) as ExpandedRow[]; + const names = rows + .flat() + .map(item => (typeof item === 'string' ? item : item?.name)); + + expect(names).toEqual(expectedNames); + }, +);
