This is an automated email from the ASF dual-hosted git repository.

villebro pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git


The following commit(s) were added to refs/heads/master by this push:
     new 30be86f  chore(native-filters): Connect indicator magnifier with 
Filter Bar (#12812)
30be86f is described below

commit 30be86f81179d76fe245d20979b4f433cca405a5
Author: Agata Stawarz <47450693+agata...@users.noreply.github.com>
AuthorDate: Sat Jan 30 14:15:44 2021 +0100

    chore(native-filters): Connect indicator magnifier with Filter Bar (#12812)
    
    * Add direct path to child to native filter components
    
    * Implement focus for cascading filters
    
    * Remove empty line
---
 .../src/dashboard/components/DashboardBuilder.jsx  |  2 +
 .../components/nativeFilters/CascadePopover.tsx    | 60 ++++++++++++++++------
 .../components/nativeFilters/FilterBar.tsx         | 32 +++++++++++-
 .../filters/components/Range/AntdRangeFilter.tsx   |  3 +-
 .../src/filters/components/Range/types.ts          |  2 +
 .../filters/components/Select/AntdSelectFilter.tsx |  2 +
 .../src/filters/components/Select/types.ts         |  2 +
 7 files changed, 86 insertions(+), 17 deletions(-)

diff --git a/superset-frontend/src/dashboard/components/DashboardBuilder.jsx 
b/superset-frontend/src/dashboard/components/DashboardBuilder.jsx
index 3c764b7..f14c301 100644
--- a/superset-frontend/src/dashboard/components/DashboardBuilder.jsx
+++ b/superset-frontend/src/dashboard/components/DashboardBuilder.jsx
@@ -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];
@@ -290,6 +291,7 @@ class DashboardBuilder extends React.Component {
                 <FilterBar
                   filtersOpen={this.state.dashboardFiltersOpen}
                   toggleFiltersBar={this.toggleDashboardFiltersOpen}
+                  directPathToChild={directPathToChild}
                 />
               </ErrorBoundary>
             </StickyVerticalBar>
diff --git 
a/superset-frontend/src/dashboard/components/nativeFilters/CascadePopover.tsx 
b/superset-frontend/src/dashboard/components/nativeFilters/CascadePopover.tsx
index 2b92370..e12c933 100644
--- 
a/superset-frontend/src/dashboard/components/nativeFilters/CascadePopover.tsx
+++ 
b/superset-frontend/src/dashboard/components/nativeFilters/CascadePopover.tsx
@@ -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';
@@ -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;
 }
@@ -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 => {
@@ -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>
@@ -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}
@@ -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>
                 )}
               </>
diff --git 
a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar.tsx 
b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar.tsx
index a81e5d8..6922070 100644
--- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar.tsx
+++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar.tsx
@@ -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';
@@ -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 {
@@ -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;
@@ -240,6 +250,7 @@ const FilterValue: React.FC<FilterProps> = ({
     url_params: {},
     viz_type: 'filter_select',
     defaultValues: currentValue || defaultValue || [],
+    inputRef,
   });
 
   useEffect(() => {
@@ -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);
 
@@ -308,6 +330,7 @@ export const FilterControl: React.FC<FilterProps> = ({
   filter,
   icon,
   onExtraFormDataChange,
+  directPathToChild,
 }) => {
   const { name = '<undefined>' } = filter;
   return (
@@ -318,6 +341,7 @@ export const FilterControl: React.FC<FilterProps> = ({
       </StyledFilterControlTitleBox>
       <FilterValue
         filter={filter}
+        directPathToChild={directPathToChild}
         onExtraFormDataChange={onExtraFormDataChange}
       />
     </StyledFilterControlContainer>
@@ -326,11 +350,13 @@ 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,
 }) => (
   <>
@@ -338,6 +364,7 @@ export const CascadeFilterControl: 
React.FC<CascadeFilterControlProps> = ({
       <StyledCaretIcon name="caret-down" />
       <FilterControl
         filter={filter}
+        directPathToChild={directPathToChild}
         onExtraFormDataChange={onExtraFormDataChange}
       />
     </StyledFilterControlBox>
@@ -347,6 +374,7 @@ export const CascadeFilterControl: 
React.FC<CascadeFilterControlProps> = ({
         <li key={childFilter.id}>
           <CascadeFilterControl
             filter={childFilter}
+            directPathToChild={directPathToChild}
             onExtraFormDataChange={onExtraFormDataChange}
           />
         </li>
@@ -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 
}>(
     {},
@@ -493,6 +522,7 @@ const FilterBar: React.FC<FiltersBarProps> = ({
               }
               filter={filter}
               onExtraFormDataChange={handleExtraFormDataChange}
+              directPathToChild={directPathToChild}
             />
           ))}
         </FilterControls>
diff --git a/superset-frontend/src/filters/components/Range/AntdRangeFilter.tsx 
b/superset-frontend/src/filters/components/Range/AntdRangeFilter.tsx
index 33c0f83..5fb0471 100644
--- a/superset-frontend/src/filters/components/Range/AntdRangeFilter.tsx
+++ b/superset-frontend/src/filters/components/Range/AntdRangeFilter.tsx
@@ -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;
@@ -50,6 +50,7 @@ export default function AntdRangeFilter(props: 
AntdPluginFilterRangeProps) {
         max={max}
         defaultValue={[min, max]}
         onChange={handleChange}
+        ref={inputRef}
       />
     </Styles>
   );
diff --git a/superset-frontend/src/filters/components/Range/types.ts 
b/superset-frontend/src/filters/components/Range/types.ts
index 63c9fbd..4b23262 100644
--- a/superset-frontend/src/filters/components/Range/types.ts
+++ b/superset-frontend/src/filters/components/Range/types.ts
@@ -21,6 +21,7 @@ import {
   QueryFormData,
   SetExtraFormDataHook,
 } from '@superset-ui/core';
+import { RefObject } from 'react';
 import { AntdPluginFilterStylesProps } from '../types';
 
 interface AntdPluginFilterSelectCustomizeProps {
@@ -36,4 +37,5 @@ export type AntdPluginFilterRangeProps = 
AntdPluginFilterStylesProps & {
   data: DataRecord[];
   formData: PluginFilterRangeQueryFormData;
   setExtraFormData: SetExtraFormDataHook;
+  inputRef: RefObject<any>;
 };
diff --git 
a/superset-frontend/src/filters/components/Select/AntdSelectFilter.tsx 
b/superset-frontend/src/filters/components/Select/AntdSelectFilter.tsx
index c29b6aa..b97312c 100644
--- a/superset-frontend/src/filters/components/Select/AntdSelectFilter.tsx
+++ b/superset-frontend/src/filters/components/Select/AntdSelectFilter.tsx
@@ -41,6 +41,7 @@ export default function AntdPluginFilterSelect(
     multiSelect,
     showSearch,
     inverseSelection,
+    inputRef,
   } = {
     ...DEFAULT_FORM_DATA,
     ...formData,
@@ -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]}`;
diff --git a/superset-frontend/src/filters/components/Select/types.ts 
b/superset-frontend/src/filters/components/Select/types.ts
index bfa3117..d8f554b 100644
--- a/superset-frontend/src/filters/components/Select/types.ts
+++ b/superset-frontend/src/filters/components/Select/types.ts
@@ -21,6 +21,7 @@ import {
   DataRecord,
   SetExtraFormDataHook,
 } from '@superset-ui/core';
+import { RefObject } from 'react';
 import { AntdPluginFilterStylesProps } from '../types';
 
 interface AntdPluginFilterSelectCustomizeProps {
@@ -30,6 +31,7 @@ interface AntdPluginFilterSelectCustomizeProps {
   inverseSelection: boolean;
   multiSelect: boolean;
   showSearch: boolean;
+  inputRef?: RefObject<HTMLInputElement>;
 }
 
 export type AntdPluginFilterSelectQueryFormData = QueryFormData &

Reply via email to