You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by di...@apache.org on 2022/11/30 15:11:19 UTC
[superset] 02/02: Open dropdown programmatically
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 <di...@gmail.com>
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;
}