This is an automated email from the ASF dual-hosted git repository.
kgabryje pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new 7ec5f1d7ecf fix(native-filters): Filters with select first value not
restored correctly from url (#37855)
7ec5f1d7ecf is described below
commit 7ec5f1d7ecf96e7e5cf6181dbf5f76d9738984e5
Author: Kamil Gabryjelski <[email protected]>
AuthorDate: Tue Feb 10 18:54:42 2026 +0100
fix(native-filters): Filters with select first value not restored correctly
from url (#37855)
---
.../components/Select/SelectFilterPlugin.test.tsx | 118 +++++++++++++++++++++
.../components/Select/SelectFilterPlugin.tsx | 5 -
2 files changed, 118 insertions(+), 5 deletions(-)
diff --git
a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.test.tsx
b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.test.tsx
index 2427047908d..310d6fa5b9f 100644
---
a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.test.tsx
+++
b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.test.tsx
@@ -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'],
+ }),
+ }),
+ );
+ });
+});
diff --git
a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx
b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx
index 8f94817b481..58cbfc5dceb 100644
--- a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx
+++ b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx
@@ -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),
)