Skip to content

Commit

Permalink
chore(native-filters): Connect indicator magnifier with Filter Bar (#…
Browse files Browse the repository at this point in the history
…12812)

* Add direct path to child to native filter components

* Implement focus for cascading filters

* Remove empty line
  • Loading branch information
agatapst authored Jan 30, 2021
1 parent 3a2300b commit 30be86f
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ class DashboardBuilder extends React.Component {
showBuilderPane,
setColorSchemeAndUnsavedChanges,
colorScheme,
directPathToChild,
} = this.props;
const { tabIndex } = this.state;
const dashboardRoot = dashboardLayout[DASHBOARD_ROOT_ID];
Expand Down Expand Up @@ -290,6 +291,7 @@ class DashboardBuilder extends React.Component {
<FilterBar
filtersOpen={this.state.dashboardFiltersOpen}
toggleFiltersBar={this.toggleDashboardFiltersOpen}
directPathToChild={directPathToChild}
/>
</ErrorBoundary>
</StickyVerticalBar>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* specific language governing permissions and limitations
* under the License.
*/
import React, { useCallback } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { ExtraFormData, styled, t } from '@superset-ui/core';
import Popover from 'src/common/components/Popover';
import Icon from 'src/components/Icon';
Expand All @@ -27,6 +27,7 @@ import { Filter, CascadeFilter } from './types';
interface CascadePopoverProps {
filter: CascadeFilter;
visible: boolean;
directPathToChild?: string[];
onVisibleChange: (visible: boolean) => void;
onExtraFormDataChange: (filter: Filter, extraFormData: ExtraFormData) => void;
}
Expand Down Expand Up @@ -73,7 +74,18 @@ const CascadePopover: React.FC<CascadePopoverProps> = ({
visible,
onVisibleChange,
onExtraFormDataChange,
directPathToChild,
}) => {
const [currentPathToChild, setCurrentPathToChild] = useState<string[]>();

useEffect(() => {
setCurrentPathToChild(directPathToChild);
// clear local copy of directPathToChild after 500ms
// to prevent triggering multiple focus
const timeout = setTimeout(() => setCurrentPathToChild(undefined), 500);
return () => clearTimeout(timeout);
}, [directPathToChild, setCurrentPathToChild]);

const getActiveChildren = useCallback((filter: CascadeFilter):
| CascadeFilter[]
| null => {
Expand All @@ -95,30 +107,48 @@ const CascadePopover: React.FC<CascadePopoverProps> = ({
return null;
}, []);

const getAllFilters = (filter: CascadeFilter): CascadeFilter[] => {
const children = filter.cascadeChildren || [];
const allChildren = children.flatMap(getAllFilters);
return [filter, ...allChildren];
};

const allFilters = getAllFilters(filter);
const activeFilters = useMemo(() => getActiveChildren(filter) || [filter], [
filter,
getActiveChildren,
]);

useEffect(() => {
const focusedFilterId = currentPathToChild?.[0];
// filters not directly displayed in the Filter Bar
const inactiveFilters = allFilters.filter(
filterEl => !activeFilters.includes(filterEl),
);
const focusedInactiveFilter = inactiveFilters.some(
cascadeChild => cascadeChild.id === focusedFilterId,
);

if (focusedInactiveFilter) {
onVisibleChange(true);
}
}, [currentPathToChild]);

if (!filter.cascadeChildren?.length) {
return (
<FilterControl
filter={filter}
directPathToChild={directPathToChild}
onExtraFormDataChange={onExtraFormDataChange}
/>
);
}

const countFilters = (filter: CascadeFilter): number => {
let count = 1;
filter.cascadeChildren.forEach(child => {
count += countFilters(child);
});
return count;
};

const totalChildren = countFilters(filter);

const title = (
<StyledTitleBox>
<StyledTitle>
<StyledIcon name="edit" />
{t('Select parent filters')} ({totalChildren})
{t('Select parent filters')} ({allFilters.length})
</StyledTitle>
<StyledIcon name="close" onClick={() => onVisibleChange(false)} />
</StyledTitleBox>
Expand All @@ -129,12 +159,11 @@ const CascadePopover: React.FC<CascadePopoverProps> = ({
data-test="cascade-filters-control"
key={filter.id}
filter={filter}
directPathToChild={visible ? currentPathToChild : undefined}
onExtraFormDataChange={onExtraFormDataChange}
/>
);

const activeFilters = getActiveChildren(filter) || [filter];

return (
<Popover
content={content}
Expand All @@ -152,11 +181,12 @@ const CascadePopover: React.FC<CascadePopoverProps> = ({
key={activeFilter.id}
filter={activeFilter}
onExtraFormDataChange={onExtraFormDataChange}
directPathToChild={currentPathToChild}
icon={
<>
{filter.cascadeChildren.length !== 0 && (
<StyledPill onClick={() => onVisibleChange(true)}>
<Icon name="filter" /> {totalChildren}
<Icon name="filter" /> {allFilters.length}
</StyledPill>
)}
</>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,13 @@ import {
t,
ExtraFormData,
} from '@superset-ui/core';
import React, { useState, useEffect, useMemo, useCallback } from 'react';
import React, {
useState,
useEffect,
useMemo,
useCallback,
useRef,
} from 'react';
import { useSelector } from 'react-redux';
import cx from 'classnames';
import { Form } from 'src/common/components';
Expand Down Expand Up @@ -194,16 +200,19 @@ const StyledLoadingBox = styled.div`
interface FilterProps {
filter: Filter;
icon?: React.ReactElement;
directPathToChild?: string[];
onExtraFormDataChange: (filter: Filter, extraFormData: ExtraFormData) => void;
}

interface FiltersBarProps {
filtersOpen: boolean;
toggleFiltersBar: any;
directPathToChild?: string[];
}

const FilterValue: React.FC<FilterProps> = ({
filter,
directPathToChild,
onExtraFormDataChange,
}) => {
const {
Expand All @@ -219,6 +228,7 @@ const FilterValue: React.FC<FilterProps> = ({
const [state, setState] = useState([]);
const [error, setError] = useState<boolean>(false);
const [formData, setFormData] = useState<Partial<QueryFormData>>({});
const inputRef = useRef<HTMLInputElement>(null);
const [target] = targets;
const { datasetId = 18, column } = target;
const { name: groupby } = column;
Expand All @@ -240,6 +250,7 @@ const FilterValue: React.FC<FilterProps> = ({
url_params: {},
viz_type: 'filter_select',
defaultValues: currentValue || defaultValue || [],
inputRef,
});

useEffect(() => {
Expand All @@ -263,6 +274,17 @@ const FilterValue: React.FC<FilterProps> = ({
}
}, [cascadingFilters, datasetId, groupby]);

useEffect(() => {
if (directPathToChild?.[0] === filter.id) {
// wait for Cascade Popover to open
const timeout = setTimeout(() => {
inputRef?.current?.focus();
}, 200);
return () => clearTimeout(timeout);
}
return undefined;
}, [inputRef, directPathToChild, filter.id]);

const setExtraFormData = (extraFormData: ExtraFormData) =>
onExtraFormDataChange(filter, extraFormData);

Expand Down Expand Up @@ -308,6 +330,7 @@ export const FilterControl: React.FC<FilterProps> = ({
filter,
icon,
onExtraFormDataChange,
directPathToChild,
}) => {
const { name = '<undefined>' } = filter;
return (
Expand All @@ -318,6 +341,7 @@ export const FilterControl: React.FC<FilterProps> = ({
</StyledFilterControlTitleBox>
<FilterValue
filter={filter}
directPathToChild={directPathToChild}
onExtraFormDataChange={onExtraFormDataChange}
/>
</StyledFilterControlContainer>
Expand All @@ -326,18 +350,21 @@ export const FilterControl: React.FC<FilterProps> = ({

interface CascadeFilterControlProps {
filter: CascadeFilter;
directPathToChild?: string[];
onExtraFormDataChange: (filter: Filter, extraFormData: ExtraFormData) => void;
}

export const CascadeFilterControl: React.FC<CascadeFilterControlProps> = ({
filter,
directPathToChild,
onExtraFormDataChange,
}) => (
<>
<StyledFilterControlBox>
<StyledCaretIcon name="caret-down" />
<FilterControl
filter={filter}
directPathToChild={directPathToChild}
onExtraFormDataChange={onExtraFormDataChange}
/>
</StyledFilterControlBox>
Expand All @@ -347,6 +374,7 @@ export const CascadeFilterControl: React.FC<CascadeFilterControlProps> = ({
<li key={childFilter.id}>
<CascadeFilterControl
filter={childFilter}
directPathToChild={directPathToChild}
onExtraFormDataChange={onExtraFormDataChange}
/>
</li>
Expand All @@ -358,6 +386,7 @@ export const CascadeFilterControl: React.FC<CascadeFilterControlProps> = ({
const FilterBar: React.FC<FiltersBarProps> = ({
filtersOpen,
toggleFiltersBar,
directPathToChild,
}) => {
const [filterData, setFilterData] = useState<{ [id: string]: ExtraFormData }>(
{},
Expand Down Expand Up @@ -493,6 +522,7 @@ const FilterBar: React.FC<FiltersBarProps> = ({
}
filter={filter}
onExtraFormDataChange={handleExtraFormDataChange}
directPathToChild={directPathToChild}
/>
))}
</FilterControls>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const Styles = styled.div<AntdPluginFilterStylesProps>`
`;

export default function AntdRangeFilter(props: AntdPluginFilterRangeProps) {
const { data, formData, height, width, setExtraFormData } = props;
const { data, formData, height, width, setExtraFormData, inputRef } = props;
const [row] = data;
// @ts-ignore
const { min, max }: { min: number; max: number } = row;
Expand All @@ -50,6 +50,7 @@ export default function AntdRangeFilter(props: AntdPluginFilterRangeProps) {
max={max}
defaultValue={[min, max]}
onChange={handleChange}
ref={inputRef}
/>
</Styles>
);
Expand Down
2 changes: 2 additions & 0 deletions superset-frontend/src/filters/components/Range/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
QueryFormData,
SetExtraFormDataHook,
} from '@superset-ui/core';
import { RefObject } from 'react';
import { AntdPluginFilterStylesProps } from '../types';

interface AntdPluginFilterSelectCustomizeProps {
Expand All @@ -36,4 +37,5 @@ export type AntdPluginFilterRangeProps = AntdPluginFilterStylesProps & {
data: DataRecord[];
formData: PluginFilterRangeQueryFormData;
setExtraFormData: SetExtraFormDataHook;
inputRef: RefObject<any>;
};
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export default function AntdPluginFilterSelect(
multiSelect,
showSearch,
inverseSelection,
inputRef,
} = {
...DEFAULT_FORM_DATA,
...formData,
Expand Down Expand Up @@ -79,6 +80,7 @@ export default function AntdPluginFilterSelect(
placeholder={placeholderText}
// @ts-ignore
onChange={handleChange}
ref={inputRef}
>
{(data || []).map(row => {
const option = `${groupby.map(col => row[col])[0]}`;
Expand Down
2 changes: 2 additions & 0 deletions superset-frontend/src/filters/components/Select/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
DataRecord,
SetExtraFormDataHook,
} from '@superset-ui/core';
import { RefObject } from 'react';
import { AntdPluginFilterStylesProps } from '../types';

interface AntdPluginFilterSelectCustomizeProps {
Expand All @@ -30,6 +31,7 @@ interface AntdPluginFilterSelectCustomizeProps {
inverseSelection: boolean;
multiSelect: boolean;
showSearch: boolean;
inputRef?: RefObject<HTMLInputElement>;
}

export type AntdPluginFilterSelectQueryFormData = QueryFormData &
Expand Down

0 comments on commit 30be86f

Please sign in to comment.