You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by ru...@apache.org on 2026/09/16 23:45:57 UTC
(superset) branch fix/cascade-dependency-followup created (now 003ac4f568f)
This is an automated email from the ASF dual-hosted git repository.
rusackas pushed a change to branch fix/cascade-dependency-followup
in repository https://gitbox.apache.org/repos/asf/superset.git
at 003ac4f568f fix(native-filters): keep cascade dependency gate in sync with live filter type
This branch includes the following new commits:
new 003ac4f568f fix(native-filters): keep cascade dependency gate in sync with live filter type
The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
(superset) 01/01: fix(native-filters): keep cascade dependency gate in sync with live filter type
Posted by ru...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
rusackas pushed a commit to branch fix/cascade-dependency-followup
in repository https://gitbox.apache.org/repos/asf/superset.git
commit 003ac4f568f9de88ef98c4312a29a921bcc4d4be
Author: Superset Dev <de...@superset.apache.org>
AuthorDate: Wed Sep 16 16:45:16 2026 -0700
fix(native-filters): keep cascade dependency gate in sync with live filter type
Follow-up to #43223. Two bot review comments on that PR were never
addressed before merge:
- FiltersConfigForm.tsx: canDependOnOtherFilters read
formFilter?.filterType directly, unlike itemTypeField's own fallback
chain (formFilter?.filterType || filterToEdit?.filterType ||
'filter_select') used everywhere else in this component.
formFilter?.filterType can be undefined on the first render before
the antd Form hydrates, which hid the "Values are dependent on other
filters" section even for filter types that do support cascading.
- useFilterOperations.ts: buildDependencyMap read each filter's
dependencies array as-is, without re-checking that a listed parent
still supports cascading. If a parent's type changes to one that no
longer supports dependencies within the same open-modal editing
session, the stale relationship lingered in the live dependency map
and preview until save. Now filters each parent id through the
existing canBeUsedAsDependency check on every rebuild.
Added regression tests for the buildDependencyMap fix (confirmed they
fail against the pre-fix code). Did not add a FiltersConfigForm.tsx-level
test for the itemTypeField fix - no existing test harness covers this
component at that level (matches #43223's own testing approach, which
relied on manual QA there and unit tests only for the hooks).
Co-Authored-By: Evan Rusackas <ev...@preset.io>
Co-Authored-By: Claude Sonnet 5 <no...@anthropic.com>
---
.../FiltersConfigForm/FiltersConfigForm.tsx | 8 ++--
.../hooks/useFilterOperations.test.ts | 51 +++++++++++++++++++++-
.../hooks/useFilterOperations.ts | 10 ++++-
3 files changed, 63 insertions(+), 6 deletions(-)
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
index 81337c9b013..ad8a05e0c4b 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/FiltersConfigForm/FiltersConfigForm.tsx
@@ -471,9 +471,11 @@ const FiltersConfigForm = (
formFilter?.filterType,
);
- const canDependOnOtherFilters = filterSupportsDependencies(
- formFilter?.filterType,
- );
+ // Use itemTypeField, not formFilter?.filterType directly: the latter can
+ // be undefined on the first render before the antd Form hydrates (see
+ // itemTypeField's own fallback chain above), which would otherwise hide
+ // this section for a filter type that does support cascade dependencies.
+ const canDependOnOtherFilters = filterSupportsDependencies(itemTypeField);
const isDataDirty = formFilter?.isDataDirty ?? true;
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/hooks/useFilterOperations.test.ts b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/hooks/useFilterOperations.test.ts
index 42311ccf3c7..ed4b8a5cc56 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/hooks/useFilterOperations.test.ts
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/hooks/useFilterOperations.test.ts
@@ -16,8 +16,13 @@
* specific language governing permissions and limitations
* under the License.
*/
+import { renderHook } from '@testing-library/react';
import { Behavior } from '@superset-ui/core';
-import { filterSupportsDependencies } from './useFilterOperations';
+import {
+ filterSupportsDependencies,
+ useFilterOperations,
+ FilterOperationsParams,
+} from './useFilterOperations';
const mockItems: Record<string, { value: Record<string, unknown> }> = {
filter_select: {
@@ -89,3 +94,47 @@ test('filterSupportsDependencies returns false for an unknown or missing filterT
expect(filterSupportsDependencies('unknown_type')).toBe(false);
expect(filterSupportsDependencies(undefined)).toBe(false);
});
+
+function renderFilterOperations(
+ filters: Record<string, { filterType: string; dependencies?: string[] }>,
+) {
+ const params: FilterOperationsParams = {
+ form: {
+ getFieldValue: () => filters,
+ } as unknown as FilterOperationsParams['form'],
+ filterState: {
+ removedItems: {},
+ } as unknown as FilterOperationsParams['filterState'],
+ filterIds: Object.keys(filters),
+ filterConfigMap: {},
+ handleModifyItem: jest.fn(),
+ setActiveItem: jest.fn(),
+ setSaveAlertVisible: jest.fn(),
+ };
+ return renderHook(() => useFilterOperations(params)).result;
+}
+
+test('buildDependencyMap drops a parent id whose filter type no longer supports dependencies', () => {
+ // "parent" was a Select filter when "child" was configured to depend on
+ // it, then the user changed "parent" to Time grain within the same
+ // editing session (before saving) - the stale edge should not linger.
+ const result = renderFilterOperations({
+ parent: { filterType: 'filter_timegrain' },
+ child: { filterType: 'filter_select', dependencies: ['parent'] },
+ });
+
+ const dependencyMap = result.current.buildDependencyMap();
+
+ expect(dependencyMap.get('child')).toEqual([]);
+});
+
+test('buildDependencyMap keeps a parent id whose filter type still supports dependencies', () => {
+ const result = renderFilterOperations({
+ parent: { filterType: 'filter_select' },
+ child: { filterType: 'filter_select', dependencies: ['parent'] },
+ });
+
+ const dependencyMap = result.current.buildDependencyMap();
+
+ expect(dependencyMap.get('child')).toEqual(['parent']);
+});
diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/hooks/useFilterOperations.ts b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/hooks/useFilterOperations.ts
index f889b86ab75..940c22e6848 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/hooks/useFilterOperations.ts
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FiltersConfigModal/hooks/useFilterOperations.ts
@@ -189,11 +189,17 @@ export function useFilterOperations({
} else if (configItem?.cascadeParentIds) {
array = [...configItem.cascadeParentIds];
}
- dependencyMap.set(key, array);
+ // Drop parent ids that no longer qualify (removed, or its filter
+ // type changed to one that doesn't support cascade dependencies)
+ // as soon as the map is rebuilt, instead of only at save time.
+ dependencyMap.set(
+ key,
+ array.filter(parentId => canBeUsedAsDependency(parentId)),
+ );
});
}
return dependencyMap;
- }, [filterConfigMap, form]);
+ }, [canBeUsedAsDependency, filterConfigMap, form]);
const getAvailableFilters = useCallback(
(filterId: string, getItemTitle: (id: string) => string) => {