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

graceguo pushed a commit to branch scope-selector-modal-v2
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 78480273dfecb63f6fbaa867f20f6c6f3726beda
Author: Grace <grace....@airbnb.com>
AuthorDate: Tue Nov 5 14:28:12 2019 -0800

    refactory after design review
---
 .../ChartIcon.jsx}                                 |  29 +-
 .../components/filterscope/FilterFieldTree.jsx     |  18 +-
 .../components/filterscope/FilterScopeModal.jsx    |   6 +-
 .../components/filterscope/FilterScopeSelector.jsx | 372 ++++++++++-----------
 .../components/filterscope/FilterScopeTree.jsx     |  15 +-
 .../filterscope/renderFilterFieldTreeNodes.jsx     |  11 +-
 .../filterscope/renderFilterScopeTreeNodes.jsx     |  31 +-
 .../stylesheets/filter-scope-selector.less         |  66 ++--
 .../dashboard/util/buildFilterScopeTreeEntry.js    |  65 ++++
 superset/assets/src/dashboard/util/constants.js    |   3 +
 .../src/dashboard/util/getFilterFieldNodesTree.js  |  21 +-
 .../src/dashboard/util/getFilterScopeNodesTree.js  | 132 ++++----
 .../dashboard/util/getFilterScopeParentNodes.js    |   6 +-
 ...eParentNodes.js => getKeyForFilterScopeTree.js} |  27 +-
 .../src/dashboard/util/getRevertedFilterScope.js   |   8 +-
 .../util/getSelectedChartIdForFilterScopeTree.js   |  53 +++
 16 files changed, 497 insertions(+), 366 deletions(-)

diff --git a/superset/assets/src/dashboard/util/getFilterScopeParentNodes.js 
b/superset/assets/src/components/ChartIcon.jsx
similarity index 61%
copy from superset/assets/src/dashboard/util/getFilterScopeParentNodes.js
copy to superset/assets/src/components/ChartIcon.jsx
index 280661d..d25e453 100644
--- a/superset/assets/src/dashboard/util/getFilterScopeParentNodes.js
+++ b/superset/assets/src/components/ChartIcon.jsx
@@ -16,24 +16,15 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-export default function getFilterScopeParentNodes(nodes, depthLimit = 0) {
-  const parentNodes = [];
-  const traverse = (currentNode, depth) => {
-    if (!currentNode) {
-      return;
-    }
+import React from 'react';
 
-    if (currentNode.children && (depthLimit === 0 || depth < depthLimit)) {
-      parentNodes.push(currentNode.value);
-      currentNode.children.forEach(child => traverse(child, depth + 1));
-    }
-  };
+const ChartIcon = () => (
+  <svg width="18" height="18" viewBox="0 0 18 18" fill="none" 
xmlns="http://www.w3.org/2000/svg";>
+    <rect x="0.5" y="0.5" width="17" height="17" rx="2.5" fill="#EFF9F9" 
stroke="#B3DADC" />
+    <rect x="8" y="4" width="2" height="10" rx="1" fill="#B3DADC" />
+    <rect x="12" y="10" width="2" height="4" rx="1" fill="#B3DADC" />
+    <rect x="4" y="6" width="2" height="8" rx="1" fill="#B3DADC" />
+  </svg>
+);
 
-  if (nodes && nodes.length > 0) {
-    nodes.forEach(node => {
-      traverse(node, 0);
-    });
-  }
-
-  return parentNodes;
-}
+export default ChartIcon;
diff --git 
a/superset/assets/src/dashboard/components/filterscope/FilterFieldTree.jsx 
b/superset/assets/src/dashboard/components/filterscope/FilterFieldTree.jsx
index a1b7581..351f539 100644
--- a/superset/assets/src/dashboard/components/filterscope/FilterFieldTree.jsx
+++ b/superset/assets/src/dashboard/components/filterscope/FilterFieldTree.jsx
@@ -19,6 +19,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import CheckboxTree from 'react-checkbox-tree';
+import { t } from '@superset-ui/translation';
 
 import 'react-checkbox-tree/lib/react-checkbox-tree.css';
 import {
@@ -30,7 +31,7 @@ import renderFilterFieldTreeNodes from 
'./renderFilterFieldTreeNodes';
 import { filterScopeSelectorTreeNodePropShape } from '../../util/propShapes';
 
 const propTypes = {
-  activeKey: PropTypes.string.isRequired,
+  activeKey: PropTypes.string,
   nodes: PropTypes.arrayOf(filterScopeSelectorTreeNodePropShape).isRequired,
   checked: PropTypes.arrayOf(
     PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
@@ -43,21 +44,29 @@ const propTypes = {
   onClick: PropTypes.func.isRequired,
 };
 
+const defaultProps = {
+  activeKey: '',
+};
+
 const FILTER_FIELD_CHECKBOX_TREE_ICONS = {
   check: <CheckboxChecked />,
   uncheck: <CheckboxUnchecked />,
   halfCheck: <CheckboxHalfChecked />,
   expandClose: <span className="rct-icon rct-icon-expand-close" />,
   expandOpen: <span className="rct-icon rct-icon-expand-open" />,
-  expandAll: <span className="rct-icon rct-icon-expand-all" />,
-  collapseAll: <span className="rct-icon rct-icon-collapse-all" />,
+  expandAll: (
+    <span className="rct-icon rct-icon-expand-all">{t('Expand all')}</span>
+  ),
+  collapseAll: (
+    <span className="rct-icon rct-icon-collapse-all">{t('Collapse all')}</span>
+  ),
   parentClose: <span className="rct-icon rct-icon-parent-close" />,
   parentOpen: <span className="rct-icon rct-icon-parent-open" />,
   leaf: <span className="rct-icon rct-icon-leaf" />,
 };
 
 export default function FilterFieldTree({
-  activeKey = '',
+  activeKey,
   nodes = [],
   checked = [],
   expanded = [],
@@ -82,3 +91,4 @@ export default function FilterFieldTree({
 }
 
 FilterFieldTree.propTypes = propTypes;
+FilterFieldTree.defaultProps = defaultProps;
diff --git 
a/superset/assets/src/dashboard/components/filterscope/FilterScopeModal.jsx 
b/superset/assets/src/dashboard/components/filterscope/FilterScopeModal.jsx
index ba2b153..b2a8be8 100644
--- a/superset/assets/src/dashboard/components/filterscope/FilterScopeModal.jsx
+++ b/superset/assets/src/dashboard/components/filterscope/FilterScopeModal.jsx
@@ -31,10 +31,10 @@ export default class FilterScopeModal extends 
React.PureComponent {
     super(props);
 
     this.modal = React.createRef();
-    this.close = this.close.bind(this);
+    this.handleCloseModal = this.handleCloseModal.bind(this);
   }
 
-  close() {
+  handleCloseModal() {
     if (this.modal) {
       this.modal.current.close();
     }
@@ -48,7 +48,7 @@ export default class FilterScopeModal extends 
React.PureComponent {
         triggerNode={this.props.triggerNode}
         modalBody={
           <div className="dashboard-modal filter-scope">
-            <FilterScope onCloseModal={this.close} />
+            <FilterScope onCloseModal={this.handleCloseModal} />
           </div>
         }
       />
diff --git 
a/superset/assets/src/dashboard/components/filterscope/FilterScopeSelector.jsx 
b/superset/assets/src/dashboard/components/filterscope/FilterScopeSelector.jsx
index 855e66d..f20044e 100644
--- 
a/superset/assets/src/dashboard/components/filterscope/FilterScopeSelector.jsx
+++ 
b/superset/assets/src/dashboard/components/filterscope/FilterScopeSelector.jsx
@@ -21,12 +21,14 @@ import PropTypes from 'prop-types';
 import cx from 'classnames';
 import { Button } from 'react-bootstrap';
 import { t } from '@superset-ui/translation';
-import { safeStringify } from '../../../utils/safeStringify';
 
+import buildFilterScopeTreeEntry from '../../util/buildFilterScopeTreeEntry';
 import getFilterScopeNodesTree from '../../util/getFilterScopeNodesTree';
 import getFilterFieldNodesTree from '../../util/getFilterFieldNodesTree';
 import getFilterScopeParentNodes from '../../util/getFilterScopeParentNodes';
 import getCurrentScopeChartIds from '../../util/getCurrentScopeChartIds';
+import getKeyForFilterScopeTree from '../../util/getKeyForFilterScopeTree';
+import getSelectedChartIdForFilterScopeTree from 
'../../util/getSelectedChartIdForFilterScopeTree';
 import getRevertedFilterScope from '../../util/getRevertedFilterScope';
 import FilterScopeTree from './FilterScopeTree';
 import FilterFieldTree from './FilterFieldTree';
@@ -34,6 +36,7 @@ import {
   getChartIdAndColumnFromFilterKey,
   getDashboardFilterKey,
 } from '../../util/getDashboardFilterKey';
+import { ALL_FILTERS } from '../../util/constants';
 
 const propTypes = {
   dashboardFilters: PropTypes.object.isRequired,
@@ -59,26 +62,19 @@ export default class FilterScopeSelector extends 
React.PureComponent {
       // display filter fields in tree structure
       const filterFieldNodes = getFilterFieldNodesTree({
         dashboardFilters,
-        isSingleEditMode: true,
       });
+      // filterFieldNodes root node is dashboard_root component,
+      // so that we can offer a select/deselect all link
+      const filtersNodes = filterFieldNodes[0].children;
       this.allfilterFields = [];
-      filterFieldNodes.forEach(({ children }) => {
+      filtersNodes.forEach(({ children }) => {
         children.forEach(child => {
           this.allfilterFields.push(child.value);
         });
       });
+      this.defaultFilterKey = filtersNodes[0].children[0].value;
 
-      if (filterFieldNodes.length && filterFieldNodes[0].children) {
-        this.defaultFilterKey = filterFieldNodes[0].children[0].value;
-      }
-      const checkedFilterFields = [this.defaultFilterKey];
-      // expand defaultFilterKey
-      const { chartId } = getChartIdAndColumnFromFilterKey(
-        this.defaultFilterKey,
-      );
-      const expandedFilterIds = [chartId];
-
-      // display checkbox tree of whole dashboard layout
+      // build FilterScopeTree object for each filterKey
       const filterScopeMap = Object.values(dashboardFilters).reduce(
         (map, { chartId: filterId, columns }) => {
           const filterScopeByChartId = Object.keys(columns).reduce(
@@ -89,8 +85,7 @@ export default class FilterScopeSelector extends 
React.PureComponent {
               });
               const nodes = getFilterScopeNodesTree({
                 components: layout,
-                isSingleEditMode: true,
-                checkedFilterFields,
+                filterFields: [filterKey],
                 selectedChartId: filterId,
               });
               const checked = getCurrentScopeChartIds({
@@ -107,7 +102,7 @@ export default class FilterScopeSelector extends 
React.PureComponent {
                   // unfiltered nodes
                   nodes,
                   // filtered nodes in display if searchText is not empty
-                  nodesFiltered: nodes.slice(),
+                  nodesFiltered: [...nodes],
                   checked,
                   expanded,
                 },
@@ -124,15 +119,32 @@ export default class FilterScopeSelector extends 
React.PureComponent {
         {},
       );
 
+      // initial state: active defaultFilerKey
+      const { chartId } = getChartIdAndColumnFromFilterKey(
+        this.defaultFilterKey,
+      );
+      const checkedFilterFields = [];
+      const activeFilterField = this.defaultFilterKey;
+      // expand defaultFilterKey in filter field tree
+      const expandedFilterIds = [ALL_FILTERS].concat(chartId);
+
+      const filterScopeTreeEntry = buildFilterScopeTreeEntry({
+        checkedFilterFields,
+        activeFilterField,
+        filterScopeMap,
+        layout,
+      });
       this.state = {
         showSelector: true,
-        activeKey: this.defaultFilterKey,
+        activeFilterField,
         searchText: '',
-        filterScopeMap,
+        filterScopeMap: {
+          ...filterScopeMap,
+          ...filterScopeTreeEntry,
+        },
         filterFieldNodes,
         checkedFilterFields,
         expandedFilterIds,
-        isSingleEditMode: true,
       };
     } else {
       this.state = {
@@ -142,7 +154,6 @@ export default class FilterScopeSelector extends 
React.PureComponent {
 
     this.filterNodes = this.filterNodes.bind(this);
     this.onChangeFilterField = this.onChangeFilterField.bind(this);
-    this.onToggleEditMode = this.onToggleEditMode.bind(this);
     this.onCheckFilterScope = this.onCheckFilterScope.bind(this);
     this.onExpandFilterScope = this.onExpandFilterScope.bind(this);
     this.onSearchInputChange = this.onSearchInputChange.bind(this);
@@ -154,88 +165,76 @@ export default class FilterScopeSelector extends 
React.PureComponent {
 
   onCheckFilterScope(checked = []) {
     const {
-      activeKey,
+      activeFilterField,
       filterScopeMap,
-      isSingleEditMode,
       checkedFilterFields,
     } = this.state;
 
-    if (isSingleEditMode) {
-      const updatedEntry = {
-        ...filterScopeMap[activeKey],
-        checked: checked.map(c => parseInt(c, 10)),
-      };
-
-      this.setState(() => ({
-        filterScopeMap: {
-          ...filterScopeMap,
-          [activeKey]: updatedEntry,
-        },
-      }));
-    } else {
-      // multi edit mode: update every scope in checkedFilterFields based on 
grouped selection
-      const updatedEntry = {
-        ...filterScopeMap[activeKey],
-        checked,
-      };
+    const key = getKeyForFilterScopeTree({
+      activeFilterField,
+      checkedFilterFields,
+    });
+    const editingList = activeFilterField
+      ? [activeFilterField]
+      : checkedFilterFields;
+    const updatedEntry = {
+      ...filterScopeMap[key],
+      checked,
+    };
 
-      const updatedFilterScopeMap = getRevertedFilterScope({
-        checked,
-        checkedFilterFields,
-        filterScopeMap,
-      });
+    const updatedFilterScopeMap = getRevertedFilterScope({
+      checked,
+      filterFields: editingList,
+      filterScopeMap,
+    });
 
-      this.setState(() => ({
-        filterScopeMap: {
-          ...filterScopeMap,
-          ...updatedFilterScopeMap,
-          [activeKey]: updatedEntry,
-        },
-      }));
-    }
+    this.setState(() => ({
+      filterScopeMap: {
+        ...filterScopeMap,
+        ...updatedFilterScopeMap,
+        [key]: updatedEntry,
+      },
+    }));
   }
 
   onExpandFilterScope(expanded = []) {
-    const { activeKey, filterScopeMap } = this.state;
+    const {
+      activeFilterField,
+      checkedFilterFields,
+      filterScopeMap,
+    } = this.state;
+    const key = getKeyForFilterScopeTree({
+      activeFilterField,
+      checkedFilterFields,
+    });
     const updatedEntry = {
-      ...filterScopeMap[activeKey],
+      ...filterScopeMap[key],
       expanded,
     };
     this.setState(() => ({
       filterScopeMap: {
         ...filterScopeMap,
-        [activeKey]: updatedEntry,
+        [key]: updatedEntry,
       },
     }));
   }
 
   onCheckFilterField(checkedFilterFields = []) {
     const { layout } = this.props;
-    const { isSingleEditMode, filterScopeMap } = this.state;
-    const nodes = getFilterScopeNodesTree({
-      components: layout,
-      isSingleEditMode,
+    const { filterScopeMap } = this.state;
+    const filterScopeTreeEntry = buildFilterScopeTreeEntry({
       checkedFilterFields,
-    });
-    const activeKey = safeStringify(checkedFilterFields);
-    const checkedChartIdSet = new Set();
-    checkedFilterFields.forEach(filterField => {
-      (filterScopeMap[filterField].checked || []).forEach(chartId => {
-        checkedChartIdSet.add(`${chartId}:${filterField}`);
-      });
+      activeFilterField: '',
+      filterScopeMap,
+      layout,
     });
 
     this.setState(() => ({
-      activeKey,
+      activeFilterField: '',
       checkedFilterFields,
       filterScopeMap: {
         ...filterScopeMap,
-        [activeKey]: {
-          nodes,
-          nodesFiltered: [...nodes],
-          checked: [...checkedChartIdSet],
-          expanded: getFilterScopeParentNodes(nodes, 1),
-        },
+        ...filterScopeTreeEntry,
       },
     }));
   }
@@ -246,64 +245,58 @@ export default class FilterScopeSelector extends 
React.PureComponent {
     }));
   }
 
-  onSearchInputChange(e) {
-    this.setState({ searchText: e.target.value }, this.filterTree);
-  }
-
   onChangeFilterField(filterField = {}) {
-    const nextActiveKey = filterField.value;
+    const { layout } = this.props;
+    const nextActiveFilterField = filterField.value;
     const {
-      isSingleEditMode,
-      activeKey: currentActiveKey,
+      activeFilterField: currentActiveFilterField,
       checkedFilterFields,
+      filterScopeMap,
     } = this.state;
 
-    // multi-edit mode: if user click on the single filter field,
+    // we allow single edit and multiple edit in the same view.
+    // if user click on the single filter field,
     // will show filter scope for the single field.
     // if user click on the same filter filed again,
-    // will toggle activeKey back to group selected fields
-    if (!isSingleEditMode && nextActiveKey === currentActiveKey) {
-      this.onCheckFilterField(checkedFilterFields);
-    } else if (this.allfilterFields.includes(nextActiveKey)) {
-      this.setState({ activeKey: nextActiveKey });
-    }
-  }
+    // will toggle off the single filter field,
+    // and allow multi-edit all checked filter fields.
+    if (nextActiveFilterField === currentActiveFilterField) {
+      const filterScopeTreeEntry = buildFilterScopeTreeEntry({
+        checkedFilterFields,
+        activeFilterField: '',
+        filterScopeMap,
+        layout,
+      });
 
-  onToggleEditMode() {
-    const { activeKey, isSingleEditMode, checkedFilterFields } = this.state;
-    const { dashboardFilters } = this.props;
-    if (isSingleEditMode) {
-      // single edit => multi edit
-      this.setState(
-        {
-          isSingleEditMode: false,
-          checkedFilterFields: [activeKey],
-          filterFieldNodes: getFilterFieldNodesTree({
-            dashboardFilters,
-            isSingleEditMode: false,
-          }),
+      this.setState({
+        activeFilterField: '',
+        filterScopeMap: {
+          ...filterScopeMap,
+          ...filterScopeTreeEntry,
         },
-        () => this.onCheckFilterField([activeKey]),
-      );
-    } else {
-      // multi edit => single edit
-      const nextActiveKey =
-        checkedFilterFields.length === 0
-          ? this.defaultFilterKey
-          : checkedFilterFields[0];
-
-      this.setState(() => ({
-        isSingleEditMode: true,
-        activeKey: nextActiveKey,
-        checkedFilterFields: [activeKey],
-        filterFieldNodes: getFilterFieldNodesTree({
-          dashboardFilters,
-          isSingleEditMode: true,
-        }),
-      }));
+      });
+    } else if (this.allfilterFields.includes(nextActiveFilterField)) {
+      const filterScopeTreeEntry = buildFilterScopeTreeEntry({
+        checkedFilterFields,
+        activeFilterField: nextActiveFilterField,
+        filterScopeMap,
+        layout,
+      });
+
+      this.setState({
+        activeFilterField: nextActiveFilterField,
+        filterScopeMap: {
+          ...filterScopeMap,
+          ...filterScopeTreeEntry,
+        },
+      });
     }
   }
 
+  onSearchInputChange(e) {
+    this.setState({ searchText: e.target.value }, this.filterTree);
+  }
+
   onClose() {
     this.props.onCloseModal();
   }
@@ -321,40 +314,62 @@ export default class FilterScopeSelector extends 
React.PureComponent {
         {},
       ),
     );
-    this.props.onCloseModal();
+
+    // save do not close modal
   }
 
   filterTree() {
     // Reset nodes back to unfiltered state
     if (!this.state.searchText) {
       this.setState(prevState => {
-        const { activeKey, filterScopeMap } = prevState;
+        const {
+          activeFilterField,
+          checkedFilterFields,
+          filterScopeMap,
+        } = prevState;
+        const key = getKeyForFilterScopeTree({
+          activeFilterField,
+          checkedFilterFields,
+        });
+
         const updatedEntry = {
-          ...filterScopeMap[activeKey],
-          nodesFiltered: filterScopeMap[activeKey].nodes,
+          ...filterScopeMap[key],
+          nodesFiltered: filterScopeMap[key].nodes,
         };
         return {
           filterScopeMap: {
             ...filterScopeMap,
-            [activeKey]: updatedEntry,
+            [key]: updatedEntry,
           },
         };
       });
     } else {
       const updater = prevState => {
-        const { activeKey, filterScopeMap } = prevState;
-        const nodesFiltered = filterScopeMap[activeKey].nodes.reduce(
+        const {
+          activeFilterField,
+          checkedFilterFields,
+          filterScopeMap,
+        } = prevState;
+        const key = getKeyForFilterScopeTree({
+          activeFilterField,
+          checkedFilterFields,
+        });
+
+        const nodesFiltered = filterScopeMap[key].nodes.reduce(
           this.filterNodes,
           [],
         );
+        const expanded = getFilterScopeParentNodes([...nodesFiltered]);
         const updatedEntry = {
-          ...filterScopeMap[activeKey],
+          ...filterScopeMap[key],
           nodesFiltered,
+          expanded,
         };
+
         return {
           filterScopeMap: {
             ...filterScopeMap,
-            [activeKey]: updatedEntry,
+            [key]: updatedEntry,
           },
         };
       };
@@ -382,14 +397,14 @@ export default class FilterScopeSelector extends 
React.PureComponent {
 
   renderFilterFieldList() {
     const {
-      activeKey,
+      activeFilterField,
       filterFieldNodes,
       checkedFilterFields,
       expandedFilterIds,
     } = this.state;
     return (
       <FilterFieldTree
-        activeKey={activeKey}
+        activeKey={activeFilterField}
         nodes={filterFieldNodes}
         checked={checkedFilterFields}
         expanded={expandedFilterIds}
@@ -403,100 +418,85 @@ export default class FilterScopeSelector extends 
React.PureComponent {
   renderFilterScopeTree() {
     const {
       filterScopeMap,
-      activeKey,
-      isSingleEditMode,
+      activeFilterField,
+      checkedFilterFields,
       searchText,
     } = this.state;
 
-    const selectedFilterId = isSingleEditMode
-      ? getChartIdAndColumnFromFilterKey(activeKey).chartId
-      : 0;
+    const key = getKeyForFilterScopeTree({
+      activeFilterField,
+      checkedFilterFields,
+    });
+
+    const selectedChartId = getSelectedChartIdForFilterScopeTree({
+      activeFilterField,
+      checkedFilterFields,
+    });
     return (
       <React.Fragment>
         <input
-          className={cx('filter-text scope-search', {
-            'multi-edit-mode': !isSingleEditMode,
-          })}
+          className="filter-text scope-search multi-edit-mode"
           placeholder={t('Search...')}
           type="text"
           value={searchText}
           onChange={this.onSearchInputChange}
         />
         <FilterScopeTree
-          nodes={filterScopeMap[activeKey].nodesFiltered}
-          checked={filterScopeMap[activeKey].checked}
-          expanded={filterScopeMap[activeKey].expanded}
+          nodes={filterScopeMap[key].nodesFiltered}
+          checked={filterScopeMap[key].checked}
+          expanded={filterScopeMap[key].expanded}
           onCheck={this.onCheckFilterScope}
           onExpand={this.onExpandFilterScope}
           // pass selectedFilterId prop to FilterScopeTree component,
           // to hide checkbox for selected filter field itself
-          selectedFilterId={selectedFilterId}
+          selectedChartId={selectedChartId}
         />
       </React.Fragment>
     );
   }
 
-  renderEditModeControl() {
-    const { isSingleEditMode } = this.state;
-    return (
-      <span
-        role="button"
-        tabIndex="0"
-        className="edit-mode-toggle"
-        onClick={this.onToggleEditMode}
-      >
-        {isSingleEditMode
-          ? t('Edit multiple filters')
-          : t('Edit individual filter')}
-      </span>
-    );
-  }
-
-  render() {
+  renderEditingFiltersName() {
     const { dashboardFilters } = this.props;
-    const { showSelector, isSingleEditMode, activeKey } = this.state;
-    const isSingleValue = activeKey.indexOf('[') === -1;
+    const { activeFilterField, checkedFilterFields } = this.state;
     const currentFilterLabels = []
-      .concat(isSingleValue ? activeKey : JSON.parse(activeKey))
+      .concat(activeFilterField || checkedFilterFields)
       .map(key => {
         const { chartId, column } = getChartIdAndColumnFromFilterKey(key);
         return dashboardFilters[chartId].labels[column] || column;
       });
 
     return (
+      <div className="selected-fields multi-edit-mode">
+        {currentFilterLabels.length === 0 && t('No filters are selected.')}
+        {currentFilterLabels.length === 1 && t('Editing 1 filter: ')}
+        {currentFilterLabels.length > 1 &&
+          t('Batch editing %d filters: ', currentFilterLabels.length)}
+        <span className="selected-scopes">
+          {currentFilterLabels.join(', ')}
+        </span>
+      </div>
+    );
+  }
+
+  render() {
+    const { showSelector } = this.state;
+
+    return (
       <React.Fragment>
         <div className="filter-scope-container">
           <div className="filter-scope-header">
             <h4>{t('Configure filter scopes')}</h4>
-            <div
-              className={cx('selected-fields', {
-                'multi-edit-mode': !isSingleEditMode,
-              })}
-            >
-              {`Batch editing ${currentFilterLabels.length} filters: `}
-              <span className="selected-scopes">
-                {currentFilterLabels.join(', ')}
-              </span>
-            </div>
+            {this.renderEditingFiltersName()}
           </div>
 
           {!showSelector ? (
-            <div>{t('There are no filter in this dashboard')}</div>
+            <div>{t('There are no filters in this dashboard')}</div>
           ) : (
             <div className="filters-scope-selector">
-              <div
-                className={cx('filter-field-pane', {
-                  'multi-edit-mode': !isSingleEditMode,
-                })}
-              >
-                {this.renderEditModeControl()}
+              <div className={cx('filter-field-pane multi-edit-mode')}>
                 {this.renderFilterFieldList()}
               </div>
-              <div
-                className={cx('filter-scope-pane', {
-                  'multi-edit-mode': !isSingleEditMode,
-                })}
-              >
+              <div className="filter-scope-pane multi-edit-mode">
                 {this.renderFilterScopeTree()}
               </div>
             </div>
diff --git 
a/superset/assets/src/dashboard/components/filterscope/FilterScopeTree.jsx 
b/superset/assets/src/dashboard/components/filterscope/FilterScopeTree.jsx
index 08769ba..b713362 100644
--- a/superset/assets/src/dashboard/components/filterscope/FilterScopeTree.jsx
+++ b/superset/assets/src/dashboard/components/filterscope/FilterScopeTree.jsx
@@ -20,6 +20,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import CheckboxTree from 'react-checkbox-tree';
 import 'react-checkbox-tree/lib/react-checkbox-tree.css';
+import { t } from '@superset-ui/translation';
 
 import {
   CheckboxChecked,
@@ -39,7 +40,7 @@ const propTypes = {
   ).isRequired,
   onCheck: PropTypes.func.isRequired,
   onExpand: PropTypes.func.isRequired,
-  selectedFilterId: PropTypes.number.isRequired,
+  selectedChartId: PropTypes.number.isRequired,
 };
 
 const NOOP = () => {};
@@ -50,8 +51,12 @@ const FILTER_SCOPE_CHECKBOX_TREE_ICONS = {
   halfCheck: <CheckboxHalfChecked />,
   expandClose: <span className="rct-icon rct-icon-expand-close" />,
   expandOpen: <span className="rct-icon rct-icon-expand-open" />,
-  expandAll: <span className="rct-icon rct-icon-expand-all" />,
-  collapseAll: <span className="rct-icon rct-icon-collapse-all" />,
+  expandAll: (
+    <span className="rct-icon rct-icon-expand-all">{t('Expand all')}</span>
+  ),
+  collapseAll: (
+    <span className="rct-icon rct-icon-collapse-all">{t('Collapse all')}</span>
+  ),
   parentClose: <span className="rct-icon rct-icon-parent-close" />,
   parentOpen: <span className="rct-icon rct-icon-parent-open" />,
   leaf: <span className="rct-icon rct-icon-leaf" />,
@@ -63,14 +68,14 @@ export default function FilterScopeTree({
   expanded = [],
   onCheck,
   onExpand,
-  selectedFilterId = 0,
+  selectedChartId = 0,
 }) {
   return (
     <CheckboxTree
       showExpandAll
       expandOnClick
       showNodeIcon={false}
-      nodes={renderFilterScopeTreeNodes({ nodes, selectedFilterId })}
+      nodes={renderFilterScopeTreeNodes({ nodes, selectedChartId })}
       checked={checked}
       expanded={expanded}
       onCheck={onCheck}
diff --git 
a/superset/assets/src/dashboard/components/filterscope/renderFilterFieldTreeNodes.jsx
 
b/superset/assets/src/dashboard/components/filterscope/renderFilterFieldTreeNodes.jsx
index ac9e9b1..2cfec5a 100644
--- 
a/superset/assets/src/dashboard/components/filterscope/renderFilterFieldTreeNodes.jsx
+++ 
b/superset/assets/src/dashboard/components/filterscope/renderFilterFieldTreeNodes.jsx
@@ -25,7 +25,9 @@ export default function renderFilterFieldTreeNodes({
   nodes = [],
   activeKey = '',
 }) {
-  return nodes.map(node => ({
+  const root = nodes[0];
+  const allFilterNodes = nodes[0].children;
+  const children = allFilterNodes.map(node => ({
     ...node,
     children: node.children.map(child => {
       const { label, value } = child;
@@ -42,4 +44,11 @@ export default function renderFilterFieldTreeNodes({
       };
     }),
   }));
+
+  return [
+    {
+      ...root,
+      children,
+    },
+  ];
 }
diff --git 
a/superset/assets/src/dashboard/components/filterscope/renderFilterScopeTreeNodes.jsx
 
b/superset/assets/src/dashboard/components/filterscope/renderFilterScopeTreeNodes.jsx
index a4982b7..b08a207 100644
--- 
a/superset/assets/src/dashboard/components/filterscope/renderFilterScopeTreeNodes.jsx
+++ 
b/superset/assets/src/dashboard/components/filterscope/renderFilterScopeTreeNodes.jsx
@@ -18,26 +18,32 @@
  */
 import React from 'react';
 import cx from 'classnames';
-import { t } from '@superset-ui/translation';
 
-import { DASHBOARD_ROOT_TYPE } from '../../util/componentTypes';
+import ChartIcon from '../../../components/ChartIcon';
+import { CHART_TYPE } from '../../util/componentTypes';
 
-function traverse({ currentNode, selectedFilterId }) {
+function traverse({ currentNode, selectedChartId }) {
   if (!currentNode) {
     return null;
   }
 
-  const { label, type, children } = currentNode;
+  const { label, value, type, children } = currentNode;
   if (children && children.length) {
     const updatedChildren = children.map(child =>
-      traverse({ currentNode: child, selectedFilterId }),
+      traverse({ currentNode: child, selectedChartId }),
     );
     return {
       ...currentNode,
       label: (
-        <a className={`filter-scope-type ${type.toLowerCase()}`}>
-          {type !== DASHBOARD_ROOT_TYPE && (
-            <span className="type-indicator">{t(type)}</span>
+        <a
+          className={cx(`filter-scope-type ${type.toLowerCase()}`, {
+            'selected-filter': selectedChartId === value,
+          })}
+        >
+          {type === CHART_TYPE && (
+            <span className="type-indicator">
+              <ChartIcon />
+            </span>
           )}
           {label}
         </a>
@@ -45,17 +51,14 @@ function traverse({ currentNode, selectedFilterId }) {
       children: updatedChildren,
     };
   }
-
-  const { value } = currentNode;
   return {
     ...currentNode,
     label: (
       <a
         className={cx(`filter-scope-type ${type.toLowerCase()}`, {
-          'selected-filter': selectedFilterId === value,
+          'selected-filter': selectedChartId === value,
         })}
       >
-        <span className="type-indicator">{t(type)}</span>
         {label}
       </a>
     ),
@@ -64,7 +67,7 @@ function traverse({ currentNode, selectedFilterId }) {
 
 export default function renderFilterScopeTreeNodes({
   nodes = [],
-  selectedFilterId = 0,
+  selectedChartId = 0,
 }) {
-  return nodes.map(node => traverse({ currentNode: node, selectedFilterId }));
+  return nodes.map(node => traverse({ currentNode: node, selectedChartId }));
 }
diff --git 
a/superset/assets/src/dashboard/stylesheets/filter-scope-selector.less 
b/superset/assets/src/dashboard/stylesheets/filter-scope-selector.less
index cef4083..9f3822b 100644
--- a/superset/assets/src/dashboard/stylesheets/filter-scope-selector.less
+++ b/superset/assets/src/dashboard/stylesheets/filter-scope-selector.less
@@ -42,7 +42,7 @@
 }
 
 .filters-scope-selector {
-  margin: 10px -24px 20px -24px;
+  margin: 10px -24px 20px;
   display: flex;
   flex-direction: row;
   position: relative;
@@ -55,13 +55,16 @@
     text-decoration: none;
   }
 
-  .filter-field-pane .edit-mode-toggle,
   .react-checkbox-tree .rct-icon.rct-icon-expand-all,
   .react-checkbox-tree .rct-icon.rct-icon-collapse-all {
     font-size: 13px;
     font-family: @font-family-sans-serif;
     color: @brand-primary;
 
+    &::before {
+      content: '';
+    }
+
     &:hover {
       text-decoration: underline;
     }
@@ -77,11 +80,6 @@
     padding: 16px 16px 16px 24px;
     border-right: 1px solid #ccc;
 
-    .edit-mode-toggle {
-      position: absolute;
-      right: 24px;
-    }
-
     .filter-container {
       label {
         font-weight: normal;
@@ -90,7 +88,7 @@
     }
 
     .filter-field-item {
-      height: 40px;
+      height: 35px;
       display: flex;
       align-items: center;
       padding: 0 24px;
@@ -105,17 +103,8 @@
     }
 
     .react-checkbox-tree {
-      ol ol {
-        padding: 0;
-      }
-
-      .rct-bare-label {
-        font-weight: bold;
-      }
-
       .rct-text {
-        margin: 8px 0;
-        height: 30px;
+        height: 40px;
       }
     }
   }
@@ -136,12 +125,9 @@
       display: block;
 
       .type-indicator {
-        border: 1px solid @gray-light;
-        border-radius: 4px;
-        padding: 2px 4px;
-        font-size: 10px;
+        position: relative;
+        top: 3px;
         margin-right: 8px;
-        font-weight: 400;
       }
 
       &.chart {
@@ -150,6 +136,21 @@
 
       &.selected-filter {
         padding-left: 28px;
+        position: relative;
+        color: #aaa;
+
+        &::before {
+          content: " ";
+          position: absolute;
+          left: 0;
+          top: 50%;
+          width: 18px;
+          height: 18px;
+          border-radius: 2px;
+          margin-top: -9px;
+          box-shadow: inset 0 0 0 2px #ccc;
+          background: #f2f2f2;
+        }
       }
 
       &.root {
@@ -177,23 +178,10 @@
       }
     }
 
-    .rct-option .rct-icon {
-      &.rct-icon-expand-all {
-        &::before {
-          content: 'Expand all';
-        }
-      }
-
-      &.rct-icon-collapse-all {
-        &::before {
-          content: 'Collapse all';
-        }
-      }
-    }
-
     .rct-options {
       text-align: left;
       margin-left: 0;
+      margin-bottom: 8px;
     }
 
     .rct-text {
@@ -222,10 +210,6 @@
       }
     }
 
-    &.filter-text {
-      display: none;
-    }
-
     .filter-field-item {
       padding: 0 16px 0 50px;
       margin-left: -50px;
diff --git a/superset/assets/src/dashboard/util/buildFilterScopeTreeEntry.js 
b/superset/assets/src/dashboard/util/buildFilterScopeTreeEntry.js
new file mode 100644
index 0000000..318a0f6
--- /dev/null
+++ b/superset/assets/src/dashboard/util/buildFilterScopeTreeEntry.js
@@ -0,0 +1,65 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import getFilterScopeNodesTree from './getFilterScopeNodesTree';
+import getFilterScopeParentNodes from './getFilterScopeParentNodes';
+import getKeyForFilterScopeTree from './getKeyForFilterScopeTree';
+import getSelectedChartIdForFilterScopeTree from 
'./getSelectedChartIdForFilterScopeTree';
+
+export default function buildFilterScopeTreeEntry({
+  checkedFilterFields = [],
+  activeFilterField = '',
+  filterScopeMap = {},
+  layout = {},
+}) {
+  const key = getKeyForFilterScopeTree({
+    checkedFilterFields,
+    activeFilterField,
+  });
+  const editingList = activeFilterField
+    ? [activeFilterField]
+    : checkedFilterFields;
+  const selectedChartId = getSelectedChartIdForFilterScopeTree({
+    checkedFilterFields,
+    activeFilterField,
+  });
+  const nodes = getFilterScopeNodesTree({
+    components: layout,
+    filterFields: editingList,
+    selectedChartId,
+  });
+  const checkedChartIdSet = new Set();
+  editingList.forEach(filterField => {
+    (filterScopeMap[filterField].checked || []).forEach(chartId => {
+      checkedChartIdSet.add(`${chartId}:${filterField}`);
+    });
+  });
+  const checked = [...checkedChartIdSet];
+  const expanded = filterScopeMap[key]
+    ? filterScopeMap[key].expanded
+    : getFilterScopeParentNodes(nodes, 1);
+
+  return {
+    [key]: {
+      nodes,
+      nodesFiltered: [...nodes],
+      checked,
+      expanded,
+    },
+  };
+}
diff --git a/superset/assets/src/dashboard/util/constants.js 
b/superset/assets/src/dashboard/util/constants.js
index e2cbd32..25f9474 100644
--- a/superset/assets/src/dashboard/util/constants.js
+++ b/superset/assets/src/dashboard/util/constants.js
@@ -76,3 +76,6 @@ export const FILTER_INDICATORS_DISPLAY_LENGTH = 3;
 // in-component element types: can be added into
 // directPathToChild, used for in dashboard navigation and focus
 export const IN_COMPONENT_ELEMENT_TYPES = ['LABEL'];
+
+// filter scope selector filter fields pane root id
+export const ALL_FILTERS = 'ALL_FILTERS';
diff --git a/superset/assets/src/dashboard/util/getFilterFieldNodesTree.js 
b/superset/assets/src/dashboard/util/getFilterFieldNodesTree.js
index d82ea88..4ec9817 100644
--- a/superset/assets/src/dashboard/util/getFilterFieldNodesTree.js
+++ b/superset/assets/src/dashboard/util/getFilterFieldNodesTree.js
@@ -16,24 +16,31 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import { t } from '@superset-ui/translation';
+
 import { getDashboardFilterKey } from './getDashboardFilterKey';
+import { ALL_FILTERS } from './constants';
 
-export default function getFilterFieldNodesTree({
-  dashboardFilters = {},
-  isSingleEditMode = true,
-}) {
-  return Object.values(dashboardFilters).map(dashboardFilter => {
+export default function getFilterFieldNodesTree({ dashboardFilters = {} }) {
+  const allFilters = Object.values(dashboardFilters).map(dashboardFilter => {
     const { chartId, filterName, columns, labels } = dashboardFilter;
     const children = Object.keys(columns).map(column => ({
       value: getDashboardFilterKey({ chartId, column }),
       label: labels[column] || column,
-      showCheckbox: !isSingleEditMode,
     }));
     return {
       value: chartId,
       label: filterName,
       children,
-      showCheckbox: !isSingleEditMode,
+      showCheckbox: true,
     };
   });
+
+  return [
+    {
+      value: ALL_FILTERS,
+      label: t('Select/deselect all filters'),
+      children: allFilters,
+    },
+  ];
 }
diff --git a/superset/assets/src/dashboard/util/getFilterScopeNodesTree.js 
b/superset/assets/src/dashboard/util/getFilterScopeNodesTree.js
index 740ac0d..4d4279c 100644
--- a/superset/assets/src/dashboard/util/getFilterScopeNodesTree.js
+++ b/superset/assets/src/dashboard/util/getFilterScopeNodesTree.js
@@ -16,6 +16,8 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+import { isEmpty } from 'lodash';
+
 import { DASHBOARD_ROOT_ID } from './constants';
 import {
   CHART_TYPE,
@@ -25,83 +27,93 @@ import {
 
 const FILTER_SCOPE_CONTAINER_TYPES = [TAB_TYPE, DASHBOARD_ROOT_TYPE];
 
-export default function getFilterScopeNodesTree({
+function traverse({
+  currentNode = {},
   components = {},
-  isSingleEditMode = true,
-  checkedFilterFields = [],
-  selectedChartId,
+  filterFields = [],
+  selectedChartId = 0,
 }) {
-  function traverse(currentNode) {
-    if (!currentNode) {
-      return null;
-    }
-
-    const type = currentNode.type;
-    if (CHART_TYPE === type && currentNode.meta.chartId) {
-      const chartNode = {
-        value: currentNode.meta.chartId,
-        label:
-          currentNode.meta.sliceName || `${type} ${currentNode.meta.chartId}`,
-        type,
-        showCheckbox: selectedChartId !== currentNode.meta.chartId,
-      };
-
-      if (isSingleEditMode) {
-        return chartNode;
-      }
+  if (!currentNode) {
+    return null;
+  }
 
-      return {
-        ...chartNode,
-        children: checkedFilterFields.map(filterField => ({
-          value: `${currentNode.meta.chartId}:${filterField}`,
-          label: `${currentNode.meta.chartId}:${filterField}`,
-          type: 'filter_box',
-          showCheckbox: false,
-        })),
-      };
-    }
+  const type = currentNode.type;
+  if (CHART_TYPE === type && currentNode.meta.chartId) {
+    const chartNode = {
+      value: currentNode.meta.chartId,
+      label:
+        currentNode.meta.sliceName || `${type} ${currentNode.meta.chartId}`,
+      type,
+      showCheckbox: selectedChartId !== currentNode.meta.chartId,
+    };
 
-    let children = [];
-    if (currentNode.children && currentNode.children.length) {
-      currentNode.children.forEach(child => {
-        const childNodeTree = traverse(components[child]);
+    return {
+      ...chartNode,
+      children: filterFields.map(filterField => ({
+        value: `${currentNode.meta.chartId}:${filterField}`,
+        label: `${chartNode.label}`,
+        type: 'filter_box',
+        showCheckbox: false,
+      })),
+    };
+  }
 
-        const childType = components[child].type;
-        if (FILTER_SCOPE_CONTAINER_TYPES.includes(childType)) {
-          children.push(childNodeTree);
-        } else {
-          children = children.concat(childNodeTree);
-        }
+  let children = [];
+  if (currentNode.children && currentNode.children.length) {
+    currentNode.children.forEach(child => {
+      const childNodeTree = traverse({
+        currentNode: components[child],
+        components,
+        filterFields,
+        selectedChartId,
       });
-    }
 
-    if (FILTER_SCOPE_CONTAINER_TYPES.includes(type)) {
-      let label = '';
-      if (type === DASHBOARD_ROOT_TYPE) {
-        label = 'All dashboard';
+      const childType = components[child].type;
+      if (FILTER_SCOPE_CONTAINER_TYPES.includes(childType)) {
+        children.push(childNodeTree);
       } else {
-        label =
-          currentNode.meta && currentNode.meta.text
-            ? currentNode.meta.text
-            : `${type} ${currentNode.id}`;
+        children = children.concat(childNodeTree);
       }
+    });
+  }
 
-      return {
-        value: currentNode.id,
-        label,
-        type,
-        children,
-      };
+  if (FILTER_SCOPE_CONTAINER_TYPES.includes(type)) {
+    let label = '';
+    if (type === DASHBOARD_ROOT_TYPE) {
+      label = 'Select/deselect all charts';
+    } else {
+      label =
+        currentNode.meta && currentNode.meta.text
+          ? currentNode.meta.text
+          : `${type} ${currentNode.id}`;
     }
 
-    return children;
+    return {
+      value: currentNode.id,
+      label,
+      type,
+      children,
+    };
   }
 
-  if (Object.keys(components).length === 0) {
+  return children;
+}
+
+export default function getFilterScopeNodesTree({
+  components = {},
+  filterFields = [],
+  selectedChartId = 0,
+}) {
+  if (isEmpty(components)) {
     return [];
   }
 
-  const root = traverse(components[DASHBOARD_ROOT_ID]);
+  const root = traverse({
+    currentNode: components[DASHBOARD_ROOT_ID],
+    components,
+    filterFields,
+    selectedChartId,
+  });
   return [
     {
       ...root,
diff --git a/superset/assets/src/dashboard/util/getFilterScopeParentNodes.js 
b/superset/assets/src/dashboard/util/getFilterScopeParentNodes.js
index 280661d..8330c64 100644
--- a/superset/assets/src/dashboard/util/getFilterScopeParentNodes.js
+++ b/superset/assets/src/dashboard/util/getFilterScopeParentNodes.js
@@ -16,20 +16,20 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-export default function getFilterScopeParentNodes(nodes, depthLimit = 0) {
+export default function getFilterScopeParentNodes(nodes = [], depthLimit = -1) 
{
   const parentNodes = [];
   const traverse = (currentNode, depth) => {
     if (!currentNode) {
       return;
     }
 
-    if (currentNode.children && (depthLimit === 0 || depth < depthLimit)) {
+    if (currentNode.children && (depthLimit === -1 || depth < depthLimit)) {
       parentNodes.push(currentNode.value);
       currentNode.children.forEach(child => traverse(child, depth + 1));
     }
   };
 
-  if (nodes && nodes.length > 0) {
+  if (nodes.length > 0) {
     nodes.forEach(node => {
       traverse(node, 0);
     });
diff --git a/superset/assets/src/dashboard/util/getFilterScopeParentNodes.js 
b/superset/assets/src/dashboard/util/getKeyForFilterScopeTree.js
similarity index 61%
copy from superset/assets/src/dashboard/util/getFilterScopeParentNodes.js
copy to superset/assets/src/dashboard/util/getKeyForFilterScopeTree.js
index 280661d..e85dd51 100644
--- a/superset/assets/src/dashboard/util/getFilterScopeParentNodes.js
+++ b/superset/assets/src/dashboard/util/getKeyForFilterScopeTree.js
@@ -16,24 +16,13 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-export default function getFilterScopeParentNodes(nodes, depthLimit = 0) {
-  const parentNodes = [];
-  const traverse = (currentNode, depth) => {
-    if (!currentNode) {
-      return;
-    }
+import { safeStringify } from '../../utils/safeStringify';
 
-    if (currentNode.children && (depthLimit === 0 || depth < depthLimit)) {
-      parentNodes.push(currentNode.value);
-      currentNode.children.forEach(child => traverse(child, depth + 1));
-    }
-  };
-
-  if (nodes && nodes.length > 0) {
-    nodes.forEach(node => {
-      traverse(node, 0);
-    });
-  }
-
-  return parentNodes;
+export default function getKeyForFilterScopeTree({
+  activeFilterField,
+  checkedFilterFields,
+}) {
+  return activeFilterField
+    ? safeStringify([activeFilterField])
+    : safeStringify(checkedFilterFields);
 }
diff --git a/superset/assets/src/dashboard/util/getRevertedFilterScope.js 
b/superset/assets/src/dashboard/util/getRevertedFilterScope.js
index f8fe550..92e4a29 100644
--- a/superset/assets/src/dashboard/util/getRevertedFilterScope.js
+++ b/superset/assets/src/dashboard/util/getRevertedFilterScope.js
@@ -17,9 +17,9 @@
  * under the License.
  */
 export default function getRevertedFilterScope({
-  checked,
-  checkedFilterFields,
-  filterScopeMap,
+  checked = [],
+  filterFields = [],
+  filterScopeMap = {},
 }) {
   const checkedChartIdsByFilterField = checked.reduce((map, value) => {
     const [chartId, filterField] = value.split(':');
@@ -29,7 +29,7 @@ export default function getRevertedFilterScope({
     };
   }, {});
 
-  return checkedFilterFields.reduce(
+  return filterFields.reduce(
     (map, filterField) => ({
       ...map,
       [filterField]: {
diff --git 
a/superset/assets/src/dashboard/util/getSelectedChartIdForFilterScopeTree.js 
b/superset/assets/src/dashboard/util/getSelectedChartIdForFilterScopeTree.js
new file mode 100644
index 0000000..257ec2e
--- /dev/null
+++ b/superset/assets/src/dashboard/util/getSelectedChartIdForFilterScopeTree.js
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { getChartIdAndColumnFromFilterKey } from './getDashboardFilterKey';
+
+export default function getSelectedChartIdForFilterScopeTree({
+  activeFilterField,
+  checkedFilterFields,
+}) {
+  // we don't apply filter on filter_box itself, so we will disable
+  // checkbox in filter scope selector.
+  // this function returns chart id based on current filter scope selector 
local state:
+  // 1. if in single-edit mode, return the chart id for selected filter field.
+  // 2. if in multi-edit mode, if all filter fields are from same chart id,
+  // return the single chart id.
+  // otherwise, there is no chart to disable.
+  if (activeFilterField) {
+    return getChartIdAndColumnFromFilterKey(activeFilterField).chartId;
+  }
+
+  if (checkedFilterFields.length) {
+    const { chartId } = getChartIdAndColumnFromFilterKey(
+      checkedFilterFields[0],
+    );
+
+    if (
+      checkedFilterFields.some(
+        filterKey =>
+          getChartIdAndColumnFromFilterKey(filterKey).chartId !== chartId,
+      )
+    ) {
+      return 0;
+    }
+    return chartId;
+  }
+
+  return 0;
+}

Reply via email to