Working on filters

This commit is contained in:
Beto Dealmeida
2026-04-17 16:36:33 -04:00
parent 5b4035427d
commit ab52bd14c4
3 changed files with 115 additions and 4 deletions

View File

@@ -60,6 +60,12 @@ function UIFilters(
filter.current?.clearFilter?.();
});
},
clearFilterById: (id: string) => {
const index = filters.findIndex(f => f.id === id);
if (index >= 0) {
filterRefs[index]?.current?.clearFilter?.();
}
},
}));
return (

View File

@@ -264,6 +264,11 @@ export interface ListViewProps<T extends object = any> {
columnsForWrapText?: string[];
enableBulkTag?: boolean;
bulkTagResourceName?: string;
/** Optional ref exposed to callers for programmatic filter control. */
filtersRef?: React.RefObject<{
clearFilters: () => void;
clearFilterById: (id: string) => void;
}>;
}
export function ListView<T extends object = any>({
@@ -290,6 +295,7 @@ export function ListView<T extends object = any>({
columnsForWrapText,
enableBulkTag = false,
bulkTagResourceName,
filtersRef,
addSuccessToast,
addDangerToast,
}: ListViewProps<T>) {
@@ -337,7 +343,17 @@ export function ListView<T extends object = any>({
});
}
const filterControlsRef = useRef<{ clearFilters: () => void }>(null);
const filterControlsRef = useRef<{
clearFilters: () => void;
clearFilterById: (id: string) => void;
}>(null);
// Wire the optional external filtersRef to our internal filterControlsRef.
useEffect(() => {
if (filtersRef) {
(filtersRef as any).current = filterControlsRef.current;
}
});
const handleClearFilterControls = useCallback(() => {
if (query.filters) {

View File

@@ -24,7 +24,7 @@ import {
FeatureFlag,
} from '@superset-ui/core';
import { styled, useTheme, css } from '@apache-superset/core/theme';
import { FunctionComponent, useState, useMemo, useCallback, Key } from 'react';
import { FunctionComponent, useState, useMemo, useCallback, useRef, Key } from 'react';
import type { CellProps } from 'react-table';
import { Link, useHistory } from 'react-router-dom';
import rison from 'rison';
@@ -194,7 +194,6 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
const [loading, setLoading] = useState(true);
const [lastFetchConfig, setLastFetchConfig] =
useState<ListViewFetchDataConfig | null>(null);
const currentSourceFilter = useMemo(() => {
const sourceTypeFilter = lastFetchConfig?.filters.find(
filter => filter.id === 'source_type',
@@ -209,6 +208,84 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
return (sourceTypeFilter?.value as string | undefined) ?? '';
}, [lastFetchConfig]);
// Track the current type and connection filter values so cascade-clear logic
// can inspect them when a different filter changes.
const currentTypeFilter = useRef<unknown>(undefined);
const currentConnectionFilter = useRef<unknown>(undefined);
// Ref wired to ListView's filter controls for programmatic per-filter clearing.
const filtersRef = useRef<{
clearFilters: () => void;
clearFilterById: (id: string) => void;
}>(null);
/**
* Cascade-clear incompatible filters when one filter changes.
*
* Rules:
* - Selecting a DB connection → clear "Semantic View" type
* - Selecting a SL connection → clear "Physical" / "Virtual" type
* - Selecting Physical/Virtual type → clear any SL connection
* - Selecting Semantic View type → clear any DB connection
* - Selecting Source=Database → clear SL connection + Semantic View type
* - Selecting Source=Semantic Layer → clear DB connection + Physical/Virtual type
*/
const cascadeClear = useCallback(
(
changed: 'source' | 'type' | 'connection',
newValue: unknown,
) => {
if (!isFeatureEnabled(FeatureFlag.SemanticLayers)) return;
const isSlConnection = (v: unknown) =>
typeof v === 'string' && v.startsWith('sl:');
const isDbConnection = (v: unknown) =>
v !== undefined && v !== null && v !== '' && !isSlConnection(v);
const isSemanticViewType = (v: unknown) => v === 'semantic_view';
const isPhysicalVirtualType = (v: unknown) =>
v === true || v === false;
if (changed === 'connection') {
if (isSlConnection(newValue) && isPhysicalVirtualType(currentTypeFilter.current)) {
filtersRef.current?.clearFilterById('sql');
}
if (isDbConnection(newValue) && isSemanticViewType(currentTypeFilter.current)) {
filtersRef.current?.clearFilterById('sql');
}
}
if (changed === 'type') {
if (isSemanticViewType(newValue) && isDbConnection(currentConnectionFilter.current)) {
filtersRef.current?.clearFilterById('database');
}
if (isPhysicalVirtualType(newValue) && isSlConnection(currentConnectionFilter.current)) {
filtersRef.current?.clearFilterById('database');
}
}
if (changed === 'source') {
const src = newValue as string;
if (src === 'database') {
if (isSemanticViewType(currentTypeFilter.current)) {
filtersRef.current?.clearFilterById('sql');
}
if (isSlConnection(currentConnectionFilter.current)) {
filtersRef.current?.clearFilterById('database');
}
}
if (src === 'semantic_layer') {
if (isPhysicalVirtualType(currentTypeFilter.current)) {
filtersRef.current?.clearFilterById('sql');
}
if (isDbConnection(currentConnectionFilter.current)) {
filtersRef.current?.clearFilterById('database');
}
}
}
},
[],
);
/**
* Fetches "Data connection" filter options — a combined list of databases
* and semantic layers.
@@ -333,7 +410,7 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
otherFilters.push({
col: 'database',
opr: databaseFilter.operator,
value: raw,
value: raw as string | number,
});
}
}
@@ -859,6 +936,9 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
{ label: t('Database'), value: 'database' },
{ label: t('Semantic Layer'), value: 'semantic_layer' },
],
onFilterUpdate: (option: any) => {
cascadeClear('source', option?.value);
},
},
]
: []),
@@ -889,6 +969,10 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
? [{ label: t('Semantic View'), value: 'semantic_view' }]
: []),
],
onFilterUpdate: (option: any) => {
currentTypeFilter.current = option?.value;
cascadeClear('type', option?.value);
},
},
]
: [
@@ -915,6 +999,10 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
fetchSelects: fetchConnectionOptions,
paginate: true,
dropdownStyle: { minWidth: WIDER_DROPDOWN_WIDTH },
onFilterUpdate: (option: any) => {
currentConnectionFilter.current = option?.value;
cascadeClear('connection', option?.value);
},
},
{
Header: t('Schema'),
@@ -1381,6 +1469,7 @@ const DatasetList: FunctionComponent<DatasetListProps> = ({
pageSize={PAGE_SIZE}
fetchData={fetchData}
filters={filterTypes}
filtersRef={filtersRef}
loading={loading}
initialSort={initialSort}
bulkActions={bulkActions}