diff --git a/superset-frontend/packages/superset-ui-core/src/query/types/Dashboard.ts b/superset-frontend/packages/superset-ui-core/src/query/types/Dashboard.ts index c86c1cfbe4..90c1e5856e 100644 --- a/superset-frontend/packages/superset-ui-core/src/query/types/Dashboard.ts +++ b/superset-frontend/packages/superset-ui-core/src/query/types/Dashboard.ts @@ -91,6 +91,8 @@ export type Filter = { description: string; }; +export type FilterWithDataMask = Filter & { dataMask: DataMaskWithId }; + export type Divider = Partial> & { id: string; title: string; diff --git a/superset-frontend/src/components/DropdownContainer/index.tsx b/superset-frontend/src/components/DropdownContainer/index.tsx index 9af3a96534..d364d3542e 100644 --- a/superset-frontend/src/components/DropdownContainer/index.tsx +++ b/superset-frontend/src/components/DropdownContainer/index.tsx @@ -104,7 +104,7 @@ const DropdownContainer = forwardRef( { items, onOverflowingStateChange, - dropdownContent: popoverContent, + dropdownContent: getPopoverContent, dropdownRef: popoverRef, dropdownStyle: popoverStyle = {}, dropdownTriggerCount: popoverTriggerCount, @@ -209,26 +209,31 @@ const DropdownContainer = forwardRef( } }, [notOverflowedIds, onOverflowingStateChange, overflowedIds]); - const content = useMemo( - () => ( -
- {popoverContent - ? popoverContent(overflowedItems) - : overflowedItems.map(item => item.element)} -
- ), + const overflowingCount = + overflowingIndex !== -1 ? items.length - overflowingIndex : 0; + + const popoverContent = useMemo( + () => + getPopoverContent || overflowingCount ? ( +
+ {getPopoverContent + ? getPopoverContent(overflowedItems) + : overflowedItems.map(item => item.element)} +
+ ) : null, [ + getPopoverContent, overflowedItems, - popoverContent, + overflowingCount, popoverRef, popoverStyle, theme.gridUnit, @@ -244,9 +249,6 @@ const DropdownContainer = forwardRef( [ref], ); - const overflowingCount = - overflowingIndex !== -1 ? items.length - overflowingIndex : 0; - return (
{notOverflowedItems.map(item => item.element)}
- {overflowingCount > 0 && ( + {popoverContent && ( setPopoverVisible(visible)} + placement="bottom" > diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx index 9b0347ebf0..5c0ce9f902 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx @@ -24,6 +24,8 @@ import { Divider, css, SupersetTheme, + t, + isNativeFilter, } from '@superset-ui/core'; import { createHtmlPortalNode, @@ -37,6 +39,7 @@ import { } from 'src/dashboard/components/nativeFilters/state'; import { FilterBarOrientation, RootState } from 'src/dashboard/types'; import DropdownContainer from 'src/components/DropdownContainer'; +import Icons from 'src/components/Icons'; import { FiltersOutOfScopeCollapsible } from '../FiltersOutOfScopeCollapsible'; import { useFilterControlFactory } from '../useFilterControlFactory'; import { FiltersDropdownContent } from '../FiltersDropdownContent'; @@ -56,7 +59,7 @@ const FilterControls: FC = ({ state => state.dashboardInfo.filterBarOrientation, ); - const [overflowIndex, setOverflowIndex] = useState(0); + const [overflowedIds, setOverflowedIds] = useState([]); const { filterControlFactory, filtersWithValues } = useFilterControlFactory( dataMaskSelected, @@ -100,52 +103,99 @@ const FilterControls: FC = ({ ); - const renderHorizontalContent = () => { - const items = filtersInScope.map(filter => ({ - id: filter.id, - element: ( -
- {renderer(filter)} -
- ), - })); - return ( -
- css` - padding-left: ${theme.gridUnit * 4}px; - min-width: 0; - ` + const items = useMemo( + () => + filtersInScope.map(filter => ({ + id: filter.id, + element: ( +
+ {renderer(filter)} +
+ ), + })), + [filtersInScope, renderer], + ); + + const overflowedFiltersInScope = useMemo( + () => filtersInScope.filter(({ id }) => overflowedIds?.includes(id)), + [filtersInScope, overflowedIds], + ); + + const activeOverflowedFiltersInScope = useMemo( + () => + overflowedFiltersInScope.filter( + filter => isNativeFilter(filter) && filter.dataMask.filterState?.value, + ).length, + [overflowedFiltersInScope], + ); + + const renderHorizontalContent = () => ( +
+ css` + padding-left: ${theme.gridUnit * 4}px; + min-width: 0; + ` + } + > + } - > - { - const overflowedItemIds = new Set( - overflowedItems.map(({ id }) => id), - ); - return ( - - overflowedItemIds.has(id), - )} - filtersOutOfScope={filtersOutOfScope} - renderer={renderer} - showCollapsePanel={showCollapsePanel} - /> - ); - }} - onOverflowingStateChange={overflowingState => - setOverflowIndex(overflowingState.notOverflowed.length) + dropdownTriggerText={t('More filters')} + dropdownTriggerCount={activeOverflowedFiltersInScope} + dropdownContent={ + overflowedFiltersInScope.length || + (filtersOutOfScope.length && showCollapsePanel) + ? () => ( + + ) + : undefined + } + onOverflowingStateChange={({ overflowed: nextOverflowedIds }) => { + if ( + nextOverflowedIds.length !== overflowedIds.length || + overflowedIds.reduce( + (a, b, i) => a || b !== nextOverflowedIds[i], + false, + ) + ) { + setOverflowedIds(nextOverflowedIds); } - /> -
+ }} + /> +
+ ); + + const overflowedByIndex = useMemo(() => { + const filtersOutOfScopeIds = new Set(filtersOutOfScope.map(({ id }) => id)); + const overflowedFiltersInScopeIds = new Set( + overflowedFiltersInScope.map(({ id }) => id), ); - }; + + return filtersWithValues.map( + filter => + filtersOutOfScopeIds.has(filter.id) || + overflowedFiltersInScopeIds.has(filter.id), + ); + }, [filtersOutOfScope, filtersWithValues, overflowedFiltersInScope]); return ( <> @@ -153,7 +203,11 @@ const FilterControls: FC = ({ .filter((node, index) => filterIds.has(filtersWithValues[index].id)) .map((node, index) => ( - {filterControlFactory(index, filterBarOrientation, overflowIndex)} + {filterControlFactory( + index, + filterBarOrientation, + overflowedByIndex[index], + )} ))} {filterBarOrientation === FilterBarOrientation.VERTICAL && diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Horizontal.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Horizontal.tsx index ac03b0e0ff..d085e08900 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Horizontal.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Horizontal.tsx @@ -74,9 +74,6 @@ const FiltersLinkContainer = styled.div<{ hasFilters: boolean }>` button { display: flex; align-items: center; - text-transform: capitalize; - font-weight: ${theme.typography.weights.normal}; - color: ${theme.colors.primary.base}; > .anticon { height: 24px; padding-right: ${theme.gridUnit}px; diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/useFilterControlFactory.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/useFilterControlFactory.tsx index fe855ec3e3..6893e629cb 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/useFilterControlFactory.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/useFilterControlFactory.tsx @@ -23,6 +23,7 @@ import { DataMaskStateWithId, Divider, Filter, + FilterWithDataMask, isFilterDivider, } from '@superset-ui/core'; import { FilterBarOrientation } from 'src/dashboard/types'; @@ -37,7 +38,7 @@ export const useFilterControlFactory = ( ) => { const filters = useFilters(); const filterValues = useMemo(() => Object.values(filters), [filters]); - const filtersWithValues: (Filter | Divider)[] = useMemo( + const filtersWithValues: (FilterWithDataMask | Divider)[] = useMemo( () => filterValues.map(filter => ({ ...filter, @@ -50,7 +51,7 @@ export const useFilterControlFactory = ( ( index: number, filterBarOrientation: FilterBarOrientation, - overflowIndex: number, + overflow: boolean, ) => { const filter = filtersWithValues[index]; if (isFilterDivider(filter)) { @@ -59,7 +60,7 @@ export const useFilterControlFactory = ( title={filter.title} description={filter.description} orientation={filterBarOrientation} - overflow={index >= overflowIndex} + overflow={overflow} /> ); } @@ -71,7 +72,7 @@ export const useFilterControlFactory = ( onFilterSelectionChange={onFilterSelectionChange} inView={false} orientation={filterBarOrientation} - overflow={index >= overflowIndex} + overflow={overflow} /> ); }, diff --git a/superset-frontend/src/dashboard/components/nativeFilters/state.ts b/superset-frontend/src/dashboard/components/nativeFilters/state.ts index 030b65859b..51d987a577 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/state.ts +++ b/superset-frontend/src/dashboard/components/nativeFilters/state.ts @@ -23,6 +23,7 @@ import { FilterConfiguration, Divider, isFilterDivider, + FilterWithDataMask, } from '@superset-ui/core'; import { ActiveTabs, DashboardLayout, RootState } from '../../types'; import { TAB_TYPE } from '../../util/componentTypes'; @@ -109,13 +110,15 @@ function useIsFilterInScope() { })); } -export function useSelectFiltersInScope(filters: (Filter | Divider)[]) { +export function useSelectFiltersInScope( + filters: (FilterWithDataMask | Divider)[], +) { const dashboardHasTabs = useDashboardHasTabs(); const isFilterInScope = useIsFilterInScope(); return useMemo(() => { - let filtersInScope: (Filter | Divider)[] = []; - const filtersOutOfScope: (Filter | Divider)[] = []; + let filtersInScope: (FilterWithDataMask | Divider)[] = []; + const filtersOutOfScope: (FilterWithDataMask | Divider)[] = []; // we check native filters scopes only on dashboards with tabs if (!dashboardHasTabs) {