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;
 }


Reply via email to