From 962db8705ba271e8e2ab66170b0ae4afa7c11c4d Mon Sep 17 00:00:00 2001 From: Muhammad Musfir Date: Fri, 11 Jul 2025 14:06:27 +0200 Subject: [PATCH 1/9] chore(fix): adds dependent filters fixes * adds a fix for correctly selecting first value of a dependent filter * adds a fix for automatically selecting the correct dependent filter value on changing parent filter value * adds dependent filters fix support for time range filter --- .../e2e/dashboard/nativeFilters.test.ts | 61 +++++++++++++++++++ .../FilterBar/FilterControls/FilterValue.tsx | 34 +++++++++++ .../components/Select/SelectFilterPlugin.tsx | 45 ++++++++++++++ 3 files changed, 140 insertions(+) diff --git a/superset-frontend/cypress-base/cypress/e2e/dashboard/nativeFilters.test.ts b/superset-frontend/cypress-base/cypress/e2e/dashboard/nativeFilters.test.ts index 818d6ebda6e0..4557bac26566 100644 --- a/superset-frontend/cypress-base/cypress/e2e/dashboard/nativeFilters.test.ts +++ b/superset-frontend/cypress-base/cypress/e2e/dashboard/nativeFilters.test.ts @@ -45,6 +45,7 @@ import { SAMPLE_CHART, visitDashboard, } from './shared_dashboard_functions'; +import { waitForChartLoad } from 'cypress/utils'; function selectFilter(index: number) { cy.get("[data-test='filter-title-container'] [draggable='true']") @@ -160,6 +161,66 @@ describe('Native filters', () => { ); }); + it('Dependent filter selects first item based on parent filter selection', () => { + prepareDashboardFilters([ + { name: 'region', column: 'region', datasetId: 2 }, + { name: 'country_name', column: 'country_name', datasetId: 2 }, + ]); + + enterNativeFilterEditModal(); + + selectFilter(0); + cy.get(nativeFilters.filterConfigurationSections.displayedSection).within(() => { + cy.contains('Select first filter value by default') + .should('be.visible') + .click(); + }); + cy.get(nativeFilters.filterConfigurationSections.displayedSection).within(() => { + cy.contains('Can select multiple values ') + .should('be.visible') + .click(); + }); + + selectFilter(1); + cy.get(nativeFilters.filterConfigurationSections.displayedSection).within(() => { + cy.contains('Values are dependent on other filters') + .should('be.visible') + .click(); + }); + cy.get(nativeFilters.filterConfigurationSections.displayedSection).within(() => { + cy.contains('Can select multiple values ') + .should('be.visible') + .click(); + }); + addParentFilterWithValue(0, testItems.topTenChart.filterColumnRegion); + cy.get(nativeFilters.filterConfigurationSections.displayedSection).within(() => { + cy.contains('Select first filter value by default') + .should('be.visible') + .click(); + }); + + // cannot use saveNativeFilterSettings because there is a bug which + // sometimes does not allow charts to load when enabling the 'Select first filter value by default' + // to be saved when using dependent filters so, + // you reload the window. + cy.get('.ant-modal-footer') + .contains('Save') + .should('be.visible') + .click(); + + cy.get('.ant-modal-content').should('not.exist'); + cy.reload(); + [SAMPLE_CHART].forEach(waitForChartLoad); + // saveNativeFilterSettings([SAMPLE_CHART]); + + applyNativeFilterValueWithIndex(0, 'North America'); + + // Check that dependent filter auto-selects the first item + cy.get(nativeFilters.filterFromDashboardView.filterContent) + .eq(1) + .should('contain.text', 'Bermuda'); + }); + it('User can create filter depend on 2 other filters', () => { prepareDashboardFilters([ { name: 'region', column: 'region', datasetId: 2 }, diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx index 16263024d29b..4eb7158f34ad 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx @@ -58,6 +58,7 @@ import { FilterControlProps } from './types'; import { getFormData } from '../../utils'; import { useFilterDependencies } from './state'; import { useFilterOutlined } from '../useFilterOutlined'; +import { useFilters } from '../state'; const HEIGHT = 32; @@ -107,6 +108,7 @@ const FilterValue: FC = ({ state => state.dashboardInfo.id, ); + const allFilters = useFilters(); const [error, setError] = useState(); const [formData, setFormData] = useState>({ inView: false, @@ -155,6 +157,37 @@ const FilterValue: FC = ({ dashboardId, }); const filterOwnState = filter.dataMask?.ownState || {}; + if (filter?.cascadeParentIds?.length) { + // check if it is a child filter + + let selectedParentFilterValueCounts = 0; + + filter?.cascadeParentIds?.forEach(pId => { + if ( + allFilters[pId]?.controlValues?.defaultToFirstItem || + allFilters[pId]?.defaultDataMask?.extraFormData?.filters || + allFilters[pId]?.defaultDataMask?.extraFormData?.time_range + ) { + selectedParentFilterValueCounts += + allFilters[pId]?.defaultDataMask?.extraFormData?.filters?.length ?? 1; + } + }); + + // check if all parent filters with defaults have a value selected + + let depsCount = dependencies.filters?.length ?? 0; + + if (dependencies?.time_range) { + depsCount += 1; + } + + if (selectedParentFilterValueCounts !== depsCount) { + // child filter should not request backend until it + // has all the required information from parent filters + return; + } + } + // TODO: We should try to improve our useEffect hooks to depend more on // granular information instead of big objects that require deep comparison. const customizer = ( @@ -226,6 +259,7 @@ const FilterValue: FC = ({ hasDataSource, isRefreshing, shouldRefresh, + allFilters, ]); useEffect(() => { diff --git a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx index baa52d58e485..d46aa3a9e87f 100644 --- a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx @@ -151,6 +151,8 @@ 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: {}, filterState, @@ -271,6 +273,8 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { } else { updateDataMask(values); } + + isChangedByUser.current = true; }, [updateDataMask, formData.nativeFilterId, clearAllTrigger], ); @@ -368,6 +372,47 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { inverseSelection, ]); + useEffect(() => { + const prev = prevDataRef.current; + const curr = data; + + const prevFirst = prev?.[0]?.[col]; + const currFirst = curr?.[0]?.[col]; + + // If data actually changed (e.g., due to parent filter), reset flag + if (prevFirst !== currFirst) { + isChangedByUser.current = false; + prevDataRef.current = data; + } + }, [data, col]); + + useEffect(() => { + if (isChangedByUser.current) return; + + const firstItem: SelectValue = data[0] + ? (groupby.map(col => data[0][col]) as string[]) + : null; + + if ( + defaultToFirstItem && + Object.keys(formData?.extraFormData || {}).length && + filterState.value !== undefined && + firstItem !== null && + filterState.value !== firstItem + ) { + if (firstItem?.[0] !== undefined) { + updateDataMask(firstItem); + } + } + }, [ + defaultToFirstItem, + updateDataMask, + formData, + data, + JSON.stringify(filterState.value), + isChangedByUser.current, + ]); + useEffect(() => { setDataMask(dataMask); }, [JSON.stringify(dataMask)]); From b78c57524a6f61c1bfd0a900e31dc236c942d60e Mon Sep 17 00:00:00 2001 From: Muhammad Musfir Date: Mon, 14 Jul 2025 09:34:06 +0200 Subject: [PATCH 2/9] chore(fix): adds updates for FilterValue and SelectFilterPlugin --- .../FilterBar/FilterControls/FilterValue.tsx | 2 +- .../components/Select/SelectFilterPlugin.tsx | 15 ++++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx index 4eb7158f34ad..bc7f53c254f6 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx @@ -158,7 +158,7 @@ const FilterValue: FC = ({ }); const filterOwnState = filter.dataMask?.ownState || {}; if (filter?.cascadeParentIds?.length) { - // check if it is a child filter + // Prevent unnecessary backend requests by validating parent filter selections first let selectedParentFilterValueCounts = 0; diff --git a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx index d46aa3a9e87f..2c15f7975cd6 100644 --- a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx @@ -372,22 +372,27 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { inverseSelection, ]); - useEffect(() => { + useEffect(() => { const prev = prevDataRef.current; const curr = data; - const prevFirst = prev?.[0]?.[col]; - const currFirst = curr?.[0]?.[col]; + const hasDataChanged = + prev?.length !== curr?.length || + JSON.stringify(prev?.map(row => row[col])) !== JSON.stringify(curr?.map(row => row[col])); // If data actually changed (e.g., due to parent filter), reset flag - if (prevFirst !== currFirst) { + if (hasDataChanged) { isChangedByUser.current = false; prevDataRef.current = data; } }, [data, col]); useEffect(() => { - if (isChangedByUser.current) return; + if ( + isChangedByUser.current && + filterState.value && + data.some(row => row[col] === filterState.value[0]) + ) return; const firstItem: SelectValue = data[0] ? (groupby.map(col => data[0][col]) as string[]) From 537a52e4a548aaeda216321b91caa8645f29478b Mon Sep 17 00:00:00 2001 From: Muhammad Musfir Date: Mon, 14 Jul 2025 10:25:49 +0200 Subject: [PATCH 3/9] chore(fix): adds eslint and prettier fixes --- .../e2e/dashboard/nativeFilters.test.ts | 67 ++++++++++--------- .../FilterBar/FilterControls/FilterValue.tsx | 3 +- .../components/Select/SelectFilterPlugin.tsx | 6 +- 3 files changed, 43 insertions(+), 33 deletions(-) diff --git a/superset-frontend/cypress-base/cypress/e2e/dashboard/nativeFilters.test.ts b/superset-frontend/cypress-base/cypress/e2e/dashboard/nativeFilters.test.ts index 4557bac26566..ee2ae8a59036 100644 --- a/superset-frontend/cypress-base/cypress/e2e/dashboard/nativeFilters.test.ts +++ b/superset-frontend/cypress-base/cypress/e2e/dashboard/nativeFilters.test.ts @@ -22,6 +22,7 @@ import { dataTestChartName, } from 'cypress/support/directories'; +import { waitForChartLoad } from 'cypress/utils'; import { addParentFilterWithValue, applyNativeFilterValueWithIndex, @@ -45,7 +46,6 @@ import { SAMPLE_CHART, visitDashboard, } from './shared_dashboard_functions'; -import { waitForChartLoad } from 'cypress/utils'; function selectFilter(index: number) { cy.get("[data-test='filter-title-container'] [draggable='true']") @@ -170,43 +170,50 @@ describe('Native filters', () => { enterNativeFilterEditModal(); selectFilter(0); - cy.get(nativeFilters.filterConfigurationSections.displayedSection).within(() => { - cy.contains('Select first filter value by default') - .should('be.visible') - .click(); - }); - cy.get(nativeFilters.filterConfigurationSections.displayedSection).within(() => { - cy.contains('Can select multiple values ') - .should('be.visible') - .click(); - }); + cy.get(nativeFilters.filterConfigurationSections.displayedSection).within( + () => { + cy.contains('Select first filter value by default') + .should('be.visible') + .click(); + }, + ); + cy.get(nativeFilters.filterConfigurationSections.displayedSection).within( + () => { + cy.contains('Can select multiple values ') + .should('be.visible') + .click(); + }, + ); selectFilter(1); - cy.get(nativeFilters.filterConfigurationSections.displayedSection).within(() => { - cy.contains('Values are dependent on other filters') - .should('be.visible') - .click(); - }); - cy.get(nativeFilters.filterConfigurationSections.displayedSection).within(() => { - cy.contains('Can select multiple values ') - .should('be.visible') - .click(); - }); + cy.get(nativeFilters.filterConfigurationSections.displayedSection).within( + () => { + cy.contains('Values are dependent on other filters') + .should('be.visible') + .click(); + }, + ); + cy.get(nativeFilters.filterConfigurationSections.displayedSection).within( + () => { + cy.contains('Can select multiple values ') + .should('be.visible') + .click(); + }, + ); addParentFilterWithValue(0, testItems.topTenChart.filterColumnRegion); - cy.get(nativeFilters.filterConfigurationSections.displayedSection).within(() => { - cy.contains('Select first filter value by default') - .should('be.visible') - .click(); - }); + cy.get(nativeFilters.filterConfigurationSections.displayedSection).within( + () => { + cy.contains('Select first filter value by default') + .should('be.visible') + .click(); + }, + ); // cannot use saveNativeFilterSettings because there is a bug which // sometimes does not allow charts to load when enabling the 'Select first filter value by default' // to be saved when using dependent filters so, // you reload the window. - cy.get('.ant-modal-footer') - .contains('Save') - .should('be.visible') - .click(); + cy.get('.ant-modal-footer').contains('Save').should('be.visible').click(); cy.get('.ant-modal-content').should('not.exist'); cy.reload(); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx index bc7f53c254f6..fb7d9a08b00e 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx @@ -169,7 +169,8 @@ const FilterValue: FC = ({ allFilters[pId]?.defaultDataMask?.extraFormData?.time_range ) { selectedParentFilterValueCounts += - allFilters[pId]?.defaultDataMask?.extraFormData?.filters?.length ?? 1; + allFilters[pId]?.defaultDataMask?.extraFormData?.filters?.length ?? + 1; } }); diff --git a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx index 2c15f7975cd6..cec69d255079 100644 --- a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx @@ -378,7 +378,8 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { const hasDataChanged = prev?.length !== curr?.length || - JSON.stringify(prev?.map(row => row[col])) !== JSON.stringify(curr?.map(row => row[col])); + JSON.stringify(prev?.map(row => row[col])) !== + JSON.stringify(curr?.map(row => row[col])); // If data actually changed (e.g., due to parent filter), reset flag if (hasDataChanged) { @@ -392,7 +393,8 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { isChangedByUser.current && filterState.value && data.some(row => row[col] === filterState.value[0]) - ) return; + ) + return; const firstItem: SelectValue = data[0] ? (groupby.map(col => data[0][col]) as string[]) From 98a83980060665680e958843170885eb0f8e23b2 Mon Sep 17 00:00:00 2001 From: Muhammad Musfir Date: Mon, 14 Jul 2025 10:59:11 +0200 Subject: [PATCH 4/9] chore(fix): adds json stringify support for BigInt in SelectFilterPlugin --- .../src/filters/components/Select/SelectFilterPlugin.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx index cec69d255079..cda21eb145ef 100644 --- a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx @@ -376,10 +376,13 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { const prev = prevDataRef.current; const curr = data; + const stringifySafe = (val: unknown) => + typeof val === 'bigint' ? val.toString() : val; + const hasDataChanged = prev?.length !== curr?.length || - JSON.stringify(prev?.map(row => row[col])) !== - JSON.stringify(curr?.map(row => row[col])); + JSON.stringify(prev?.map(row => stringifySafe(row[col]))) !== + JSON.stringify(curr?.map(row => stringifySafe(row[col]))); // If data actually changed (e.g., due to parent filter), reset flag if (hasDataChanged) { From 54139f4117d7f4aa0ac6e3da02660039c7583a41 Mon Sep 17 00:00:00 2001 From: Muhammad Musfir Date: Wed, 16 Jul 2025 13:02:40 +0200 Subject: [PATCH 5/9] chore(fix): replaces allFilters with dataMaskSelected in FilterValue and updates test --- .../cypress/e2e/dashboard/nativeFilters.test.ts | 9 +++++---- .../FilterBar/FilterControls/FilterValue.tsx | 11 +++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/superset-frontend/cypress-base/cypress/e2e/dashboard/nativeFilters.test.ts b/superset-frontend/cypress-base/cypress/e2e/dashboard/nativeFilters.test.ts index ee2ae8a59036..8fe2da0b87c4 100644 --- a/superset-frontend/cypress-base/cypress/e2e/dashboard/nativeFilters.test.ts +++ b/superset-frontend/cypress-base/cypress/e2e/dashboard/nativeFilters.test.ts @@ -213,12 +213,13 @@ describe('Native filters', () => { // sometimes does not allow charts to load when enabling the 'Select first filter value by default' // to be saved when using dependent filters so, // you reload the window. - cy.get('.ant-modal-footer').contains('Save').should('be.visible').click(); + cy.get(nativeFilters.modal.footer) + .contains('Save') + .should('be.visible') + .click({ force: true }); - cy.get('.ant-modal-content').should('not.exist'); + cy.get(nativeFilters.modal.container).should('not.exist'); cy.reload(); - [SAMPLE_CHART].forEach(waitForChartLoad); - // saveNativeFilterSettings([SAMPLE_CHART]); applyNativeFilterValueWithIndex(0, 'North America'); diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx index fb7d9a08b00e..7a3c2e9231bd 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx @@ -108,7 +108,6 @@ const FilterValue: FC = ({ state => state.dashboardInfo.id, ); - const allFilters = useFilters(); const [error, setError] = useState(); const [formData, setFormData] = useState>({ inView: false, @@ -157,6 +156,7 @@ const FilterValue: FC = ({ dashboardId, }); const filterOwnState = filter.dataMask?.ownState || {}; + // if (Object.keys(dataMaskSelected)) if (filter?.cascadeParentIds?.length) { // Prevent unnecessary backend requests by validating parent filter selections first @@ -164,12 +164,11 @@ const FilterValue: FC = ({ filter?.cascadeParentIds?.forEach(pId => { if ( - allFilters[pId]?.controlValues?.defaultToFirstItem || - allFilters[pId]?.defaultDataMask?.extraFormData?.filters || - allFilters[pId]?.defaultDataMask?.extraFormData?.time_range + dataMaskSelected?.[pId]?.extraFormData?.filters || + dataMaskSelected?.[pId]?.extraFormData?.time_range ) { selectedParentFilterValueCounts += - allFilters[pId]?.defaultDataMask?.extraFormData?.filters?.length ?? + dataMaskSelected?.[pId]?.extraFormData?.filters?.length ?? 1; } }); @@ -260,7 +259,7 @@ const FilterValue: FC = ({ hasDataSource, isRefreshing, shouldRefresh, - allFilters, + dataMaskSelected ]); useEffect(() => { From a5a600bfe4850737cdaf93adc31a86ad61d75438 Mon Sep 17 00:00:00 2001 From: Muhammad Musfir Date: Wed, 16 Jul 2025 13:17:33 +0200 Subject: [PATCH 6/9] chore(fix): adds linting fixes for FilterValue --- .../nativeFilters/FilterBar/FilterControls/FilterValue.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx index 7a3c2e9231bd..e8fe140e99fd 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx @@ -58,7 +58,6 @@ import { FilterControlProps } from './types'; import { getFormData } from '../../utils'; import { useFilterDependencies } from './state'; import { useFilterOutlined } from '../useFilterOutlined'; -import { useFilters } from '../state'; const HEIGHT = 32; @@ -168,8 +167,7 @@ const FilterValue: FC = ({ dataMaskSelected?.[pId]?.extraFormData?.time_range ) { selectedParentFilterValueCounts += - dataMaskSelected?.[pId]?.extraFormData?.filters?.length ?? - 1; + dataMaskSelected?.[pId]?.extraFormData?.filters?.length ?? 1; } }); @@ -259,7 +257,7 @@ const FilterValue: FC = ({ hasDataSource, isRefreshing, shouldRefresh, - dataMaskSelected + dataMaskSelected, ]); useEffect(() => { From 85f749effac17cfd3a3193c70d51ca3157b0d3f5 Mon Sep 17 00:00:00 2001 From: Muhammad Musfir Date: Mon, 28 Jul 2025 12:43:11 +0200 Subject: [PATCH 7/9] chore(fix): adds feedback changes recommended by korbit-at --- .../FilterBar/FilterControls/FilterValue.tsx | 13 +++++-------- .../components/Select/SelectFilterPlugin.tsx | 19 ++++++++++--------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx index e8fe140e99fd..97a6327f751f 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx @@ -155,19 +155,17 @@ const FilterValue: FC = ({ dashboardId, }); const filterOwnState = filter.dataMask?.ownState || {}; - // if (Object.keys(dataMaskSelected)) if (filter?.cascadeParentIds?.length) { // Prevent unnecessary backend requests by validating parent filter selections first let selectedParentFilterValueCounts = 0; filter?.cascadeParentIds?.forEach(pId => { - if ( - dataMaskSelected?.[pId]?.extraFormData?.filters || - dataMaskSelected?.[pId]?.extraFormData?.time_range - ) { - selectedParentFilterValueCounts += - dataMaskSelected?.[pId]?.extraFormData?.filters?.length ?? 1; + const extraFormData = dataMaskSelected?.[pId]?.extraFormData; + if (extraFormData?.filters?.length) { + selectedParentFilterValueCounts += extraFormData.filters.length; + } else if (extraFormData?.time_range) { + selectedParentFilterValueCounts += 1; } }); @@ -178,7 +176,6 @@ const FilterValue: FC = ({ if (dependencies?.time_range) { depsCount += 1; } - if (selectedParentFilterValueCounts !== depsCount) { // child filter should not request backend until it // has all the required information from parent filters diff --git a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx index cda21eb145ef..763d67e59053 100644 --- a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx @@ -31,7 +31,7 @@ import { tn, styled, } from '@superset-ui/core'; -import { debounce, isUndefined } from 'lodash'; +import { debounce, first, isUndefined } from 'lodash'; import { useImmerReducer } from 'use-immer'; import { FormItem, @@ -376,13 +376,14 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { const prev = prevDataRef.current; const curr = data; - const stringifySafe = (val: unknown) => - typeof val === 'bigint' ? val.toString() : val; - - const hasDataChanged = - prev?.length !== curr?.length || - JSON.stringify(prev?.map(row => stringifySafe(row[col]))) !== - JSON.stringify(curr?.map(row => stringifySafe(row[col]))); + const hasDataChanged = prev?.length !== curr?.length || + prev?.some((row, i) => { + const prevVal = row[col]; + const currVal = curr[i][col]; + return typeof prevVal === 'bigint' || typeof currVal === 'bigint' + ? prevVal?.toString() !== currVal?.toString() + : prevVal !== currVal; + }); // If data actually changed (e.g., due to parent filter), reset flag if (hasDataChanged) { @@ -395,7 +396,7 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { if ( isChangedByUser.current && filterState.value && - data.some(row => row[col] === filterState.value[0]) + filterState.value.every((value?: any) => data.some(row => row[col] === value)) ) return; From 7ba4415e97822411cfbae01e73bdf259faa6a8f1 Mon Sep 17 00:00:00 2001 From: Muhammad Musfir Date: Mon, 28 Jul 2025 13:15:33 +0200 Subject: [PATCH 8/9] chore(fix): removes an unused first import --- .../src/filters/components/Select/SelectFilterPlugin.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx index 763d67e59053..6739a068c8dd 100644 --- a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx @@ -31,7 +31,7 @@ import { tn, styled, } from '@superset-ui/core'; -import { debounce, first, isUndefined } from 'lodash'; +import { debounce, isUndefined } from 'lodash'; import { useImmerReducer } from 'use-immer'; import { FormItem, From a3194635a03addc667c68a154687317776030cc6 Mon Sep 17 00:00:00 2001 From: Muhammad Musfir Date: Wed, 30 Jul 2025 10:12:17 +0200 Subject: [PATCH 9/9] chore(fix): adds eslint and prettier fixes --- .../src/filters/components/Select/SelectFilterPlugin.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx index 6739a068c8dd..b792360786b2 100644 --- a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx @@ -376,7 +376,8 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { const prev = prevDataRef.current; const curr = data; - const hasDataChanged = prev?.length !== curr?.length || + const hasDataChanged = + prev?.length !== curr?.length || prev?.some((row, i) => { const prevVal = row[col]; const currVal = curr[i][col]; @@ -396,7 +397,9 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { if ( isChangedByUser.current && filterState.value && - filterState.value.every((value?: any) => data.some(row => row[col] === value)) + filterState.value.every((value?: any) => + data.some(row => row[col] === value), + ) ) return;