mirror of https://github.com/apache/superset.git
feat: Programmatically open "more filters" dropdown in Horizontal Filter Bar (#22276)
Co-authored-by: Michael S. Molina <70410625+michael-s-molina@users.noreply.github.com>
This commit is contained in:
parent
7bc5f04368
commit
df91664217
|
@ -126,6 +126,7 @@ export type NativeFiltersState = {
|
|||
filters: Filters;
|
||||
filterSets: FilterSets;
|
||||
focusedFilterId?: string;
|
||||
hoveredFilterId?: string;
|
||||
};
|
||||
|
||||
export type DashboardComponentMetadata = {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
@ -369,7 +368,7 @@ const DashboardBuilder: FC<DashboardBuilderProps> = () => {
|
|||
{showFilterBar &&
|
||||
filterBarOrientation === FilterBarOrientation.HORIZONTAL && (
|
||||
<FilterBar
|
||||
directPathToChild={directPathToChild}
|
||||
focusedFilterId={focusedFilterId}
|
||||
orientation={FilterBarOrientation.HORIZONTAL}
|
||||
/>
|
||||
)}
|
||||
|
@ -401,7 +400,7 @@ const DashboardBuilder: FC<DashboardBuilderProps> = () => {
|
|||
</div>
|
||||
),
|
||||
[
|
||||
directPathToChild,
|
||||
focusedFilterId,
|
||||
nativeFiltersEnabled,
|
||||
filterBarOrientation,
|
||||
editMode,
|
||||
|
@ -437,7 +436,7 @@ const DashboardBuilder: FC<DashboardBuilderProps> = () => {
|
|||
<StickyPanel ref={containerRef} width={filterBarWidth}>
|
||||
<ErrorBoundary>
|
||||
<FilterBar
|
||||
directPathToChild={directPathToChild}
|
||||
focusedFilterId={focusedFilterId}
|
||||
orientation={FilterBarOrientation.VERTICAL}
|
||||
verticalConfig={{
|
||||
filtersOpen: dashboardFiltersOpen,
|
||||
|
|
|
@ -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],
|
||||
);
|
||||
|
|
|
@ -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 highlightedFilterId =
|
||||
nativeFilters?.focusedFilterId || nativeFilters?.hoveredFilterId;
|
||||
if (highlightedFilterId) {
|
||||
tabsToHighlight = nativeFilters.filters[highlightedFilterId]?.tabsInScope;
|
||||
}
|
||||
return (
|
||||
<DragDroppable
|
||||
|
|
|
@ -217,7 +217,7 @@ const FilterControl = ({
|
|||
filter,
|
||||
icon,
|
||||
onFilterSelectionChange,
|
||||
directPathToChild,
|
||||
focusedFilterId,
|
||||
inView,
|
||||
showOverflow,
|
||||
parentRef,
|
||||
|
@ -298,7 +298,7 @@ const FilterControl = ({
|
|||
dataMaskSelected={dataMaskSelected}
|
||||
filter={filter}
|
||||
showOverflow={showOverflow}
|
||||
directPathToChild={directPathToChild}
|
||||
focusedFilterId={focusedFilterId}
|
||||
onFilterSelectionChange={onFilterSelectionChange}
|
||||
inView={inView}
|
||||
parentRef={parentRef}
|
||||
|
|
|
@ -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,
|
||||
|
@ -40,20 +47,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,
|
||||
}) => {
|
||||
|
@ -65,10 +74,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(() => {
|
||||
|
@ -183,6 +193,7 @@ const FilterControls: FC<FilterControlsProps> = ({
|
|||
)
|
||||
: undefined
|
||||
}
|
||||
ref={popoverRef}
|
||||
onOverflowingStateChange={({ overflowed: nextOverflowedIds }) => {
|
||||
if (
|
||||
nextOverflowedIds.length !== overflowedIds.length ||
|
||||
|
@ -211,6 +222,12 @@ const FilterControls: FC<FilterControlsProps> = ({
|
|||
);
|
||||
}, [filtersOutOfScope, filtersWithValues, overflowedFiltersInScope]);
|
||||
|
||||
useEffect(() => {
|
||||
if (focusedFilterId && overflowedIds.includes(focusedFilterId)) {
|
||||
popoverRef?.current?.open();
|
||||
}
|
||||
}, [focusedFilterId, popoverRef, overflowedIds]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{portalNodes
|
||||
|
|
|
@ -44,7 +44,8 @@ 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 { FAST_DEBOUNCE } from 'src/constants';
|
||||
import { dispatchHoverAction, dispatchFocusAction } from './utils';
|
||||
import { FilterControlProps } from './types';
|
||||
import { getFormData } from '../../utils';
|
||||
import { useFilterDependencies } from './state';
|
||||
|
@ -78,7 +79,7 @@ const useShouldFilterRefresh = () => {
|
|||
const FilterValue: React.FC<FilterControlProps> = ({
|
||||
dataMaskSelected,
|
||||
filter,
|
||||
directPathToChild,
|
||||
focusedFilterId,
|
||||
onFilterSelectionChange,
|
||||
inView = true,
|
||||
showOverflow,
|
||||
|
@ -211,10 +212,12 @@ const FilterValue: React.FC<FilterControlProps> = ({
|
|||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (directPathToChild?.[0] === filter.id) {
|
||||
inputRef?.current?.focus();
|
||||
if (focusedFilterId && focusedFilterId === filter.id) {
|
||||
setTimeout(() => {
|
||||
inputRef?.current?.focus();
|
||||
}, FAST_DEBOUNCE);
|
||||
}
|
||||
}, [inputRef, directPathToChild, filter.id]);
|
||||
}, [inputRef, focusedFilterId, filter.id]);
|
||||
|
||||
const setDataMask = useCallback(
|
||||
(dataMask: DataMask) => onFilterSelectionChange(filter, dataMask),
|
||||
|
@ -230,14 +233,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(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -21,7 +21,21 @@ import { Dispatch } from 'react';
|
|||
import {
|
||||
setFocusedNativeFilter,
|
||||
unsetFocusedNativeFilter,
|
||||
setHoveredNativeFilter,
|
||||
unsetHoveredNativeFilter,
|
||||
} from 'src/dashboard/actions/nativeFilters';
|
||||
import { FAST_DEBOUNCE } from 'src/constants';
|
||||
|
||||
export const dispatchHoverAction = debounce(
|
||||
(dispatch: Dispatch<any>, id?: string) => {
|
||||
if (id) {
|
||||
dispatch(setHoveredNativeFilter(id));
|
||||
} else {
|
||||
dispatch(unsetHoveredNativeFilter());
|
||||
}
|
||||
},
|
||||
FAST_DEBOUNCE,
|
||||
);
|
||||
|
||||
export const dispatchFocusAction = debounce(
|
||||
(dispatch: Dispatch<any>, id?: string) => {
|
||||
|
@ -31,5 +45,5 @@ export const dispatchFocusAction = debounce(
|
|||
dispatch(unsetFocusedNativeFilter());
|
||||
}
|
||||
},
|
||||
300,
|
||||
FAST_DEBOUNCE,
|
||||
);
|
||||
|
|
|
@ -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}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
],
|
||||
);
|
||||
|
|
|
@ -19,9 +19,9 @@
|
|||
import React, { useCallback, useMemo, useRef } from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { css, t, useTheme } from '@superset-ui/core';
|
||||
import { setDirectPathToChild } from 'src/dashboard/actions/dashboardState';
|
||||
import Icons from 'src/components/Icons';
|
||||
import { useTruncation } from 'src/hooks/useTruncation';
|
||||
import { setFocusedNativeFilter } from 'src/dashboard/actions/nativeFilters';
|
||||
import {
|
||||
DependencyItem,
|
||||
Row,
|
||||
|
@ -40,7 +40,7 @@ const DependencyValue = ({
|
|||
}: DependencyValueProps) => {
|
||||
const dispatch = useDispatch();
|
||||
const handleClick = useCallback(() => {
|
||||
dispatch(setDirectPathToChild([dependency.id]));
|
||||
dispatch(setFocusedNativeFilter(dependency.id));
|
||||
}, [dependency.id, dispatch]);
|
||||
return (
|
||||
<span>
|
||||
|
|
|
@ -23,7 +23,7 @@ import { Filter, NativeFilterType } from '@superset-ui/core';
|
|||
import userEvent from '@testing-library/user-event';
|
||||
import { render, screen } from 'spec/helpers/testing-library';
|
||||
import { DASHBOARD_ROOT_ID } from 'src/dashboard/util/constants';
|
||||
import { SET_DIRECT_PATH } from 'src/dashboard/actions/dashboardState';
|
||||
import { SET_FOCUSED_NATIVE_FILTER } from 'src/dashboard/actions/nativeFilters';
|
||||
import { FilterCardContent } from './FilterCardContent';
|
||||
|
||||
const baseInitialState = {
|
||||
|
@ -298,8 +298,8 @@ describe('Filter Card', () => {
|
|||
|
||||
userEvent.click(screen.getByText('Native filter 2'));
|
||||
expect(dummyDispatch).toHaveBeenCalledWith({
|
||||
type: SET_DIRECT_PATH,
|
||||
path: ['NATIVE_FILTER-2'],
|
||||
type: SET_FOCUSED_NATIVE_FILTER,
|
||||
id: 'NATIVE_FILTER-2',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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({
|
||||
|
|
|
@ -49,8 +49,9 @@ const useFilterFocusHighlightStyles = (chartId: number) => {
|
|||
dashboardFilters,
|
||||
);
|
||||
|
||||
const focusedNativeFilterId = nativeFilters.focusedFilterId;
|
||||
if (!(focusedFilterScope || focusedNativeFilterId)) {
|
||||
const highlightedFilterId =
|
||||
nativeFilters?.focusedFilterId || nativeFilters?.hoveredFilterId;
|
||||
if (!(focusedFilterScope || highlightedFilterId)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -67,9 +68,9 @@ const useFilterFocusHighlightStyles = (chartId: number) => {
|
|||
pointerEvents: 'auto',
|
||||
};
|
||||
|
||||
if (focusedNativeFilterId) {
|
||||
if (highlightedFilterId) {
|
||||
if (
|
||||
nativeFilters.filters[focusedNativeFilterId]?.chartsInScope?.includes(
|
||||
nativeFilters.filters[highlightedFilterId]?.chartsInScope?.includes(
|
||||
chartId,
|
||||
)
|
||||
) {
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -171,6 +171,8 @@ export default function RangeFilterPlugin(props: PluginFilterRangeProps) {
|
|||
setDataMask,
|
||||
setFocusedFilter,
|
||||
unsetFocusedFilter,
|
||||
setHoveredFilter,
|
||||
unsetHoveredFilter,
|
||||
setFilterActive,
|
||||
filterState,
|
||||
inputRef,
|
||||
|
@ -312,8 +314,8 @@ export default function RangeFilterPlugin(props: PluginFilterRangeProps) {
|
|||
isOverflowing={isOverflowingFilterBar}
|
||||
onFocus={setFocusedFilter}
|
||||
onBlur={unsetFocusedFilter}
|
||||
onMouseEnter={setFocusedFilter}
|
||||
onMouseLeave={unsetFocusedFilter}
|
||||
onMouseEnter={setHoveredFilter}
|
||||
onMouseLeave={unsetHoveredFilter}
|
||||
onMouseDown={() => setFilterActive(true)}
|
||||
onMouseUp={() => setFilterActive(false)}
|
||||
>
|
||||
|
|
|
@ -35,6 +35,8 @@ export default function transformProps(chartProps: ChartProps) {
|
|||
setDataMask = noOp,
|
||||
setFocusedFilter = noOp,
|
||||
unsetFocusedFilter = noOp,
|
||||
setHoveredFilter = noOp,
|
||||
unsetHoveredFilter = noOp,
|
||||
setFilterActive = noOp,
|
||||
} = hooks;
|
||||
const { data } = queriesData[0];
|
||||
|
@ -47,6 +49,8 @@ export default function transformProps(chartProps: ChartProps) {
|
|||
setDataMask,
|
||||
filterState,
|
||||
width,
|
||||
setHoveredFilter,
|
||||
unsetHoveredFilter,
|
||||
setFocusedFilter,
|
||||
unsetFocusedFilter,
|
||||
setFilterActive,
|
||||
|
|
|
@ -83,6 +83,8 @@ export default function PluginFilterSelect(props: PluginFilterSelectProps) {
|
|||
isRefreshing,
|
||||
width,
|
||||
setDataMask,
|
||||
setHoveredFilter,
|
||||
unsetHoveredFilter,
|
||||
setFocusedFilter,
|
||||
unsetFocusedFilter,
|
||||
setFilterActive,
|
||||
|
@ -319,8 +321,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}
|
||||
|
|
|
@ -39,6 +39,8 @@ export default function transformProps(
|
|||
const newFormData = { ...DEFAULT_FORM_DATA, ...formData };
|
||||
const {
|
||||
setDataMask = noOp,
|
||||
setHoveredFilter = noOp,
|
||||
unsetHoveredFilter = noOp,
|
||||
setFocusedFilter = noOp,
|
||||
unsetFocusedFilter = noOp,
|
||||
setFilterActive = noOp,
|
||||
|
@ -61,6 +63,8 @@ export default function transformProps(
|
|||
formData: newFormData,
|
||||
isRefreshing,
|
||||
setDataMask,
|
||||
setHoveredFilter,
|
||||
unsetHoveredFilter,
|
||||
setFocusedFilter,
|
||||
unsetFocusedFilter,
|
||||
setFilterActive,
|
||||
|
|
|
@ -66,6 +66,8 @@ const ControlContainer = styled.div<{
|
|||
export default function TimeFilterPlugin(props: PluginFilterTimeProps) {
|
||||
const {
|
||||
setDataMask,
|
||||
setHoveredFilter,
|
||||
unsetHoveredFilter,
|
||||
setFocusedFilter,
|
||||
unsetFocusedFilter,
|
||||
setFilterActive,
|
||||
|
@ -104,8 +106,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}
|
||||
|
|
|
@ -34,6 +34,8 @@ export default function transformProps(chartProps: ChartProps) {
|
|||
} = chartProps;
|
||||
const {
|
||||
setDataMask = noOp,
|
||||
setHoveredFilter = noOp,
|
||||
unsetHoveredFilter = noOp,
|
||||
setFocusedFilter = noOp,
|
||||
unsetFocusedFilter = noOp,
|
||||
setFilterActive = noOp,
|
||||
|
@ -50,6 +52,8 @@ export default function transformProps(chartProps: ChartProps) {
|
|||
height,
|
||||
behaviors,
|
||||
setDataMask,
|
||||
setHoveredFilter,
|
||||
unsetHoveredFilter,
|
||||
setFocusedFilter,
|
||||
unsetFocusedFilter,
|
||||
setFilterActive,
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -30,5 +30,7 @@ export interface PluginFilterHooks {
|
|||
setDataMask: SetDataMaskHook;
|
||||
setFocusedFilter: () => void;
|
||||
unsetFocusedFilter: () => void;
|
||||
setHoveredFilter: () => void;
|
||||
unsetHoveredFilter: () => void;
|
||||
setFilterActive: (isActive: boolean) => void;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue