Skip to content

Commit

Permalink
fix(dashboard): Add correct icon, label and badge to horizontal nativ…
Browse files Browse the repository at this point in the history
…e filters dropdown button (#22211)

Co-authored-by: Kamil Gabryjelski <kamil.gabryjelski@gmail.com>
  • Loading branch information
codyml and kgabryje authored Nov 28, 2022
1 parent 93158ea commit 435926b
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ export type Filter = {
description: string;
};

export type FilterWithDataMask = Filter & { dataMask: DataMaskWithId };

export type Divider = Partial<Omit<Filter, 'id' | 'type'>> & {
id: string;
title: string;
Expand Down
67 changes: 41 additions & 26 deletions superset-frontend/src/components/DropdownContainer/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ const DropdownContainer = forwardRef(
{
items,
onOverflowingStateChange,
dropdownContent: popoverContent,
dropdownContent: getPopoverContent,
dropdownRef: popoverRef,
dropdownStyle: popoverStyle = {},
dropdownTriggerCount: popoverTriggerCount,
Expand Down Expand Up @@ -209,26 +209,31 @@ const DropdownContainer = forwardRef(
}
}, [notOverflowedIds, onOverflowingStateChange, overflowedIds]);

const content = useMemo(
() => (
<div
css={css`
display: flex;
flex-direction: column;
gap: ${theme.gridUnit * 4}px;
`}
data-test="dropdown-content"
style={popoverStyle}
ref={popoverRef}
>
{popoverContent
? popoverContent(overflowedItems)
: overflowedItems.map(item => item.element)}
</div>
),
const overflowingCount =
overflowingIndex !== -1 ? items.length - overflowingIndex : 0;

const popoverContent = useMemo(
() =>
getPopoverContent || overflowingCount ? (
<div
css={css`
display: flex;
flex-direction: column;
gap: ${theme.gridUnit * 4}px;
`}
data-test="dropdown-content"
style={popoverStyle}
ref={popoverRef}
>
{getPopoverContent
? getPopoverContent(overflowedItems)
: overflowedItems.map(item => item.element)}
</div>
) : null,
[
getPopoverContent,
overflowedItems,
popoverContent,
overflowingCount,
popoverRef,
popoverStyle,
theme.gridUnit,
Expand All @@ -244,9 +249,6 @@ const DropdownContainer = forwardRef(
[ref],
);

const overflowingCount =
overflowingIndex !== -1 ? items.length - overflowingIndex : 0;

return (
<div
ref={ref}
Expand All @@ -268,20 +270,33 @@ const DropdownContainer = forwardRef(
>
{notOverflowedItems.map(item => item.element)}
</div>
{overflowingCount > 0 && (
{popoverContent && (
<Popover
content={content}
content={popoverContent}
trigger="click"
visible={popoverVisible}
onVisibleChange={visible => setPopoverVisible(visible)}
placement="bottom"
>
<Button buttonStyle="secondary">
{popoverTriggerIcon}
{popoverTriggerText}
<Badge count={popoverTriggerCount || overflowingCount} />
<Badge
count={popoverTriggerCount ?? overflowingCount}
css={css`
margin-left: ${popoverTriggerCount ?? overflowingCount
? '8px'
: '0'};
`}
/>
<Icons.DownOutlined
iconSize="m"
iconColor={theme.colors.grayscale.base}
iconColor={theme.colors.grayscale.light1}
css={css`
.anticon {
display: flex;
}
`}
/>
</Button>
</Popover>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import {
Divider,
css,
SupersetTheme,
t,
isNativeFilter,
} from '@superset-ui/core';
import {
createHtmlPortalNode,
Expand All @@ -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';
Expand All @@ -56,7 +59,7 @@ const FilterControls: FC<FilterControlsProps> = ({
state => state.dashboardInfo.filterBarOrientation,
);

const [overflowIndex, setOverflowIndex] = useState(0);
const [overflowedIds, setOverflowedIds] = useState<string[]>([]);

const { filterControlFactory, filtersWithValues } = useFilterControlFactory(
dataMaskSelected,
Expand Down Expand Up @@ -100,60 +103,111 @@ const FilterControls: FC<FilterControlsProps> = ({
</>
);

const renderHorizontalContent = () => {
const items = filtersInScope.map(filter => ({
id: filter.id,
element: (
<div
css={css`
flex-shrink: 0;
`}
>
{renderer(filter)}
</div>
),
}));
return (
<div
css={(theme: SupersetTheme) =>
css`
padding-left: ${theme.gridUnit * 4}px;
min-width: 0;
`
const items = useMemo(
() =>
filtersInScope.map(filter => ({
id: filter.id,
element: (
<div
css={css`
flex-shrink: 0;
`}
>
{renderer(filter)}
</div>
),
})),
[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 = () => (
<div
css={(theme: SupersetTheme) =>
css`
padding-left: ${theme.gridUnit * 4}px;
min-width: 0;
`
}
>
<DropdownContainer
items={items}
dropdownTriggerIcon={
<Icons.FilterSmall
css={css`
&& {
margin-right: -4px;
display: flex;
}
`}
/>
}
>
<DropdownContainer
items={items}
dropdownContent={overflowedItems => {
const overflowedItemIds = new Set(
overflowedItems.map(({ id }) => id),
);
return (
<FiltersDropdownContent
filtersInScope={filtersInScope.filter(({ id }) =>
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)
? () => (
<FiltersDropdownContent
filtersInScope={overflowedFiltersInScope}
filtersOutOfScope={filtersOutOfScope}
renderer={renderer}
showCollapsePanel={showCollapsePanel}
/>
)
: undefined
}
onOverflowingStateChange={({ overflowed: nextOverflowedIds }) => {
if (
nextOverflowedIds.length !== overflowedIds.length ||
overflowedIds.reduce(
(a, b, i) => a || b !== nextOverflowedIds[i],
false,
)
) {
setOverflowedIds(nextOverflowedIds);
}
/>
</div>
}}
/>
</div>
);

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 (
<>
{portalNodes
.filter((node, index) => filterIds.has(filtersWithValues[index].id))
.map((node, index) => (
<InPortal node={node}>
{filterControlFactory(index, filterBarOrientation, overflowIndex)}
{filterControlFactory(
index,
filterBarOrientation,
overflowedByIndex[index],
)}
</InPortal>
))}
{filterBarOrientation === FilterBarOrientation.VERTICAL &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
DataMaskStateWithId,
Divider,
Filter,
FilterWithDataMask,
isFilterDivider,
} from '@superset-ui/core';
import { FilterBarOrientation } from 'src/dashboard/types';
Expand All @@ -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,
Expand All @@ -50,7 +51,7 @@ export const useFilterControlFactory = (
(
index: number,
filterBarOrientation: FilterBarOrientation,
overflowIndex: number,
overflow: boolean,
) => {
const filter = filtersWithValues[index];
if (isFilterDivider(filter)) {
Expand All @@ -59,7 +60,7 @@ export const useFilterControlFactory = (
title={filter.title}
description={filter.description}
orientation={filterBarOrientation}
overflow={index >= overflowIndex}
overflow={overflow}
/>
);
}
Expand All @@ -71,7 +72,7 @@ export const useFilterControlFactory = (
onFilterSelectionChange={onFilterSelectionChange}
inView={false}
orientation={filterBarOrientation}
overflow={index >= overflowIndex}
overflow={overflow}
/>
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 435926b

Please sign in to comment.