You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by gr...@apache.org on 2019/11/13 06:18:43 UTC
[incubator-superset] 05/12: fix code review comments (round 1)
This is an automated email from the ASF dual-hosted git repository.
graceguo pushed a commit to branch gg-Test-Scoped-Filters
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git
commit 0b048082d7b15f5810a1a02a9dd6a4c6ca7a11a4
Author: Grace <gr...@airbnb.com>
AuthorDate: Tue Oct 29 00:24:27 2019 -0700
fix code review comments (round 1)
---
.../dashboard/fixtures/mockDashboardFilters.js | 5 +-
.../dashboard/fixtures/mockDashboardLayout.js | 2 +-
.../dashboard/reducers/dashboardFilters_spec.js | 21 ++--
superset/assets/src/components/CheckboxChecked.jsx | 28 -----
.../assets/src/components/CheckboxHalfchecked.jsx | 28 -----
superset/assets/src/components/CheckboxIcons.jsx | 40 +++++++
.../assets/src/components/CheckboxUnchecked.jsx | 28 -----
.../components/FilterIndicatorsContainer.jsx | 5 +-
.../components/filterscope/FilterFieldTree.jsx | 53 +++++----
.../components/filterscope/FilterScopeModal.jsx | 13 +--
.../components/filterscope/FilterScopeSelector.jsx | 118 ++++++++++-----------
.../components/filterscope/FilterScopeTree.jsx | 59 ++++++-----
.../filterscope/renderFilterFieldTreeNodes.jsx | 9 +-
.../filterscope/renderFilterScopeTreeNodes.jsx | 64 ++++++-----
.../src/dashboard/containers/FilterScope.jsx | 1 -
.../src/dashboard/reducers/dashboardFilters.js | 14 +++
.../src/dashboard/stylesheets/dashboard.less | 4 +-
.../stylesheets/filter-scope-selector.less | 15 +--
.../src/dashboard/util/activeDashboardFilters.js | 10 +-
.../src/dashboard/util/dashboardFiltersColorMap.js | 2 +-
.../src/dashboard/util/getDashboardFilterKey.js | 8 +-
.../src/dashboard/util/getFilterFieldNodesTree.js | 6 +-
.../src/dashboard/util/getFilterScopeNodesTree.js | 6 +-
.../dashboard/util/getFilterScopeParentNodes.js | 2 +-
superset/assets/src/dashboard/util/propShapes.jsx | 24 +++++
.../src/visualizations/FilterBox/FilterBox.jsx | 2 +-
26 files changed, 294 insertions(+), 273 deletions(-)
diff --git a/superset/assets/spec/javascripts/dashboard/fixtures/mockDashboardFilters.js b/superset/assets/spec/javascripts/dashboard/fixtures/mockDashboardFilters.js
index e72e13e..7f13db7 100644
--- a/superset/assets/spec/javascripts/dashboard/fixtures/mockDashboardFilters.js
+++ b/superset/assets/spec/javascripts/dashboard/fixtures/mockDashboardFilters.js
@@ -17,6 +17,7 @@
* under the License.
*/
import { filterId } from './mockSliceEntities';
+import { DASHBOARD_FILTER_SCOPE_GLOBAL } from '../../../../src/dashboard/reducers/dashboardFilters';
export const emptyFilters = {};
@@ -33,7 +34,9 @@ export const dashboardFilters = {
'ROW-l6PrlhwSjh',
'CHART-rwDfbGqeEn',
],
- scope: 'ROOT_ID',
+ scopes: {
+ region: DASHBOARD_FILTER_SCOPE_GLOBAL,
+ },
isDateFilter: false,
isInstantFilter: true,
columns: {
diff --git a/superset/assets/spec/javascripts/dashboard/fixtures/mockDashboardLayout.js b/superset/assets/spec/javascripts/dashboard/fixtures/mockDashboardLayout.js
index aad4445..3267c5c 100644
--- a/superset/assets/spec/javascripts/dashboard/fixtures/mockDashboardLayout.js
+++ b/superset/assets/spec/javascripts/dashboard/fixtures/mockDashboardLayout.js
@@ -204,6 +204,6 @@ export const filterComponent = {
chartId: filterId,
width: 3,
height: 10,
- chartName: 'Filter',
+ sliceName: 'Filter',
},
};
diff --git a/superset/assets/spec/javascripts/dashboard/reducers/dashboardFilters_spec.js b/superset/assets/spec/javascripts/dashboard/reducers/dashboardFilters_spec.js
index 34a09b5..e234248 100644
--- a/superset/assets/spec/javascripts/dashboard/reducers/dashboardFilters_spec.js
+++ b/superset/assets/spec/javascripts/dashboard/reducers/dashboardFilters_spec.js
@@ -22,7 +22,9 @@ import {
REMOVE_FILTER,
CHANGE_FILTER,
} from '../../../../src/dashboard/actions/dashboardFilters';
-import dashboardFiltersReducer from '../../../../src/dashboard/reducers/dashboardFilters';
+import dashboardFiltersReducer, {
+ DASHBOARD_FILTER_SCOPE_GLOBAL,
+} from '../../../../src/dashboard/reducers/dashboardFilters';
import {
emptyFilters,
dashboardFilters,
@@ -33,10 +35,8 @@ import {
column,
} from '../fixtures/mockSliceEntities';
import { filterComponent } from '../fixtures/mockDashboardLayout';
-import { DASHBOARD_ROOT_ID } from '../../../../src/dashboard/util/constants';
-// disable broken unit tests by now, will fix it in another PR
-xdescribe('dashboardFilters reducer', () => {
+describe('dashboardFilters reducer', () => {
const form_data = sliceEntitiesForDashboard.slices[filterId].form_data;
const component = filterComponent;
const directPathToFilter = (component.parents || []).slice();
@@ -55,7 +55,7 @@ xdescribe('dashboardFilters reducer', () => {
chartId: filterId,
componentId: component.id,
directPathToFilter,
- scope: 'ROOT_ID',
+ filterName: component.meta.sliceName,
isDateFilter: false,
isInstantFilter: !!form_data.instant_filtering,
columns: {
@@ -64,6 +64,9 @@ xdescribe('dashboardFilters reducer', () => {
labels: {
[column]: column,
},
+ scopes: {
+ [column]: DASHBOARD_FILTER_SCOPE_GLOBAL,
+ },
},
});
});
@@ -84,7 +87,6 @@ xdescribe('dashboardFilters reducer', () => {
chartId: filterId,
componentId: component.id,
directPathToFilter,
- scopes: {},
isDateFilter: false,
isInstantFilter: !!form_data.instant_filtering,
columns: {
@@ -94,6 +96,9 @@ xdescribe('dashboardFilters reducer', () => {
labels: {
[column]: column,
},
+ scopes: {
+ [column]: DASHBOARD_FILTER_SCOPE_GLOBAL,
+ },
},
});
});
@@ -114,7 +119,6 @@ xdescribe('dashboardFilters reducer', () => {
chartId: filterId,
componentId: component.id,
directPathToFilter,
- scope: DASHBOARD_ROOT_ID,
isDateFilter: false,
isInstantFilter: !!form_data.instant_filtering,
columns: {
@@ -124,6 +128,9 @@ xdescribe('dashboardFilters reducer', () => {
labels: {
[column]: column,
},
+ scopes: {
+ [column]: DASHBOARD_FILTER_SCOPE_GLOBAL,
+ },
},
});
});
diff --git a/superset/assets/src/components/CheckboxChecked.jsx b/superset/assets/src/components/CheckboxChecked.jsx
deleted file mode 100644
index e88aad0..0000000
--- a/superset/assets/src/components/CheckboxChecked.jsx
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * 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 React from 'react';
-
-export default function CheckboxChecked() {
- return (
- <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
- <path d="M16 0H2C0.89 0 0 0.9 0 2V16C0 17.1 0.89 18 2 18H16C17.11 18 18 17.1 18 16V2C18 0.9 17.11 0 16 0Z" fill="#00A699" />
- <path d="M7 14L2 9L3.41 7.59L7 11.17L14.59 3.58L16 5L7 14Z" fill="white" />
- </svg>
- );
-}
diff --git a/superset/assets/src/components/CheckboxHalfchecked.jsx b/superset/assets/src/components/CheckboxHalfchecked.jsx
deleted file mode 100644
index 7122e7c..0000000
--- a/superset/assets/src/components/CheckboxHalfchecked.jsx
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * 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 React from 'react';
-
-export default function CheckboxHalfchecked() {
- return (
- <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
- <path d="M16 0H2C0.9 0 0 0.9 0 2V16C0 17.1 0.9 18 2 18H16C17.1 18 18 17.1 18 16V2C18 0.9 17.1 0 16 0Z" fill="#999999" />
- <path d="M14 10H4V8H14V10Z" fill="white" />
- </svg>
- );
-}
diff --git a/superset/assets/src/components/CheckboxIcons.jsx b/superset/assets/src/components/CheckboxIcons.jsx
new file mode 100644
index 0000000..f26e752
--- /dev/null
+++ b/superset/assets/src/components/CheckboxIcons.jsx
@@ -0,0 +1,40 @@
+/**
+ * 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 React from 'react';
+
+export const CheckboxChecked = () => (
+ <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <path d="M16 0H2C0.89 0 0 0.9 0 2V16C0 17.1 0.89 18 2 18H16C17.11 18 18 17.1 18 16V2C18 0.9 17.11 0 16 0Z" fill="#00A699" />
+ <path d="M7 14L2 9L3.41 7.59L7 11.17L14.59 3.58L16 5L7 14Z" fill="white" />
+ </svg>
+);
+
+export const CheckboxHalfChecked = () => (
+ <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <path d="M16 0H2C0.9 0 0 0.9 0 2V16C0 17.1 0.9 18 2 18H16C17.1 18 18 17.1 18 16V2C18 0.9 17.1 0 16 0Z" fill="#999999" />
+ <path d="M14 10H4V8H14V10Z" fill="white" />
+ </svg>
+);
+
+export const CheckboxUnchecked = () => (
+ <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+ <path d="M16 0H2C0.9 0 0 0.9 0 2V16C0 17.1 0.9 18 2 18H16C17.1 18 18 17.1 18 16V2C18 0.9 17.1 0 16 0Z" fill="#CCCCCC" />
+ <path d="M16 2V16H2V2H16V2Z" fill="white" />
+ </svg>
+);
diff --git a/superset/assets/src/components/CheckboxUnchecked.jsx b/superset/assets/src/components/CheckboxUnchecked.jsx
deleted file mode 100644
index 0153789..0000000
--- a/superset/assets/src/components/CheckboxUnchecked.jsx
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * 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 React from 'react';
-
-export default function CheckboxUnchecked() {
- return (
- <svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
- <path d="M16 0H2C0.9 0 0 0.9 0 2V16C0 17.1 0.9 18 2 18H16C17.1 18 18 17.1 18 16V2C18 0.9 17.1 0 16 0Z" fill="#CCCCCC" />
- <path d="M16 2V16H2V2H16V2Z" fill="white" />
- </svg>
- );
-}
diff --git a/superset/assets/src/dashboard/components/FilterIndicatorsContainer.jsx b/superset/assets/src/dashboard/components/FilterIndicatorsContainer.jsx
index 6c8b6cf..f287108 100644
--- a/superset/assets/src/dashboard/components/FilterIndicatorsContainer.jsx
+++ b/superset/assets/src/dashboard/components/FilterIndicatorsContainer.jsx
@@ -90,7 +90,10 @@ export default class FilterIndicatorsContainer extends React.PureComponent {
!filterImmuneSlices.includes(currentChartId)
) {
Object.keys(columns).forEach(name => {
- const colorMapKey = getDashboardFilterKey(chartId, name);
+ const colorMapKey = getDashboardFilterKey({
+ chartId,
+ column: name,
+ });
const directPathToLabel = directPathToFilter.slice();
directPathToLabel.push(`LABEL-${name}`);
const indicator = {
diff --git a/superset/assets/src/dashboard/components/filterscope/FilterFieldTree.jsx b/superset/assets/src/dashboard/components/filterscope/FilterFieldTree.jsx
index dd76e96..a1b7581 100644
--- a/superset/assets/src/dashboard/components/filterscope/FilterFieldTree.jsx
+++ b/superset/assets/src/dashboard/components/filterscope/FilterFieldTree.jsx
@@ -21,26 +21,46 @@ import PropTypes from 'prop-types';
import CheckboxTree from 'react-checkbox-tree';
import 'react-checkbox-tree/lib/react-checkbox-tree.css';
-import CheckboxChecked from '../../../components/CheckboxChecked';
-import CheckboxUnchecked from '../../../components/CheckboxUnchecked';
-import CheckboxHalfchecked from '../../../components/CheckboxHalfchecked';
+import {
+ CheckboxChecked,
+ CheckboxUnchecked,
+ CheckboxHalfChecked,
+} from '../../../components/CheckboxIcons';
import renderFilterFieldTreeNodes from './renderFilterFieldTreeNodes';
+import { filterScopeSelectorTreeNodePropShape } from '../../util/propShapes';
const propTypes = {
activeKey: PropTypes.string.isRequired,
- nodes: PropTypes.arrayOf(PropTypes.object).isRequired,
- checked: PropTypes.arrayOf(PropTypes.string).isRequired,
- expanded: PropTypes.arrayOf(PropTypes.string).isRequired,
+ nodes: PropTypes.arrayOf(filterScopeSelectorTreeNodePropShape).isRequired,
+ checked: PropTypes.arrayOf(
+ PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
+ ).isRequired,
+ expanded: PropTypes.arrayOf(
+ PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
+ ).isRequired,
onCheck: PropTypes.func.isRequired,
onExpand: PropTypes.func.isRequired,
onClick: PropTypes.func.isRequired,
};
+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" />,
+ 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,
- nodes,
- checked,
- expanded,
+ activeKey = '',
+ nodes = [],
+ checked = [],
+ expanded = [],
onClick,
onCheck,
onExpand,
@@ -56,18 +76,7 @@ export default function FilterFieldTree({
onClick={onClick}
onCheck={onCheck}
onExpand={onExpand}
- 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" />,
- 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" />,
- }}
+ icons={FILTER_FIELD_CHECKBOX_TREE_ICONS}
/>
);
}
diff --git a/superset/assets/src/dashboard/components/filterscope/FilterScopeModal.jsx b/superset/assets/src/dashboard/components/filterscope/FilterScopeModal.jsx
index bc20185..ba2b153 100644
--- a/superset/assets/src/dashboard/components/filterscope/FilterScopeModal.jsx
+++ b/superset/assets/src/dashboard/components/filterscope/FilterScopeModal.jsx
@@ -30,24 +30,21 @@ export default class FilterScopeModal extends React.PureComponent {
constructor(props) {
super(props);
- this.modal = null;
+ this.modal = React.createRef();
this.close = this.close.bind(this);
- this.setModalRef = this.setModalRef.bind(this);
- }
-
- setModalRef(ref) {
- this.modal = ref;
}
close() {
- this.modal.close();
+ if (this.modal) {
+ this.modal.current.close();
+ }
}
render() {
return (
<ModalTrigger
dialogClassName="filter-scope-modal"
- ref={this.setModalRef}
+ ref={this.modal}
triggerNode={this.props.triggerNode}
modalBody={
<div className="dashboard-modal filter-scope">
diff --git a/superset/assets/src/dashboard/components/filterscope/FilterScopeSelector.jsx b/superset/assets/src/dashboard/components/filterscope/FilterScopeSelector.jsx
index 79256ac..855e66d 100644
--- a/superset/assets/src/dashboard/components/filterscope/FilterScopeSelector.jsx
+++ b/superset/assets/src/dashboard/components/filterscope/FilterScopeSelector.jsx
@@ -31,7 +31,7 @@ import getRevertedFilterScope from '../../util/getRevertedFilterScope';
import FilterScopeTree from './FilterScopeTree';
import FilterFieldTree from './FilterFieldTree';
import {
- getDashboardFilterByKey,
+ getChartIdAndColumnFromFilterKey,
getDashboardFilterKey,
} from '../../util/getDashboardFilterKey';
@@ -40,7 +40,6 @@ const propTypes = {
layout: PropTypes.object.isRequired,
filterImmuneSlices: PropTypes.arrayOf(PropTypes.number).isRequired,
filterImmuneSliceFields: PropTypes.object.isRequired,
-
setDirectPathToChild: PropTypes.func.isRequired,
onCloseModal: PropTypes.func.isRequired,
};
@@ -69,12 +68,14 @@ export default class FilterScopeSelector extends React.PureComponent {
});
});
- this.defaultFilterKey = Object.keys(filterFieldNodes).length
- ? filterFieldNodes[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] = getDashboardFilterByKey(this.defaultFilterKey);
+ const { chartId } = getChartIdAndColumnFromFilterKey(
+ this.defaultFilterKey,
+ );
const expandedFilterIds = [chartId];
// display checkbox tree of whole dashboard layout
@@ -82,13 +83,23 @@ export default class FilterScopeSelector extends React.PureComponent {
(map, { chartId: filterId, columns }) => {
const filterScopeByChartId = Object.keys(columns).reduce(
(mapByChartId, columnName) => {
- const filterKey = getDashboardFilterKey(filterId, columnName);
+ const filterKey = getDashboardFilterKey({
+ chartId: filterId,
+ column: columnName,
+ });
const nodes = getFilterScopeNodesTree({
components: layout,
isSingleEditMode: true,
checkedFilterFields,
selectedChartId: filterId,
});
+ const checked = getCurrentScopeChartIds({
+ scopeComponentIds: ['ROOT_ID'], // dashboardFilters[chartId].scopes[columnName],
+ filterField: columnName,
+ filterImmuneSlices,
+ filterImmuneSliceFields,
+ components: layout,
+ });
const expanded = getFilterScopeParentNodes(nodes, 1);
return {
...mapByChartId,
@@ -97,13 +108,7 @@ export default class FilterScopeSelector extends React.PureComponent {
nodes,
// filtered nodes in display if searchText is not empty
nodesFiltered: nodes.slice(),
- checked: getCurrentScopeChartIds({
- scopeComponentIds: ['ROOT_ID'], // dashboardFilters[chartId].scopes[columnName],
- filterField: columnName,
- filterImmuneSlices,
- filterImmuneSliceFields,
- components: layout,
- }),
+ checked,
expanded,
},
};
@@ -141,14 +146,13 @@ export default class FilterScopeSelector extends React.PureComponent {
this.onCheckFilterScope = this.onCheckFilterScope.bind(this);
this.onExpandFilterScope = this.onExpandFilterScope.bind(this);
this.onSearchInputChange = this.onSearchInputChange.bind(this);
- this.onClickFilterField = this.onClickFilterField.bind(this);
this.onCheckFilterField = this.onCheckFilterField.bind(this);
this.onExpandFilterField = this.onExpandFilterField.bind(this);
this.onClose = this.onClose.bind(this);
this.onSave = this.onSave.bind(this);
}
- onCheckFilterScope(checked) {
+ onCheckFilterScope(checked = []) {
const {
activeKey,
filterScopeMap,
@@ -159,7 +163,7 @@ export default class FilterScopeSelector extends React.PureComponent {
if (isSingleEditMode) {
const updatedEntry = {
...filterScopeMap[activeKey],
- checked: checked.map(c => JSON.parse(c)),
+ checked: checked.map(c => parseInt(c, 10)),
};
this.setState(() => ({
@@ -191,7 +195,7 @@ export default class FilterScopeSelector extends React.PureComponent {
}
}
- onExpandFilterScope(expanded) {
+ onExpandFilterScope(expanded = []) {
const { activeKey, filterScopeMap } = this.state;
const updatedEntry = {
...filterScopeMap[activeKey],
@@ -205,11 +209,7 @@ export default class FilterScopeSelector extends React.PureComponent {
}));
}
- onClickFilterField(filterField) {
- this.onChangeFilterField(filterField.value);
- }
-
- onCheckFilterField(checkedFilterFields) {
+ onCheckFilterField(checkedFilterFields = []) {
const { layout } = this.props;
const { isSingleEditMode, filterScopeMap } = this.state;
const nodes = getFilterScopeNodesTree({
@@ -232,7 +232,7 @@ export default class FilterScopeSelector extends React.PureComponent {
...filterScopeMap,
[activeKey]: {
nodes,
- nodesFiltered: nodes.slice(),
+ nodesFiltered: [...nodes],
checked: [...checkedChartIdSet],
expanded: getFilterScopeParentNodes(nodes, 1),
},
@@ -240,7 +240,7 @@ export default class FilterScopeSelector extends React.PureComponent {
}));
}
- onExpandFilterField(expandedFilterIds) {
+ onExpandFilterField(expandedFilterIds = []) {
this.setState(() => ({
expandedFilterIds,
}));
@@ -250,7 +250,8 @@ export default class FilterScopeSelector extends React.PureComponent {
this.setState({ searchText: e.target.value }, this.filterTree);
}
- onChangeFilterField(nextActiveKey) {
+ onChangeFilterField(filterField = {}) {
+ const nextActiveKey = filterField.value;
const {
isSingleEditMode,
activeKey: currentActiveKey,
@@ -339,32 +340,30 @@ export default class FilterScopeSelector extends React.PureComponent {
},
};
});
-
- return;
- }
-
- const updater = prevState => {
- const { activeKey, filterScopeMap } = prevState;
- const nodesFiltered = filterScopeMap[activeKey].nodes.reduce(
- this.filterNodes,
- [],
- );
- const updatedEntry = {
- ...filterScopeMap[activeKey],
- nodesFiltered,
- };
- return {
- filterScopeMap: {
- ...filterScopeMap,
- [activeKey]: updatedEntry,
- },
+ } else {
+ const updater = prevState => {
+ const { activeKey, filterScopeMap } = prevState;
+ const nodesFiltered = filterScopeMap[activeKey].nodes.reduce(
+ this.filterNodes,
+ [],
+ );
+ const updatedEntry = {
+ ...filterScopeMap[activeKey],
+ nodesFiltered,
+ };
+ return {
+ filterScopeMap: {
+ ...filterScopeMap,
+ [activeKey]: updatedEntry,
+ },
+ };
};
- };
- this.setState(updater);
+ this.setState(updater);
+ }
}
- filterNodes(filtered, node) {
+ filterNodes(filtered = [], node = {}) {
const { searchText } = this.state;
const children = (node.children || []).reduce(this.filterNodes, []);
@@ -394,7 +393,7 @@ export default class FilterScopeSelector extends React.PureComponent {
nodes={filterFieldNodes}
checked={checkedFilterFields}
expanded={expandedFilterIds}
- onClick={this.onClickFilterField}
+ onClick={this.onChangeFilterField}
onCheck={this.onCheckFilterField}
onExpand={this.onExpandFilterField}
/>
@@ -409,16 +408,16 @@ export default class FilterScopeSelector extends React.PureComponent {
searchText,
} = this.state;
- const [chartId] = isSingleEditMode
- ? getDashboardFilterByKey(activeKey)
- : [0];
+ const selectedFilterId = isSingleEditMode
+ ? getChartIdAndColumnFromFilterKey(activeKey).chartId
+ : 0;
return (
<React.Fragment>
<input
className={cx('filter-text scope-search', {
'multi-edit-mode': !isSingleEditMode,
})}
- placeholder="Search..."
+ placeholder={t('Search...')}
type="text"
value={searchText}
onChange={this.onSearchInputChange}
@@ -429,8 +428,9 @@ export default class FilterScopeSelector extends React.PureComponent {
expanded={filterScopeMap[activeKey].expanded}
onCheck={this.onCheckFilterScope}
onExpand={this.onExpandFilterScope}
- // hide checkbox for selected filter field itself
- selectedFilterId={chartId}
+ // pass selectedFilterId prop to FilterScopeTree component,
+ // to hide checkbox for selected filter field itself
+ selectedFilterId={selectedFilterId}
/>
</React.Fragment>
);
@@ -459,7 +459,7 @@ export default class FilterScopeSelector extends React.PureComponent {
const currentFilterLabels = []
.concat(isSingleValue ? activeKey : JSON.parse(activeKey))
.map(key => {
- const [chartId, column] = getDashboardFilterByKey(key);
+ const { chartId, column } = getChartIdAndColumnFromFilterKey(key);
return dashboardFilters[chartId].labels[column] || column;
});
@@ -480,9 +480,9 @@ export default class FilterScopeSelector extends React.PureComponent {
</div>
</div>
- {!showSelector && <div>There is no filter in this dashboard</div>}
-
- {showSelector && (
+ {!showSelector ? (
+ <div>{t('There are no filter in this dashboard')}</div>
+ ) : (
<div className="filters-scope-selector">
<div
className={cx('filter-field-pane', {
diff --git a/superset/assets/src/dashboard/components/filterscope/FilterScopeTree.jsx b/superset/assets/src/dashboard/components/filterscope/FilterScopeTree.jsx
index 9e869a5..08769ba 100644
--- a/superset/assets/src/dashboard/components/filterscope/FilterScopeTree.jsx
+++ b/superset/assets/src/dashboard/components/filterscope/FilterScopeTree.jsx
@@ -21,51 +21,62 @@ import PropTypes from 'prop-types';
import CheckboxTree from 'react-checkbox-tree';
import 'react-checkbox-tree/lib/react-checkbox-tree.css';
-import CheckboxChecked from '../../../components/CheckboxChecked';
-import CheckboxUnchecked from '../../../components/CheckboxUnchecked';
-import CheckboxHalfchecked from '../../../components/CheckboxHalfchecked';
+import {
+ CheckboxChecked,
+ CheckboxUnchecked,
+ CheckboxHalfChecked,
+} from '../../../components/CheckboxIcons';
import renderFilterScopeTreeNodes from './renderFilterScopeTreeNodes';
+import { filterScopeSelectorTreeNodePropShape } from '../../util/propShapes';
const propTypes = {
- nodes: PropTypes.arrayOf(PropTypes.object).isRequired,
- checked: PropTypes.arrayOf(PropTypes.string).isRequired,
- expanded: PropTypes.arrayOf(PropTypes.string).isRequired,
+ nodes: PropTypes.arrayOf(filterScopeSelectorTreeNodePropShape).isRequired,
+ checked: PropTypes.arrayOf(
+ PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
+ ).isRequired,
+ expanded: PropTypes.arrayOf(
+ PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
+ ).isRequired,
onCheck: PropTypes.func.isRequired,
onExpand: PropTypes.func.isRequired,
selectedFilterId: PropTypes.number.isRequired,
};
+const NOOP = () => {};
+
+const FILTER_SCOPE_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" />,
+ 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 FilterScopeTree({
- nodes,
- checked,
- expanded,
+ nodes = [],
+ checked = [],
+ expanded = [],
onCheck,
onExpand,
- selectedFilterId,
+ selectedFilterId = 0,
}) {
return (
<CheckboxTree
showExpandAll
expandOnClick
showNodeIcon={false}
- nodes={renderFilterScopeTreeNodes(nodes, selectedFilterId)}
+ nodes={renderFilterScopeTreeNodes({ nodes, selectedFilterId })}
checked={checked}
expanded={expanded}
onCheck={onCheck}
onExpand={onExpand}
- onClick={() => {}}
- 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" />,
- 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" />,
- }}
+ onClick={NOOP}
+ icons={FILTER_SCOPE_CHECKBOX_TREE_ICONS}
/>
);
}
diff --git a/superset/assets/src/dashboard/components/filterscope/renderFilterFieldTreeNodes.jsx b/superset/assets/src/dashboard/components/filterscope/renderFilterFieldTreeNodes.jsx
index b3866a9..ac9e9b1 100644
--- a/superset/assets/src/dashboard/components/filterscope/renderFilterFieldTreeNodes.jsx
+++ b/superset/assets/src/dashboard/components/filterscope/renderFilterFieldTreeNodes.jsx
@@ -21,11 +21,10 @@ import React from 'react';
import FilterFieldItem from './FilterFieldItem';
import { getFilterColorMap } from '../../util/dashboardFiltersColorMap';
-export default function renderFilterFieldTreeNodes({ nodes, activeKey }) {
- if (nodes.length === 0) {
- return [];
- }
-
+export default function renderFilterFieldTreeNodes({
+ nodes = [],
+ activeKey = '',
+}) {
return nodes.map(node => ({
...node,
children: node.children.map(child => {
diff --git a/superset/assets/src/dashboard/components/filterscope/renderFilterScopeTreeNodes.jsx b/superset/assets/src/dashboard/components/filterscope/renderFilterScopeTreeNodes.jsx
index 5d0fbf1..a4982b7 100644
--- a/superset/assets/src/dashboard/components/filterscope/renderFilterScopeTreeNodes.jsx
+++ b/superset/assets/src/dashboard/components/filterscope/renderFilterScopeTreeNodes.jsx
@@ -18,43 +18,53 @@
*/
import React from 'react';
import cx from 'classnames';
+import { t } from '@superset-ui/translation';
-export default function renderFilterScopeTreeNodes(nodes, selectedFilterId) {
- if (nodes.length === 0) {
- return [];
- }
-
- function traverse(currentNode) {
- if (!currentNode) {
- return null;
- }
+import { DASHBOARD_ROOT_TYPE } from '../../util/componentTypes';
- const { label, type, children } = currentNode;
- if (children && children.length) {
- const updatedChildren = children.map(child => traverse(child));
- return {
- ...currentNode,
- label: (
- <a className={`filter-scope-type ${type.toLowerCase()}`}>{label}</a>
- ),
- children: updatedChildren,
- };
- }
+function traverse({ currentNode, selectedFilterId }) {
+ if (!currentNode) {
+ return null;
+ }
- const { value } = currentNode;
+ const { label, type, children } = currentNode;
+ if (children && children.length) {
+ const updatedChildren = children.map(child =>
+ traverse({ currentNode: child, selectedFilterId }),
+ );
return {
...currentNode,
label: (
- <a
- className={cx(`filter-scope-type ${type.toLowerCase()}`, {
- 'selected-filter': selectedFilterId === value,
- })}
- >
+ <a className={`filter-scope-type ${type.toLowerCase()}`}>
+ {type !== DASHBOARD_ROOT_TYPE && (
+ <span className="type-indicator">{t(type)}</span>
+ )}
{label}
</a>
),
+ children: updatedChildren,
};
}
- return nodes.map(node => traverse(node));
+ const { value } = currentNode;
+ return {
+ ...currentNode,
+ label: (
+ <a
+ className={cx(`filter-scope-type ${type.toLowerCase()}`, {
+ 'selected-filter': selectedFilterId === value,
+ })}
+ >
+ <span className="type-indicator">{t(type)}</span>
+ {label}
+ </a>
+ ),
+ };
+}
+
+export default function renderFilterScopeTreeNodes({
+ nodes = [],
+ selectedFilterId = 0,
+}) {
+ return nodes.map(node => traverse({ currentNode: node, selectedFilterId }));
}
diff --git a/superset/assets/src/dashboard/containers/FilterScope.jsx b/superset/assets/src/dashboard/containers/FilterScope.jsx
index 6758ce5..f88d665 100644
--- a/superset/assets/src/dashboard/containers/FilterScope.jsx
+++ b/superset/assets/src/dashboard/containers/FilterScope.jsx
@@ -29,7 +29,6 @@ function mapStateToProps({ dashboardLayout, dashboardFilters, dashboardInfo }) {
filterImmuneSliceFields:
dashboardInfo.metadata.filterImmuneSliceFields || {},
layout: dashboardLayout.present,
- // closeModal: ownProps.onCloseModal,
};
}
diff --git a/superset/assets/src/dashboard/reducers/dashboardFilters.js b/superset/assets/src/dashboard/reducers/dashboardFilters.js
index 1525462..224b50b 100644
--- a/superset/assets/src/dashboard/reducers/dashboardFilters.js
+++ b/superset/assets/src/dashboard/reducers/dashboardFilters.js
@@ -27,6 +27,12 @@ import { TIME_RANGE } from '../../visualizations/FilterBox/FilterBox';
import getFilterConfigsFromFormdata from '../util/getFilterConfigsFromFormdata';
import { buildFilterColorMap } from '../util/dashboardFiltersColorMap';
import { buildActiveFilters } from '../util/activeDashboardFilters';
+import { DASHBOARD_ROOT_ID } from '../util/constants';
+
+export const DASHBOARD_FILTER_SCOPE_GLOBAL = {
+ scope: [DASHBOARD_ROOT_ID],
+ immune: [],
+};
export const dashboardFilter = {
chartId: 0,
@@ -45,6 +51,13 @@ export default function dashboardFiltersReducer(dashboardFilters = {}, action) {
[ADD_FILTER]() {
const { chartId, component, form_data } = action;
const { columns, labels } = getFilterConfigsFromFormdata(form_data);
+ const scopes = Object.keys(columns).reduce(
+ (map, column) => ({
+ ...map,
+ [column]: DASHBOARD_FILTER_SCOPE_GLOBAL,
+ }),
+ {},
+ );
const directPathToFilter = component
? (component.parents || []).slice().concat(component.id)
: [];
@@ -57,6 +70,7 @@ export default function dashboardFiltersReducer(dashboardFilters = {}, action) {
directPathToFilter,
columns,
labels,
+ scopes,
isInstantFilter: !!form_data.instant_filtering,
isDateFilter: Object.keys(columns).includes(TIME_RANGE),
};
diff --git a/superset/assets/src/dashboard/stylesheets/dashboard.less b/superset/assets/src/dashboard/stylesheets/dashboard.less
index fd9288d..cdf11f7 100644
--- a/superset/assets/src/dashboard/stylesheets/dashboard.less
+++ b/superset/assets/src/dashboard/stylesheets/dashboard.less
@@ -183,8 +183,8 @@ body {
.dashboard-modal.delete {
.btn.btn-primary {
- background: @pink !important;
- border-color: @pink !important;
+ background: @pink;
+ border-color: @pink;
}
}
}
diff --git a/superset/assets/src/dashboard/stylesheets/filter-scope-selector.less b/superset/assets/src/dashboard/stylesheets/filter-scope-selector.less
index d57d6de..cef4083 100644
--- a/superset/assets/src/dashboard/stylesheets/filter-scope-selector.less
+++ b/superset/assets/src/dashboard/stylesheets/filter-scope-selector.less
@@ -19,7 +19,6 @@
@import "../../../stylesheets/less/cosmo/variables.less";
.filter-scope-container {
- font-family: @font-family-sans-serif;
font-size: 14px;
.nav.nav-tabs {
@@ -116,6 +115,7 @@
.rct-text {
margin: 8px 0;
+ height: 30px;
}
}
}
@@ -135,7 +135,7 @@
padding: 8px 0;
display: block;
- &::before {
+ .type-indicator {
border: 1px solid @gray-light;
border-radius: 4px;
padding: 2px 4px;
@@ -146,9 +146,6 @@
&.chart {
font-weight: normal;
- &::before {
- content: 'Chart';
- }
}
&.selected-filter {
@@ -161,10 +158,6 @@
&.tab {
font-weight: 700;
-
- &::before {
- content: 'Tab';
- }
}
}
@@ -213,7 +206,7 @@
font-weight: bold;
}
- // disable style from react-checkbox-tress.css
+ // disable style from react-checkbox-trees.css
.rct-node-clickable:hover,
.rct-node-clickable:focus,
label:hover,
@@ -234,7 +227,7 @@
}
.filter-field-item {
- padding: 0 50px;
+ padding: 0 16px 0 50px;
margin-left: -50px;
&.is-selected {
diff --git a/superset/assets/src/dashboard/util/activeDashboardFilters.js b/superset/assets/src/dashboard/util/activeDashboardFilters.js
index 3b20ea3..8fa00d0 100644
--- a/superset/assets/src/dashboard/util/activeDashboardFilters.js
+++ b/superset/assets/src/dashboard/util/activeDashboardFilters.js
@@ -17,7 +17,7 @@
* under the License.
*/
let activeFilters = {};
-let allFilterIds = [];
+let allFilterBoxChartIds = [];
export function getActiveFilters() {
return activeFilters;
@@ -28,17 +28,17 @@ export function getActiveFilters() {
// after we make filterbox a dashboard build-in component,
// will not need this check anymore
export function isFilterBox(chartId) {
- return allFilterIds.includes(chartId);
+ return allFilterBoxChartIds.includes(chartId);
}
-export function getAllFilterIds() {
- return allFilterIds;
+export function getAllFilterBoxChartIds() {
+ return allFilterBoxChartIds;
}
// non-empty filters from dashboardFilters,
// this function does not take into account: filter immune or filter scope settings
export function buildActiveFilters(allDashboardFilters = {}) {
- allFilterIds = Object.values(allDashboardFilters).map(
+ allFilterBoxChartIds = Object.values(allDashboardFilters).map(
filter => filter.chartId,
);
diff --git a/superset/assets/src/dashboard/util/dashboardFiltersColorMap.js b/superset/assets/src/dashboard/util/dashboardFiltersColorMap.js
index 55e0b72..129acf5 100644
--- a/superset/assets/src/dashboard/util/dashboardFiltersColorMap.js
+++ b/superset/assets/src/dashboard/util/dashboardFiltersColorMap.js
@@ -36,7 +36,7 @@ export function buildFilterColorMap(allDashboardFilters = {}) {
Object.keys(columns)
.sort()
.forEach(column => {
- const key = getDashboardFilterKey(chartId, column);
+ const key = getDashboardFilterKey({ chartId, column });
const colorCode = `badge-${filterColorIndex % FILTER_COLORS_COUNT}`;
/* eslint-disable no-param-reassign */
colorMap[key] = colorCode;
diff --git a/superset/assets/src/dashboard/util/getDashboardFilterKey.js b/superset/assets/src/dashboard/util/getDashboardFilterKey.js
index aa65559..e6307c3 100644
--- a/superset/assets/src/dashboard/util/getDashboardFilterKey.js
+++ b/superset/assets/src/dashboard/util/getDashboardFilterKey.js
@@ -16,12 +16,12 @@
* specific language governing permissions and limitations
* under the License.
*/
-export function getDashboardFilterKey(chartId, column) {
+export function getDashboardFilterKey({ chartId, column }) {
return `${chartId}_${column}`;
}
-export function getDashboardFilterByKey(key) {
+export function getChartIdAndColumnFromFilterKey(key) {
const [chartId, ...parts] = key.split('_');
- const columnName = parts.slice().join('_');
- return [parseInt(chartId, 10), columnName];
+ const column = parts.slice().join('_');
+ return { chartId: parseInt(chartId, 10), column };
}
diff --git a/superset/assets/src/dashboard/util/getFilterFieldNodesTree.js b/superset/assets/src/dashboard/util/getFilterFieldNodesTree.js
index bf74129..d82ea88 100644
--- a/superset/assets/src/dashboard/util/getFilterFieldNodesTree.js
+++ b/superset/assets/src/dashboard/util/getFilterFieldNodesTree.js
@@ -22,14 +22,10 @@ export default function getFilterFieldNodesTree({
dashboardFilters = {},
isSingleEditMode = true,
}) {
- if (Object.keys(dashboardFilters).length === 0) {
- return [];
- }
-
return Object.values(dashboardFilters).map(dashboardFilter => {
const { chartId, filterName, columns, labels } = dashboardFilter;
const children = Object.keys(columns).map(column => ({
- value: getDashboardFilterKey(chartId, column),
+ value: getDashboardFilterKey({ chartId, column }),
label: labels[column] || column,
showCheckbox: !isSingleEditMode,
}));
diff --git a/superset/assets/src/dashboard/util/getFilterScopeNodesTree.js b/superset/assets/src/dashboard/util/getFilterScopeNodesTree.js
index d067f45..740ac0d 100644
--- a/superset/assets/src/dashboard/util/getFilterScopeNodesTree.js
+++ b/superset/assets/src/dashboard/util/getFilterScopeNodesTree.js
@@ -64,13 +64,13 @@ export default function getFilterScopeNodesTree({
let children = [];
if (currentNode.children && currentNode.children.length) {
currentNode.children.forEach(child => {
- const cNode = traverse(components[child]);
+ const childNodeTree = traverse(components[child]);
const childType = components[child].type;
if (FILTER_SCOPE_CONTAINER_TYPES.includes(childType)) {
- children.push(cNode);
+ children.push(childNodeTree);
} else {
- children = children.concat(cNode);
+ children = children.concat(childNodeTree);
}
});
}
diff --git a/superset/assets/src/dashboard/util/getFilterScopeParentNodes.js b/superset/assets/src/dashboard/util/getFilterScopeParentNodes.js
index 02a92a1..280661d 100644
--- a/superset/assets/src/dashboard/util/getFilterScopeParentNodes.js
+++ b/superset/assets/src/dashboard/util/getFilterScopeParentNodes.js
@@ -29,7 +29,7 @@ export default function getFilterScopeParentNodes(nodes, depthLimit = 0) {
}
};
- if (nodes && nodes.length) {
+ if (nodes && nodes.length > 0) {
nodes.forEach(node => {
traverse(node, 0);
});
diff --git a/superset/assets/src/dashboard/util/propShapes.jsx b/superset/assets/src/dashboard/util/propShapes.jsx
index 60b5f55..b59f41e 100644
--- a/superset/assets/src/dashboard/util/propShapes.jsx
+++ b/superset/assets/src/dashboard/util/propShapes.jsx
@@ -105,6 +105,30 @@ export const dashboardInfoPropShape = PropTypes.shape({
userId: PropTypes.string.isRequired,
});
+/* eslint-disable-next-line no-undef */
+const lazyFunction = f => () => f().apply(this, arguments);
+
+const leafType = PropTypes.shape({
+ value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
+ label: PropTypes.string.isRequired,
+});
+
+const parentShape = {
+ value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
+ label: PropTypes.string.isRequired,
+ children: PropTypes.arrayOf(
+ PropTypes.oneOfType([
+ PropTypes.shape(lazyFunction(() => parentShape)),
+ leafType,
+ ]),
+ ),
+};
+
+export const filterScopeSelectorTreeNodePropShape = PropTypes.oneOfType([
+ PropTypes.shape(parentShape),
+ leafType,
+]);
+
export const loadStatsPropShape = PropTypes.objectOf(
PropTypes.shape({
didLoad: PropTypes.bool.isRequired,
diff --git a/superset/assets/src/visualizations/FilterBox/FilterBox.jsx b/superset/assets/src/visualizations/FilterBox/FilterBox.jsx
index d4308f1..b259e7b 100644
--- a/superset/assets/src/visualizations/FilterBox/FilterBox.jsx
+++ b/superset/assets/src/visualizations/FilterBox/FilterBox.jsx
@@ -304,7 +304,7 @@ class FilterBox extends React.Component {
}
renderFilterBadge(chartId, column) {
- const colorKey = getDashboardFilterKey(chartId, column);
+ const colorKey = getDashboardFilterKey({ chartId, column });
const filterColorMap = getFilterColorMap();
const colorCode = filterColorMap[colorKey];