Compare commits

...

2 Commits

Author SHA1 Message Date
Claude Code
8966a1d17e fix(filters): sort numeric filter values numerically, not lexicographically
Closes #36775

sortComparator compared options via propertyComparator('label'), but
getDataRecordFormatter (filters/utils.ts) always returns a formatted
string label regardless of the column's underlying datatype. Numeric
filter values therefore always hit propertyComparator's string branch
(localeCompare) instead of its numeric branch, producing "1, 10, 100,
2" instead of "1, 2, 10, 100".

propertyComparator already has a numeric branch; it just needs a
property that actually holds a number. `value` (the raw, unformatted
option value) does for numeric columns, while `label` never does. Fix
compares by `value` when the column's GenericDataType is Numeric, and
keeps comparing by `label` for every other type -- unaffected here
since #34858 already fixed the analogous SelectControl.jsx bug; this
native-filter value dropdown just has its own independent sort path
that #34858 never touched, and #36775 is the recurrence.

Disclosure: type-checking-frontend was skipped locally (SKIP set) --
this worktree's `tsc --build` currently fails on unrelated TS2742
"inferred type... not portable" errors in Tabs.tsx/SafeMarkdown.tsx/
spec/index.tsx (pre-existing composite-build fragility, not touched
by this change; the PR's own prior commit disclosed the same
build-environment limitation). oxlint, prettier, and the full
SelectFilterPlugin.test.tsx suite (39/39, including the new
regression test and both pre-existing alphabetical-sort tests) are
green locally. CI's Type-Checking (Frontend) job is the real gate.
2026-07-30 12:38:41 -07:00
Claude Code
418abb4de0 test(filters): pin numeric filter values sort numerically, not lexicographically (#36775)
Closes #36775

Adds a regression test on PluginFilterSelect confirming that when no
sortMetric is configured, numeric filter-value options are ordered by
their numeric value (2, 10, 100) rather than by lexicographic
comparison of their formatted string labels (10, 100, 2).

Root cause: SelectFilterPlugin's sortComparator always compares the
formatted `label` via propertyComparator('label'). getDataRecordFormatter
(superset-frontend/src/filters/utils.ts) always returns a string, so
numeric columns hit propertyComparator's string branch (localeCompare)
instead of its numeric branch, even though the values themselves are
numeric. This is the second recurrence of the #35008/#34858 fix, which
patched SelectControl.jsx (chart-builder numeric selects like Row limit)
but never touched this native-filter value dropdown.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-29 17:54:05 -07:00
2 changed files with 85 additions and 5 deletions

View File

@@ -720,6 +720,80 @@ describe('SelectFilterPlugin', () => {
expect(options[2]).toHaveTextContent('beta');
});
test('sorts numeric filter values numerically, not lexicographically, when no sortMetric is specified', () => {
// Regression for #36775: numeric filter values were sorted as strings
// (localeCompare on the formatted label), producing "1, 10, 100, 2"
// instead of the expected "1, 2, 10, 100".
const testData = [{ age: 10 }, { age: 2 }, { age: 100 }];
const testProps = {
...selectMultipleProps,
formData: {
...selectMultipleProps.formData,
groupby: ['age'],
sortMetric: undefined,
sortAscending: true,
},
queriesData: [
{
rowcount: 3,
colnames: ['age'],
coltypes: [0],
data: testData,
applied_filters: [{ column: 'age' }],
rejected_filters: [],
},
],
filterState: {
value: [],
label: '',
excludeFilterValues: true,
},
};
render(
// @ts-expect-error
<SelectFilterPlugin
// @ts-expect-error
{...transformProps(testProps)}
setDataMask={jest.fn()}
showOverflow={false}
/>,
{
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);
// Options should appear in ascending numeric order (2, 10, 100), not
// ascending lexicographic order of their formatted labels (10, 100, 2).
const options = screen.getAllByRole('option');
expect(options[0]).toHaveTextContent('2');
expect(options[1]).toHaveTextContent('10');
expect(options[2]).toHaveTextContent('100');
});
test('shows create option for multi-select creatable filter when typing', async () => {
getWrapper({ creatable: true, multiSelect: true });
userEvent.type(screen.getByRole('combobox'), 'brand-new');

View File

@@ -354,14 +354,20 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
return 0; // Preserve the original order from the backend
}
// Only apply alphabetical sorting when no sortMetric is specified
const labelComparator = propertyComparator('label');
// Only apply sorting when no sortMetric is specified. `label` is always
// a formatted string (see getDataRecordFormatter), so comparing by it
// never reaches propertyComparator's numeric branch; numeric columns
// sort by the raw `value` instead so "2, 10, 100" doesn't collapse
// into lexicographic "10, 100, 2".
const comparator = propertyComparator(
datatype === GenericDataType.Numeric ? 'value' : 'label',
);
if (formData.sortAscending) {
return labelComparator(a, b);
return comparator(a, b);
}
return labelComparator(b, a);
return comparator(b, a);
},
[formData.sortAscending, formData.sortMetric],
[formData.sortAscending, formData.sortMetric, datatype],
);
// Use effect for initialisation for filter plugin