This is an automated email from the ASF dual-hosted git repository. diegopucci pushed a commit to branch feat/hfb-open-more-filters in repository https://gitbox.apache.org/repos/asf/superset.git
commit c1b9d8844d3f11c4510fcc3534763248f397ffb1 Author: geido <[email protected]> AuthorDate: Wed Nov 30 17:11:00 2022 +0200 Open dropdown programmatically --- .../superset-ui-core/src/query/types/Dashboard.ts | 1 + .../src/dashboard/actions/nativeFilters.ts | 24 +++++++++++++ .../DashboardBuilder/DashboardBuilder.tsx | 11 +++--- .../dashboard/components/FiltersBadge/index.tsx | 4 +-- .../dashboard/components/gridComponents/Tabs.jsx | 7 ++-- .../FilterBar/FilterControls/FilterControl.tsx | 4 +-- .../FilterBar/FilterControls/FilterControls.tsx | 27 ++++++++++++--- .../FilterBar/FilterControls/FilterValue.tsx | 32 ++++++++++++++---- .../FilterBar/FilterControls/types.ts | 2 +- .../FilterBar/FilterControls/utils.ts | 13 ++++++++ .../nativeFilters/FilterBar/Horizontal.tsx | 4 +-- .../nativeFilters/FilterBar/Vertical.tsx | 6 ++-- .../components/nativeFilters/FilterBar/index.tsx | 6 ++-- .../components/nativeFilters/FilterBar/types.ts | 4 +-- .../FilterBar/useFilterControlFactory.tsx | 6 ++-- .../src/dashboard/reducers/nativeFilters.ts | 14 ++++++++ .../util/useFilterFocusHighlightStyles.test.tsx | 39 ++++++++++++++++++++++ .../util/useFilterFocusHighlightStyles.ts | 9 ++--- .../components/GroupBy/GroupByFilterPlugin.tsx | 4 +++ .../filters/components/GroupBy/transformProps.ts | 4 +++ .../filters/components/Range/RangeFilterPlugin.tsx | 6 ++-- .../src/filters/components/Range/transformProps.ts | 4 +++ .../components/Select/SelectFilterPlugin.tsx | 7 ++-- .../filters/components/Select/transformProps.ts | 4 +++ .../filters/components/Time/TimeFilterPlugin.tsx | 6 ++-- .../src/filters/components/Time/transformProps.ts | 4 +++ .../TimeColumn/TimeColumnFilterPlugin.tsx | 8 +++-- .../components/TimeColumn/transformProps.ts | 4 +++ .../components/TimeGrain/TimeGrainFilterPlugin.tsx | 8 +++-- .../filters/components/TimeGrain/transformProps.ts | 4 +++ superset-frontend/src/filters/components/types.ts | 2 ++ 31 files changed, 226 insertions(+), 52 deletions(-) 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 90c1e5856e..9f46ecfa47 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 @@ -126,6 +126,7 @@ export type NativeFiltersState = { filters: Filters; filterSets: FilterSets; focusedFilterId?: string; + hoveredFilterId?: string; }; export type DashboardComponentMetadata = { diff --git a/superset-frontend/src/dashboard/actions/nativeFilters.ts b/superset-frontend/src/dashboard/actions/nativeFilters.ts index 71cc01d996..76ac6cc1fb 100644 --- a/superset-frontend/src/dashboard/actions/nativeFilters.ts +++ b/superset-frontend/src/dashboard/actions/nativeFilters.ts @@ -372,6 +372,28 @@ export function unsetFocusedNativeFilter(): UnsetFocusedNativeFilter { }; } +export const SET_HOVERED_NATIVE_FILTER = 'SET_HOVERED_NATIVE_FILTER'; +export interface SetHoveredNativeFilter { + type: typeof SET_HOVERED_NATIVE_FILTER; + id: string; +} +export const UNSET_HOVERED_NATIVE_FILTER = 'UNSET_HOVERED_NATIVE_FILTER'; +export interface UnsetHoveredNativeFilter { + type: typeof UNSET_HOVERED_NATIVE_FILTER; +} + +export function setHoveredNativeFilter(id: string): SetHoveredNativeFilter { + return { + type: SET_HOVERED_NATIVE_FILTER, + id, + }; +} +export function unsetHoveredNativeFilter(): UnsetHoveredNativeFilter { + return { + type: UNSET_HOVERED_NATIVE_FILTER, + }; +} + export type AnyFilterAction = | SetFilterConfigBegin | SetFilterConfigComplete @@ -383,6 +405,8 @@ export type AnyFilterAction = | SetBootstrapData | SetFocusedNativeFilter | UnsetFocusedNativeFilter + | SetHoveredNativeFilter + | UnsetHoveredNativeFilter | CreateFilterSetBegin | CreateFilterSetComplete | CreateFilterSetFail diff --git a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx index 6a3c516afd..079fce46a7 100644 --- a/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx +++ b/superset-frontend/src/dashboard/components/DashboardBuilder/DashboardBuilder.tsx @@ -239,9 +239,8 @@ const DashboardBuilder: FC<DashboardBuilderProps> = () => { const canEdit = useSelector<RootState, boolean>( ({ dashboardInfo }) => dashboardInfo.dash_edit_perm, ); - const directPathToChild = useSelector<RootState, string[]>( - state => state.dashboardState.directPathToChild, - ); + const nativeFilters = useSelector((state: RootState) => state.nativeFilters); + const focusedFilterId = nativeFilters?.focusedFilterId; const fullSizeChartId = useSelector<RootState, number | null>( state => state.dashboardState.fullSizeChartId, ); @@ -366,7 +365,7 @@ const DashboardBuilder: FC<DashboardBuilderProps> = () => { {showFilterBar && filterBarOrientation === FilterBarOrientation.HORIZONTAL && ( <FilterBar - directPathToChild={directPathToChild} + focusedFilterId={focusedFilterId} orientation={FilterBarOrientation.HORIZONTAL} /> )} @@ -398,7 +397,7 @@ const DashboardBuilder: FC<DashboardBuilderProps> = () => { </div> ), [ - directPathToChild, + focusedFilterId, nativeFiltersEnabled, filterBarOrientation, editMode, @@ -434,7 +433,7 @@ const DashboardBuilder: FC<DashboardBuilderProps> = () => { <StickyPanel ref={containerRef} width={filterBarWidth}> <ErrorBoundary> <FilterBar - directPathToChild={directPathToChild} + focusedFilterId={focusedFilterId} orientation={FilterBarOrientation.VERTICAL} verticalConfig={{ filtersOpen: dashboardFiltersOpen, diff --git a/superset-frontend/src/dashboard/components/FiltersBadge/index.tsx b/superset-frontend/src/dashboard/components/FiltersBadge/index.tsx index 3165348237..fd21d07fc2 100644 --- a/superset-frontend/src/dashboard/components/FiltersBadge/index.tsx +++ b/superset-frontend/src/dashboard/components/FiltersBadge/index.tsx @@ -23,6 +23,7 @@ import cx from 'classnames'; import { DataMaskStateWithId, Filters } from '@superset-ui/core'; import Icons from 'src/components/Icons'; import { usePrevious } from 'src/hooks/usePrevious'; +import { setFocusedNativeFilter } from 'src/dashboard/actions/nativeFilters'; import DetailsPanelPopover from './DetailsPanel'; import { Pill } from './Styles'; import { @@ -31,7 +32,6 @@ import { selectIndicatorsForChart, selectNativeIndicatorsForChart, } from './selectors'; -import { setDirectPathToChild } from '../../actions/dashboardState'; import { ChartsState, DashboardInfo, @@ -87,7 +87,7 @@ export const FiltersBadge = ({ chartId }: FiltersBadgeProps) => { const onHighlightFilterSource = useCallback( (path: string[]) => { - dispatch(setDirectPathToChild(path)); + dispatch(setFocusedNativeFilter(path[0])); }, [dispatch], ); diff --git a/superset-frontend/src/dashboard/components/gridComponents/Tabs.jsx b/superset-frontend/src/dashboard/components/gridComponents/Tabs.jsx index 5f4e6f5150..3b0732ac23 100644 --- a/superset-frontend/src/dashboard/components/gridComponents/Tabs.jsx +++ b/superset-frontend/src/dashboard/components/gridComponents/Tabs.jsx @@ -340,9 +340,10 @@ export class Tabs extends React.PureComponent { const { tabIndex: selectedTabIndex, activeKey } = this.state; let tabsToHighlight; - if (nativeFilters?.focusedFilterId) { - tabsToHighlight = - nativeFilters.filters[nativeFilters.focusedFilterId].tabsInScope; + const highlighterFilterId = + nativeFilters?.focusedFilterId || nativeFilters?.hoveredFilterId; + if (highlighterFilterId) { + tabsToHighlight = nativeFilters.filters[highlighterFilterId]?.tabsInScope; } return ( <DragDroppable diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControl.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControl.tsx index 29cdf9d460..41c74cc950 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControl.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControl.tsx @@ -214,7 +214,7 @@ const FilterControl = ({ filter, icon, onFilterSelectionChange, - directPathToChild, + focusedFilterId, inView, showOverflow, parentRef, @@ -295,7 +295,7 @@ const FilterControl = ({ dataMaskSelected={dataMaskSelected} filter={filter} showOverflow={showOverflow} - directPathToChild={directPathToChild} + focusedFilterId={focusedFilterId} onFilterSelectionChange={onFilterSelectionChange} inView={inView} parentRef={parentRef} 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 5c0ce9f902..435e179104 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterControls.tsx @@ -16,7 +16,14 @@ * specific language governing permissions and limitations * under the License. */ -import React, { FC, useCallback, useMemo, useState } from 'react'; +import React, { + FC, + useEffect, + useCallback, + useMemo, + useRef, + useState, +} from 'react'; import { DataMask, DataMaskStateWithId, @@ -38,20 +45,22 @@ import { useSelectFiltersInScope, } from 'src/dashboard/components/nativeFilters/state'; import { FilterBarOrientation, RootState } from 'src/dashboard/types'; -import DropdownContainer from 'src/components/DropdownContainer'; +import DropdownContainer, { + Ref as DropdownContainerRef, +} from 'src/components/DropdownContainer'; import Icons from 'src/components/Icons'; import { FiltersOutOfScopeCollapsible } from '../FiltersOutOfScopeCollapsible'; import { useFilterControlFactory } from '../useFilterControlFactory'; import { FiltersDropdownContent } from '../FiltersDropdownContent'; type FilterControlsProps = { - directPathToChild?: string[]; + focusedFilterId?: string; dataMaskSelected: DataMaskStateWithId; onFilterSelectionChange: (filter: Filter, dataMask: DataMask) => void; }; const FilterControls: FC<FilterControlsProps> = ({ - directPathToChild, + focusedFilterId, dataMaskSelected, onFilterSelectionChange, }) => { @@ -60,10 +69,11 @@ const FilterControls: FC<FilterControlsProps> = ({ ); const [overflowedIds, setOverflowedIds] = useState<string[]>([]); + const popoverRef = useRef<DropdownContainerRef>(null); const { filterControlFactory, filtersWithValues } = useFilterControlFactory( dataMaskSelected, - directPathToChild, + focusedFilterId, onFilterSelectionChange, ); const portalNodes = useMemo(() => { @@ -169,6 +179,7 @@ const FilterControls: FC<FilterControlsProps> = ({ ) : undefined } + ref={popoverRef} onOverflowingStateChange={({ overflowed: nextOverflowedIds }) => { if ( nextOverflowedIds.length !== overflowedIds.length || @@ -197,6 +208,12 @@ const FilterControls: FC<FilterControlsProps> = ({ ); }, [filtersOutOfScope, filtersWithValues, overflowedFiltersInScope]); + useEffect(() => { + if (focusedFilterId && overflowedIds.includes(focusedFilterId)) { + popoverRef?.current?.open(); + } + }, [focusedFilterId, popoverRef, overflowedIds]); + return ( <> {portalNodes 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 a08200d83b..0c9641f516 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx @@ -44,7 +44,7 @@ import { waitForAsyncData } from 'src/middleware/asyncEvent'; import { ClientErrorObject } from 'src/utils/getClientErrorObject'; import { FilterBarOrientation, RootState } from 'src/dashboard/types'; import { onFiltersRefreshSuccess } from 'src/dashboard/actions/dashboardState'; -import { dispatchFocusAction } from './utils'; +import { dispatchHoverAction, dispatchFocusAction } from './utils'; import { FilterControlProps } from './types'; import { getFormData } from '../../utils'; import { useFilterDependencies } from './state'; @@ -78,7 +78,7 @@ const useShouldFilterRefresh = () => { const FilterValue: React.FC<FilterControlProps> = ({ dataMaskSelected, filter, - directPathToChild, + focusedFilterId, onFilterSelectionChange, inView = true, showOverflow, @@ -211,10 +211,12 @@ const FilterValue: React.FC<FilterControlProps> = ({ ]); useEffect(() => { - if (directPathToChild?.[0] === filter.id) { - inputRef?.current?.focus(); + if (focusedFilterId && focusedFilterId === filter.id) { + setTimeout(() => { + inputRef?.current?.focus(); + }, 100); } - }, [inputRef, directPathToChild, filter.id]); + }, [inputRef, focusedFilterId, filter.id]); const setDataMask = useCallback( (dataMask: DataMask) => onFilterSelectionChange(filter, dataMask), @@ -230,14 +232,32 @@ const FilterValue: React.FC<FilterControlProps> = ({ [dispatch], ); + const setHoveredFilter = useCallback( + () => dispatchHoverAction(dispatch, id), + [dispatch, id], + ); + const unsetHoveredFilter = useCallback( + () => dispatchHoverAction(dispatch), + [dispatch], + ); + const hooks = useMemo( () => ({ setDataMask, + setHoveredFilter, + unsetHoveredFilter, setFocusedFilter, unsetFocusedFilter, setFilterActive, }), - [setDataMask, setFilterActive, setFocusedFilter, unsetFocusedFilter], + [ + setDataMask, + setFilterActive, + setHoveredFilter, + unsetHoveredFilter, + setFocusedFilter, + unsetFocusedFilter, + ], ); const isMissingRequiredValue = checkIsMissingRequiredValue( diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/types.ts b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/types.ts index a48ca5f0aa..ac9cca8c01 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/types.ts +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/types.ts @@ -36,7 +36,7 @@ export interface FilterControlProps extends BaseFilterProps { dataMask?: DataMask; }; icon?: React.ReactElement; - directPathToChild?: string[]; + focusedFilterId?: string; onFilterSelectionChange: (filter: Filter, dataMask: DataMask) => void; inView?: boolean; showOverflow?: boolean; diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/utils.ts b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/utils.ts index 3077c42768..546d65a344 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/utils.ts +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/utils.ts @@ -21,8 +21,21 @@ import { Dispatch } from 'react'; import { setFocusedNativeFilter, unsetFocusedNativeFilter, + setHoveredNativeFilter, + unsetHoveredNativeFilter, } from 'src/dashboard/actions/nativeFilters'; +export const dispatchHoverAction = debounce( + (dispatch: Dispatch<any>, id?: string) => { + if (id) { + dispatch(setHoveredNativeFilter(id)); + } else { + dispatch(unsetHoveredNativeFilter()); + } + }, + 300, +); + export const dispatchFocusAction = debounce( (dispatch: Dispatch<any>, id?: string) => { if (id) { diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Horizontal.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Horizontal.tsx index d085e08900..d640236ea8 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Horizontal.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Horizontal.tsx @@ -93,7 +93,7 @@ const HorizontalFilterBar: React.FC<HorizontalBarProps> = ({ dataMaskSelected, filterValues, isInitialized, - directPathToChild, + focusedFilterId, onSelectionChange, }) => { const hasFilters = filterValues.length > 0; @@ -124,7 +124,7 @@ const HorizontalFilterBar: React.FC<HorizontalBarProps> = ({ {hasFilters && ( <FilterControls dataMaskSelected={dataMaskSelected} - directPathToChild={directPathToChild} + focusedFilterId={focusedFilterId} onFilterSelectionChange={onSelectionChange} /> )} diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Vertical.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Vertical.tsx index 54ec436ea4..2a6b717836 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Vertical.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/Vertical.tsx @@ -141,7 +141,7 @@ const VerticalFilterBar: React.FC<VerticalBarProps> = ({ actions, canEdit, dataMaskSelected, - directPathToChild, + focusedFilterId, filtersOpen, filterValues, height, @@ -258,7 +258,7 @@ const VerticalFilterBar: React.FC<VerticalBarProps> = ({ <FilterControlsWrapper> <FilterControls dataMaskSelected={dataMaskSelected} - directPathToChild={directPathToChild} + focusedFilterId={focusedFilterId} onFilterSelectionChange={onSelectionChange} /> </FilterControlsWrapper> @@ -300,7 +300,7 @@ const VerticalFilterBar: React.FC<VerticalBarProps> = ({ <FilterControlsWrapper> <FilterControls dataMaskSelected={dataMaskSelected} - directPathToChild={directPathToChild} + focusedFilterId={focusedFilterId} onFilterSelectionChange={onSelectionChange} /> </FilterControlsWrapper> diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx index 2905c6a075..6eeee2ae70 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/index.tsx @@ -111,7 +111,7 @@ const publishDataMask = debounce( export const FilterBarScrollContext = createContext(false); const FilterBar: React.FC<FiltersBarProps> = ({ - directPathToChild, + focusedFilterId, orientation = FilterBarOrientation.VERTICAL, verticalConfig, }) => { @@ -254,7 +254,7 @@ const FilterBar: React.FC<FiltersBarProps> = ({ canEdit={canEdit} dashboardId={dashboardId} dataMaskSelected={dataMaskSelected} - directPathToChild={directPathToChild} + focusedFilterId={focusedFilterId} filterValues={filterValues} isInitialized={isInitialized} onSelectionChange={handleFilterSelectionChange} @@ -264,7 +264,7 @@ const FilterBar: React.FC<FiltersBarProps> = ({ actions={actions} canEdit={canEdit} dataMaskSelected={dataMaskSelected} - directPathToChild={directPathToChild} + focusedFilterId={focusedFilterId} filtersOpen={verticalConfig.filtersOpen} filterValues={filterValues} isInitialized={isInitialized} diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/types.ts b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/types.ts index ae1368eff8..034a639f58 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/types.ts +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/types.ts @@ -28,7 +28,7 @@ interface CommonFiltersBarProps { actions: React.ReactNode; canEdit: boolean; dataMaskSelected: DataMaskStateWithId; - directPathToChild?: string[]; + focusedFilterId?: string; filterValues: (Filter | Divider)[]; isInitialized: boolean; onSelectionChange: ( @@ -46,7 +46,7 @@ interface VerticalBarConfig { } export interface FiltersBarProps - extends Pick<CommonFiltersBarProps, 'directPathToChild'> { + extends Pick<CommonFiltersBarProps, 'focusedFilterId'> { orientation: FilterBarOrientation; verticalConfig?: VerticalBarConfig; } diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/useFilterControlFactory.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/useFilterControlFactory.tsx index 6893e629cb..80e532e45e 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/useFilterControlFactory.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/useFilterControlFactory.tsx @@ -33,7 +33,7 @@ import FilterDivider from './FilterControls/FilterDivider'; export const useFilterControlFactory = ( dataMaskSelected: DataMaskStateWithId, - directPathToChild: string[] | undefined, + focusedFilterId: string | undefined, onFilterSelectionChange: (filter: Filter, dataMask: DataMask) => void, ) => { const filters = useFilters(); @@ -68,7 +68,7 @@ export const useFilterControlFactory = ( <FilterControl dataMaskSelected={dataMaskSelected} filter={filter} - directPathToChild={directPathToChild} + focusedFilterId={focusedFilterId} onFilterSelectionChange={onFilterSelectionChange} inView={false} orientation={filterBarOrientation} @@ -79,7 +79,7 @@ export const useFilterControlFactory = ( [ filtersWithValues, dataMaskSelected, - directPathToChild, + focusedFilterId, onFilterSelectionChange, ], ); diff --git a/superset-frontend/src/dashboard/reducers/nativeFilters.ts b/superset-frontend/src/dashboard/reducers/nativeFilters.ts index b3900af31c..81a1a4bd2d 100644 --- a/superset-frontend/src/dashboard/reducers/nativeFilters.ts +++ b/superset-frontend/src/dashboard/reducers/nativeFilters.ts @@ -23,6 +23,8 @@ import { SET_FILTER_SETS_COMPLETE, SET_FOCUSED_NATIVE_FILTER, UNSET_FOCUSED_NATIVE_FILTER, + SET_HOVERED_NATIVE_FILTER, + UNSET_HOVERED_NATIVE_FILTER, } from 'src/dashboard/actions/nativeFilters'; import { FilterSet, @@ -102,6 +104,18 @@ export default function nativeFilterReducer( ...state, focusedFilterId: undefined, }; + + case SET_HOVERED_NATIVE_FILTER: + return { + ...state, + hoveredFilterId: action.id, + }; + + case UNSET_HOVERED_NATIVE_FILTER: + return { + ...state, + hoveredFilterId: undefined, + }; // TODO handle SET_FILTER_CONFIG_FAIL action default: return state; diff --git a/superset-frontend/src/dashboard/util/useFilterFocusHighlightStyles.test.tsx b/superset-frontend/src/dashboard/util/useFilterFocusHighlightStyles.test.tsx index 1d4d5af367..fdc31f78af 100644 --- a/superset-frontend/src/dashboard/util/useFilterFocusHighlightStyles.test.tsx +++ b/superset-frontend/src/dashboard/util/useFilterFocusHighlightStyles.test.tsx @@ -79,6 +79,25 @@ describe('useFilterFocusHighlightStyles', () => { expect(parseFloat(styles.opacity)).toBe(0.3); }); + it('should return unfocused styles if chart is not in scope of hovered native filter', async () => { + const store = createMockStore({ + nativeFilters: { + hoveredFilterId: 'test-filter', + filters: { + otherId: { + chartsInScope: [], + }, + }, + }, + }); + renderWrapper(10, store); + + const container = screen.getByTestId('test-component'); + + const styles = getComputedStyle(container); + expect(parseFloat(styles.opacity)).toBe(0.3); + }); + it('should return focused styles if chart is in scope of focused native filter', async () => { const chartId = 18; const store = createMockStore({ @@ -99,6 +118,26 @@ describe('useFilterFocusHighlightStyles', () => { expect(parseFloat(styles.opacity)).toBe(1); }); + it('should return focused styles if chart is in scope of hovered native filter', async () => { + const chartId = 18; + const store = createMockStore({ + nativeFilters: { + hoveredFilterId: 'testFilter', + filters: { + testFilter: { + chartsInScope: [chartId], + }, + }, + }, + }); + renderWrapper(chartId, store); + + const container = screen.getByTestId('test-component'); + + const styles = getComputedStyle(container); + expect(parseFloat(styles.opacity)).toBe(1); + }); + it('should return unfocused styles if focusedFilterField is targeting a different chart', async () => { const chartId = 18; const store = createMockStore({ diff --git a/superset-frontend/src/dashboard/util/useFilterFocusHighlightStyles.ts b/superset-frontend/src/dashboard/util/useFilterFocusHighlightStyles.ts index 958dce57fc..c64bc508ce 100644 --- a/superset-frontend/src/dashboard/util/useFilterFocusHighlightStyles.ts +++ b/superset-frontend/src/dashboard/util/useFilterFocusHighlightStyles.ts @@ -49,8 +49,9 @@ const useFilterFocusHighlightStyles = (chartId: number) => { dashboardFilters, ); - const focusedNativeFilterId = nativeFilters.focusedFilterId; - if (!(focusedFilterScope || focusedNativeFilterId)) { + const highlighterFilterId = + nativeFilters?.focusedFilterId || nativeFilters?.hoveredFilterId; + if (!(focusedFilterScope || highlighterFilterId)) { return {}; } @@ -67,9 +68,9 @@ const useFilterFocusHighlightStyles = (chartId: number) => { pointerEvents: 'auto', }; - if (focusedNativeFilterId) { + if (highlighterFilterId) { if ( - nativeFilters.filters[focusedNativeFilterId]?.chartsInScope?.includes( + nativeFilters.filters[highlighterFilterId]?.chartsInScope?.includes( chartId, ) ) { diff --git a/superset-frontend/src/filters/components/GroupBy/GroupByFilterPlugin.tsx b/superset-frontend/src/filters/components/GroupBy/GroupByFilterPlugin.tsx index 091232a5ec..dfc314024e 100644 --- a/superset-frontend/src/filters/components/GroupBy/GroupByFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/GroupBy/GroupByFilterPlugin.tsx @@ -36,6 +36,8 @@ export default function PluginFilterGroupBy(props: PluginFilterGroupByProps) { height, width, setDataMask, + setHoveredFilter, + unsetHoveredFilter, setFocusedFilter, unsetFocusedFilter, setFilterActive, @@ -116,6 +118,8 @@ export default function PluginFilterGroupBy(props: PluginFilterGroupByProps) { onChange={handleChange} onBlur={unsetFocusedFilter} onFocus={setFocusedFilter} + onMouseEnter={setHoveredFilter} + onMouseLeave={unsetHoveredFilter} ref={inputRef} options={options} onDropdownVisibleChange={setFilterActive} diff --git a/superset-frontend/src/filters/components/GroupBy/transformProps.ts b/superset-frontend/src/filters/components/GroupBy/transformProps.ts index 9ca0ec047b..1f50260273 100644 --- a/superset-frontend/src/filters/components/GroupBy/transformProps.ts +++ b/superset-frontend/src/filters/components/GroupBy/transformProps.ts @@ -33,6 +33,8 @@ export default function transformProps(chartProps: ChartProps) { } = chartProps; const { setDataMask = noOp, + setHoveredFilter = noOp, + unsetHoveredFilter = noOp, setFocusedFilter = noOp, unsetFocusedFilter = noOp, setFilterActive = noOp, @@ -48,6 +50,8 @@ export default function transformProps(chartProps: ChartProps) { data, formData: { ...DEFAULT_FORM_DATA, ...formData }, setDataMask, + setHoveredFilter, + unsetHoveredFilter, setFocusedFilter, unsetFocusedFilter, setFilterActive, diff --git a/superset-frontend/src/filters/components/Range/RangeFilterPlugin.tsx b/superset-frontend/src/filters/components/Range/RangeFilterPlugin.tsx index f3673f223e..1f8ea48206 100644 --- a/superset-frontend/src/filters/components/Range/RangeFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/Range/RangeFilterPlugin.tsx @@ -152,6 +152,8 @@ export default function RangeFilterPlugin(props: PluginFilterRangeProps) { setDataMask, setFocusedFilter, unsetFocusedFilter, + setHoveredFilter, + unsetHoveredFilter, setFilterActive, filterState, inputRef, @@ -289,8 +291,8 @@ export default function RangeFilterPlugin(props: PluginFilterRangeProps) { validateStatus={filterState.validateStatus} onFocus={setFocusedFilter} onBlur={unsetFocusedFilter} - onMouseEnter={setFocusedFilter} - onMouseLeave={unsetFocusedFilter} + onMouseEnter={setHoveredFilter} + onMouseLeave={unsetHoveredFilter} onMouseDown={() => setFilterActive(true)} onMouseUp={() => setFilterActive(false)} > diff --git a/superset-frontend/src/filters/components/Range/transformProps.ts b/superset-frontend/src/filters/components/Range/transformProps.ts index f4c47cec26..370b7dccd4 100644 --- a/superset-frontend/src/filters/components/Range/transformProps.ts +++ b/superset-frontend/src/filters/components/Range/transformProps.ts @@ -34,6 +34,8 @@ export default function transformProps(chartProps: ChartProps) { setDataMask = noOp, setFocusedFilter = noOp, unsetFocusedFilter = noOp, + setHoveredFilter = noOp, + unsetHoveredFilter = noOp, setFilterActive = noOp, } = hooks; const { data } = queriesData[0]; @@ -46,6 +48,8 @@ export default function transformProps(chartProps: ChartProps) { setDataMask, filterState, width, + setHoveredFilter, + unsetHoveredFilter, setFocusedFilter, unsetFocusedFilter, setFilterActive, diff --git a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx index 9cea856d47..52dbaf108e 100644 --- a/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/Select/SelectFilterPlugin.tsx @@ -82,6 +82,8 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { isRefreshing, width, setDataMask, + setHoveredFilter, + unsetHoveredFilter, setFocusedFilter, unsetFocusedFilter, setFilterActive, @@ -317,8 +319,9 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) { onSearch={searchWrapper} onSelect={clearSuggestionSearch} onBlur={handleBlur} - onMouseEnter={setFocusedFilter} - onMouseLeave={unsetFocusedFilter} + onFocus={setFocusedFilter} + onMouseEnter={setHoveredFilter} + onMouseLeave={unsetHoveredFilter} // @ts-ignore onChange={handleChange} ref={inputRef} diff --git a/superset-frontend/src/filters/components/Select/transformProps.ts b/superset-frontend/src/filters/components/Select/transformProps.ts index 59d9adc57e..666d937e43 100644 --- a/superset-frontend/src/filters/components/Select/transformProps.ts +++ b/superset-frontend/src/filters/components/Select/transformProps.ts @@ -38,6 +38,8 @@ export default function transformProps( const newFormData = { ...DEFAULT_FORM_DATA, ...formData }; const { setDataMask = noOp, + setHoveredFilter = noOp, + unsetHoveredFilter = noOp, setFocusedFilter = noOp, unsetFocusedFilter = noOp, setFilterActive = noOp, @@ -60,6 +62,8 @@ export default function transformProps( formData: newFormData, isRefreshing, setDataMask, + setHoveredFilter, + unsetHoveredFilter, setFocusedFilter, unsetFocusedFilter, setFilterActive, diff --git a/superset-frontend/src/filters/components/Time/TimeFilterPlugin.tsx b/superset-frontend/src/filters/components/Time/TimeFilterPlugin.tsx index 82bfc678f0..1d45ba28af 100644 --- a/superset-frontend/src/filters/components/Time/TimeFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/Time/TimeFilterPlugin.tsx @@ -57,6 +57,8 @@ const ControlContainer = styled.div<{ export default function TimeFilterPlugin(props: PluginFilterTimeProps) { const { setDataMask, + setHoveredFilter, + unsetHoveredFilter, setFocusedFilter, unsetFocusedFilter, setFilterActive, @@ -95,8 +97,8 @@ export default function TimeFilterPlugin(props: PluginFilterTimeProps) { validateStatus={filterState.validateStatus} onFocus={setFocusedFilter} onBlur={unsetFocusedFilter} - onMouseEnter={setFocusedFilter} - onMouseLeave={unsetFocusedFilter} + onMouseEnter={setHoveredFilter} + onMouseLeave={unsetHoveredFilter} > <DateFilterControl value={filterState.value || NO_TIME_RANGE} diff --git a/superset-frontend/src/filters/components/Time/transformProps.ts b/superset-frontend/src/filters/components/Time/transformProps.ts index 883b6002d8..93837d7f5a 100644 --- a/superset-frontend/src/filters/components/Time/transformProps.ts +++ b/superset-frontend/src/filters/components/Time/transformProps.ts @@ -33,6 +33,8 @@ export default function transformProps(chartProps: ChartProps) { } = chartProps; const { setDataMask = noOp, + setHoveredFilter = noOp, + unsetHoveredFilter = noOp, setFocusedFilter = noOp, unsetFocusedFilter = noOp, setFilterActive = noOp, @@ -49,6 +51,8 @@ export default function transformProps(chartProps: ChartProps) { height, behaviors, setDataMask, + setHoveredFilter, + unsetHoveredFilter, setFocusedFilter, unsetFocusedFilter, setFilterActive, diff --git a/superset-frontend/src/filters/components/TimeColumn/TimeColumnFilterPlugin.tsx b/superset-frontend/src/filters/components/TimeColumn/TimeColumnFilterPlugin.tsx index fd5f21caad..4c25b4bec0 100644 --- a/superset-frontend/src/filters/components/TimeColumn/TimeColumnFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/TimeColumn/TimeColumnFilterPlugin.tsx @@ -38,6 +38,8 @@ export default function PluginFilterTimeColumn( height, width, setDataMask, + setHoveredFilter, + unsetHoveredFilter, setFocusedFilter, unsetFocusedFilter, setFilterActive, @@ -114,8 +116,10 @@ export default function PluginFilterTimeColumn( placeholder={placeholderText} // @ts-ignore onChange={handleChange} - onMouseEnter={setFocusedFilter} - onMouseLeave={unsetFocusedFilter} + onBlur={unsetFocusedFilter} + onFocus={setFocusedFilter} + onMouseEnter={setHoveredFilter} + onMouseLeave={unsetHoveredFilter} ref={inputRef} options={options} onDropdownVisibleChange={setFilterActive} diff --git a/superset-frontend/src/filters/components/TimeColumn/transformProps.ts b/superset-frontend/src/filters/components/TimeColumn/transformProps.ts index 9ca0ec047b..1f50260273 100644 --- a/superset-frontend/src/filters/components/TimeColumn/transformProps.ts +++ b/superset-frontend/src/filters/components/TimeColumn/transformProps.ts @@ -33,6 +33,8 @@ export default function transformProps(chartProps: ChartProps) { } = chartProps; const { setDataMask = noOp, + setHoveredFilter = noOp, + unsetHoveredFilter = noOp, setFocusedFilter = noOp, unsetFocusedFilter = noOp, setFilterActive = noOp, @@ -48,6 +50,8 @@ export default function transformProps(chartProps: ChartProps) { data, formData: { ...DEFAULT_FORM_DATA, ...formData }, setDataMask, + setHoveredFilter, + unsetHoveredFilter, setFocusedFilter, unsetFocusedFilter, setFilterActive, diff --git a/superset-frontend/src/filters/components/TimeGrain/TimeGrainFilterPlugin.tsx b/superset-frontend/src/filters/components/TimeGrain/TimeGrainFilterPlugin.tsx index 383272c7bb..55dca4b280 100644 --- a/superset-frontend/src/filters/components/TimeGrain/TimeGrainFilterPlugin.tsx +++ b/superset-frontend/src/filters/components/TimeGrain/TimeGrainFilterPlugin.tsx @@ -38,6 +38,8 @@ export default function PluginFilterTimegrain( height, width, setDataMask, + setHoveredFilter, + unsetHoveredFilter, setFocusedFilter, unsetFocusedFilter, setFilterActive, @@ -124,8 +126,10 @@ export default function PluginFilterTimegrain( placeholder={placeholderText} // @ts-ignore onChange={handleChange} - onMouseEnter={setFocusedFilter} - onMouseLeave={unsetFocusedFilter} + onBlur={unsetFocusedFilter} + onFocus={setFocusedFilter} + onMouseEnter={setHoveredFilter} + onMouseLeave={unsetHoveredFilter} ref={inputRef} options={options} onDropdownVisibleChange={setFilterActive} diff --git a/superset-frontend/src/filters/components/TimeGrain/transformProps.ts b/superset-frontend/src/filters/components/TimeGrain/transformProps.ts index 797a2d1d80..b86d04c0c4 100644 --- a/superset-frontend/src/filters/components/TimeGrain/transformProps.ts +++ b/superset-frontend/src/filters/components/TimeGrain/transformProps.ts @@ -25,6 +25,8 @@ export default function transformProps(chartProps: ChartProps) { chartProps; const { setDataMask = noOp, + setHoveredFilter = noOp, + unsetHoveredFilter = noOp, setFocusedFilter = noOp, unsetFocusedFilter = noOp, setFilterActive = noOp, @@ -39,6 +41,8 @@ export default function transformProps(chartProps: ChartProps) { data, formData: { ...DEFAULT_FORM_DATA, ...formData }, setDataMask, + setHoveredFilter, + unsetHoveredFilter, setFocusedFilter, unsetFocusedFilter, setFilterActive, diff --git a/superset-frontend/src/filters/components/types.ts b/superset-frontend/src/filters/components/types.ts index 4ab75a825c..fd6229826b 100644 --- a/superset-frontend/src/filters/components/types.ts +++ b/superset-frontend/src/filters/components/types.ts @@ -30,5 +30,7 @@ export interface PluginFilterHooks { setDataMask: SetDataMaskHook; setFocusedFilter: () => void; unsetFocusedFilter: () => void; + setHoveredFilter: () => void; + unsetHoveredFilter: () => void; setFilterActive: (isActive: boolean) => void; }
