fix(native-filters): Filters with select first value not restored correctly from url (#37855)

This commit is contained in:
Kamil Gabryjelski
2026-02-10 18:54:42 +01:00
committed by GitHub
parent 76aa91f5ea
commit 7ec5f1d7ec
2 changed files with 118 additions and 5 deletions

View File

@@ -1131,3 +1131,121 @@ test('Clear boolean TRUE value', async () => {
});
});
});
test('preserves dependent filter value restored from URL when it exists in data', async () => {
const setDataMaskMock = jest.fn();
const testProps = {
...selectMultipleProps,
formData: {
...selectMultipleProps.formData,
defaultToFirstItem: true,
multiSelect: false,
// Non-empty extraFormData indicates parent filter dependency
extraFormData: {
filters: [{ col: 'region', op: 'IN', val: ['North America'] }],
},
},
// 'girl' is NOT the first item but exists in data — simulates a
// value restored from URL/permalink for a dependent filter
filterState: { value: ['girl'] },
};
render(
// @ts-expect-error
<SelectFilterPlugin
// @ts-expect-error
{...transformProps(testProps)}
setDataMask={setDataMaskMock}
showOverflow={false}
/>,
{
useRedux: true,
initialState: {
nativeFilters: {
filters: {
'test-filter': {
name: 'Test Filter',
},
},
},
dataMask: {
'test-filter': {
extraFormData: {},
filterState: {
value: ['girl'],
},
},
},
},
},
);
await waitFor(() => {
expect(setDataMaskMock).toHaveBeenLastCalledWith(
expect.objectContaining({
filterState: expect.objectContaining({
value: ['girl'],
}),
}),
);
});
});
test('resets dependent filter to first item when value does not exist in data', async () => {
const setDataMaskMock = jest.fn();
const testProps = {
...selectMultipleProps,
formData: {
...selectMultipleProps.formData,
defaultToFirstItem: true,
multiSelect: false,
extraFormData: {
filters: [{ col: 'region', op: 'IN', val: ['North America'] }],
},
},
// 'unknown' does NOT exist in data — simulates a stale value after
// parent filter changed to a different selection
filterState: { value: ['unknown'] },
};
render(
// @ts-expect-error
<SelectFilterPlugin
// @ts-expect-error
{...transformProps(testProps)}
setDataMask={setDataMaskMock}
showOverflow={false}
/>,
{
useRedux: true,
initialState: {
nativeFilters: {
filters: {
'test-filter': {
name: 'Test Filter',
},
},
},
dataMask: {
'test-filter': {
extraFormData: {},
filterState: {
value: ['unknown'],
},
},
},
},
},
);
await waitFor(() => {
expect(setDataMaskMock).toHaveBeenLastCalledWith(
expect.objectContaining({
filterState: expect.objectContaining({
// Should reset to first item ('boy') since 'unknown' is not in data
value: ['boy'],
}),
}),
);
});
});

View File

@@ -151,7 +151,6 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
const [col] = groupby;
const [initialColtypeMap] = useState(coltypeMap);
const [search, setSearch] = useState('');
const isChangedByUser = useRef(false);
const prevDataRef = useRef(data);
const [dataMask, dispatchDataMask] = useImmerReducer(reducer, {
extraFormData: {},
@@ -273,8 +272,6 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
} else {
updateDataMask(values);
}
isChangedByUser.current = true;
},
[updateDataMask, formData.nativeFilterId, clearAllTrigger],
);
@@ -400,14 +397,12 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
// If data actually changed (e.g., due to parent filter), reset flag
if (hasDataChanged) {
isChangedByUser.current = false;
prevDataRef.current = data;
}
}, [data, col]);
useEffect(() => {
if (
isChangedByUser.current &&
filterState.value?.every((value?: any) =>
data.some(row => row[col] === value),
)