diff --git a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.test.tsx b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.test.tsx index 4f66e6dd61be..bf90cd77165d 100644 --- a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.test.tsx +++ b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.test.tsx @@ -379,4 +379,300 @@ describe('SelectFilterPlugin', () => { userEvent.type(screen.getByRole('combobox'), 'new value'); expect(await screen.findByTitle('new value')).toBeInTheDocument(); }); + + test('preserves backend order when sortMetric is specified', () => { + const testData = [ + { gender: 'zebra' }, + { gender: 'alpha' }, + { gender: 'beta' }, + ]; + + const testProps = { + ...selectMultipleProps, + formData: { + ...selectMultipleProps.formData, + sortMetric: 'count', + sortAscending: true, + }, + queriesData: [ + { + rowcount: 3, + colnames: ['gender'], + coltypes: [1], + data: testData, + applied_filters: [{ column: 'gender' }], + rejected_filters: [], + }, + ], + filterState: { + value: [], + label: '', + excludeFilterValues: true, + }, + }; + + render( + // @ts-ignore + , + { + useRedux: true, + initialState: { + nativeFilters: { + filters: { + 'test-filter': { + name: 'Test Filter', + }, + }, + }, + dataMask: { + 'test-filter': { + extraFormData: {}, + filterState: { + value: [], + label: '', + excludeFilterValues: true, + }, + }, + }, + }, + }, + ); + + const filterSelect = screen.getAllByRole('combobox')[0]; + userEvent.click(filterSelect); + + // When sortMetric is specified, options should appear in the original data order + // (zebra, alpha, beta) not alphabetically sorted + const options = screen.getAllByRole('option'); + expect(options[0]).toHaveTextContent('zebra'); + expect(options[1]).toHaveTextContent('alpha'); + expect(options[2]).toHaveTextContent('beta'); + }); + + test('applies alphabetical sorting when sortMetric is not specified', () => { + const testData = [ + { gender: 'zebra' }, + { gender: 'alpha' }, + { gender: 'beta' }, + ]; + + const testProps = { + ...selectMultipleProps, + formData: { + ...selectMultipleProps.formData, + sortMetric: undefined, + sortAscending: true, + }, + queriesData: [ + { + rowcount: 3, + colnames: ['gender'], + coltypes: [1], + data: testData, + applied_filters: [{ column: 'gender' }], + rejected_filters: [], + }, + ], + filterState: { + value: [], + label: '', + excludeFilterValues: true, + }, + }; + + render( + // @ts-ignore + , + { + useRedux: true, + initialState: { + nativeFilters: { + filters: { + 'test-filter': { + name: 'Test Filter', + }, + }, + }, + dataMask: { + 'test-filter': { + extraFormData: {}, + filterState: { + value: [], + label: '', + excludeFilterValues: true, + }, + }, + }, + }, + }, + ); + + const filterSelect = screen.getAllByRole('combobox')[0]; + userEvent.click(filterSelect); + + // When sortMetric is not specified, options should be sorted alphabetically + // (alpha, beta, zebra) + const options = screen.getAllByRole('option'); + expect(options[0]).toHaveTextContent('alpha'); + expect(options[1]).toHaveTextContent('beta'); + expect(options[2]).toHaveTextContent('zebra'); + }); + + test('applies descending alphabetical sorting when sortAscending is false and no sortMetric', () => { + const testData = [ + { gender: 'zebra' }, + { gender: 'alpha' }, + { gender: 'beta' }, + ]; + + const testProps = { + ...selectMultipleProps, + formData: { + ...selectMultipleProps.formData, + sortMetric: undefined, + sortAscending: false, + }, + queriesData: [ + { + rowcount: 3, + colnames: ['gender'], + coltypes: [1], + data: testData, + applied_filters: [{ column: 'gender' }], + rejected_filters: [], + }, + ], + filterState: { + value: [], + label: '', + excludeFilterValues: true, + }, + }; + + render( + // @ts-ignore + , + { + useRedux: true, + initialState: { + nativeFilters: { + filters: { + 'test-filter': { + name: 'Test Filter', + }, + }, + }, + dataMask: { + 'test-filter': { + extraFormData: {}, + filterState: { + value: [], + label: '', + excludeFilterValues: true, + }, + }, + }, + }, + }, + ); + + const filterSelect = screen.getAllByRole('combobox')[0]; + userEvent.click(filterSelect); + + // When sortAscending is false and no sortMetric, options should be sorted + // in descending alphabetical order (zebra, beta, alpha) + const options = screen.getAllByRole('option'); + expect(options[0]).toHaveTextContent('zebra'); + expect(options[1]).toHaveTextContent('beta'); + expect(options[2]).toHaveTextContent('alpha'); + }); + + test('preserves backend order even when sortAscending is false and sortMetric is specified', () => { + const testData = [ + { gender: 'zebra' }, + { gender: 'alpha' }, + { gender: 'beta' }, + ]; + + const testProps = { + ...selectMultipleProps, + formData: { + ...selectMultipleProps.formData, + sortMetric: 'count', + sortAscending: false, + }, + queriesData: [ + { + rowcount: 3, + colnames: ['gender'], + coltypes: [1], + data: testData, + applied_filters: [{ column: 'gender' }], + rejected_filters: [], + }, + ], + filterState: { + value: [], + label: '', + excludeFilterValues: true, + }, + }; + + render( + // @ts-ignore + , + { + useRedux: true, + initialState: { + nativeFilters: { + filters: { + 'test-filter': { + name: 'Test Filter', + }, + }, + }, + dataMask: { + 'test-filter': { + extraFormData: {}, + filterState: { + value: [], + label: '', + excludeFilterValues: true, + }, + }, + }, + }, + }, + ); + + const filterSelect = screen.getAllByRole('combobox')[0]; + userEvent.click(filterSelect); + + // When sortMetric is specified, original order should be preserved regardless + // of sortAscending value (zebra, alpha, beta) + const options = screen.getAllByRole('option'); + expect(options[0]).toHaveTextContent('zebra'); + expect(options[1]).toHaveTextContent('alpha'); + expect(options[2]).toHaveTextContent('beta'); + }); }); diff --git a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx index 4ba6e1b73587..69da95bbb939 100644 --- a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx @@ -317,13 +317,20 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { const sortComparator = useCallback( (a: LabeledValue, b: LabeledValue) => { + // When sortMetric is specified, the backend already sorted the data correctly + // Don't override the backend's metric-based sorting with frontend alphabetical sorting + if (formData.sortMetric) { + return 0; // Preserve the original order from the backend + } + + // Only apply alphabetical sorting when no sortMetric is specified const labelComparator = propertyComparator('label'); if (formData.sortAscending) { return labelComparator(a, b); } return labelComparator(b, a); }, - [formData.sortAscending], + [formData.sortAscending, formData.sortMetric], ); // Use effect for initialisation for filter plugin