diff --git a/superset/assets/javascripts/dashboard/Dashboard.jsx b/superset/assets/javascripts/dashboard/Dashboard.jsx index 8b0a0e1c69114..c6262f580ee8e 100644 --- a/superset/assets/javascripts/dashboard/Dashboard.jsx +++ b/superset/assets/javascripts/dashboard/Dashboard.jsx @@ -166,6 +166,10 @@ export function dashboardContainer(dashboard, datasources, userid) { } }, effectiveExtraFilters(sliceId) { + // Don't filter the filter_box itself by preselect_filters + if (this.getSlice(sliceId).formData.viz_type === 'filter_box') { + return []; + } const f = []; const immuneSlices = this.metadata.filter_immune_slices || []; if (sliceId && immuneSlices.includes(sliceId)) { @@ -195,21 +199,24 @@ export function dashboardContainer(dashboard, datasources, userid) { return f; }, addFilter(sliceId, col, vals, merge = true, refresh = true) { - if (!(sliceId in this.filters)) { - this.filters[sliceId] = {}; - } - if (!(col in this.filters[sliceId]) || !merge) { - this.filters[sliceId][col] = vals; + if (this.getSlice(sliceId) && (col === '__from' || col === '__to' || + this.getSlice(sliceId).formData.groupby.indexOf(col) !== -1)) { + if (!(sliceId in this.filters)) { + this.filters[sliceId] = {}; + } + if (!(col in this.filters[sliceId]) || !merge) { + this.filters[sliceId][col] = vals; - // d3.merge pass in array of arrays while some value form filter components - // from and to filter box require string to be process and return - } else if (this.filters[sliceId][col] instanceof Array) { - this.filters[sliceId][col] = d3.merge([this.filters[sliceId][col], vals]); - } else { - this.filters[sliceId][col] = d3.merge([[this.filters[sliceId][col]], vals])[0] || ''; - } - if (refresh) { - this.refreshExcept(sliceId); + // d3.merge pass in array of arrays while some value form filter components + // from and to filter box require string to be process and return + } else if (this.filters[sliceId][col] instanceof Array) { + this.filters[sliceId][col] = d3.merge([this.filters[sliceId][col], vals]); + } else { + this.filters[sliceId][col] = d3.merge([[this.filters[sliceId][col]], vals])[0] || ''; + } + if (refresh) { + this.refreshExcept(sliceId); + } } this.updateFilterParamsInUrl(); }, diff --git a/superset/assets/javascripts/dashboard/components/SaveModal.jsx b/superset/assets/javascripts/dashboard/components/SaveModal.jsx index a22f4ac7725e0..82b44df0f678a 100644 --- a/superset/assets/javascripts/dashboard/components/SaveModal.jsx +++ b/superset/assets/javascripts/dashboard/components/SaveModal.jsx @@ -80,6 +80,7 @@ class SaveModal extends React.PureComponent { css: this.state.css, expanded_slices: expandedSlices, dashboard_title: dashboard.dashboard_title, + default_filters: dashboard.readFilters(), }; let url = null; if (saveType === 'overwrite') { diff --git a/superset/assets/visualizations/filter_box.jsx b/superset/assets/visualizations/filter_box.jsx index a97ec3fe6f85b..7e02dd59bbaeb 100644 --- a/superset/assets/visualizations/filter_box.jsx +++ b/superset/assets/visualizations/filter_box.jsx @@ -74,6 +74,24 @@ class FilterBox extends React.Component { ); }); } + // Add created options to filtersChoices, even though it doesn't exist, + // or these options will exist in query sql but invisible to end user. + if (this.state.selectedValues.hasOwnProperty()) { + for (const filterKey of this.state.selectedValues) { + const existValues = this.props.filtersChoices[filterKey].map(f => f.id); + for (const v of this.state.selectedValues[filterKey]) { + if (existValues.indexOf(v) === -1) { + const addChoice = { + filter: filterKey, + id: v, + text: v, + metric: 0, + }; + this.props.filtersChoices[filterKey].push(addChoice); + } + } + } + } const filters = Object.keys(this.props.filtersChoices).map((filter) => { const data = this.props.filtersChoices[filter]; const maxes = {}; diff --git a/superset/models/core.py b/superset/models/core.py index 5527f11d45291..674a84f61e683 100644 --- a/superset/models/core.py +++ b/superset/models/core.py @@ -322,6 +322,14 @@ def table_names(self): @property def url(self): + if self.json_metadata: + # add default_filters to the preselect_filters of dashboard + json_metadata = json.loads(self.json_metadata) + default_filters = json_metadata.get('default_filters') + if default_filters: + filters = parse.quote(default_filters.encode('utf8')) + return "/superset/dashboard/{}/?preselect_filters={}".format( + self.slug or self.id, filters) return "/superset/dashboard/{}/".format(self.slug or self.id) @property diff --git a/superset/views/core.py b/superset/views/core.py index eded30904f4bf..9a6bae43f6f85 100755 --- a/superset/views/core.py +++ b/superset/views/core.py @@ -1345,6 +1345,7 @@ def _set_dash_metadata(dashboard, data): if 'filter_immune_slice_fields' not in md: md['filter_immune_slice_fields'] = {} md['expanded_slices'] = data['expanded_slices'] + md['default_filters'] = data.get('default_filters', '') dashboard.json_metadata = json.dumps(md, indent=4) @api diff --git a/tests/core_tests.py b/tests/core_tests.py index f3e98873a63fa..4a7efc4de6aff 100644 --- a/tests/core_tests.py +++ b/tests/core_tests.py @@ -380,6 +380,42 @@ def test_save_dash(self, username='admin'): resp = self.get_resp(url, data=dict(data=json.dumps(data))) self.assertIn("SUCCESS", resp) + def test_save_dash_with_filter(self, username='admin'): + self.login(username=username) + dash = db.session.query(models.Dashboard).filter_by( + slug="world_health").first() + positions = [] + for i, slc in enumerate(dash.slices): + d = { + 'col': 0, + 'row': i * 4, + 'size_x': 4, + 'size_y': 4, + 'slice_id': '{}'.format(slc.id)} + positions.append(d) + + filters = {str(dash.slices[0].id): {'region': ['North America']}} + default_filters = json.dumps(filters) + data = { + 'css': '', + 'expanded_slices': {}, + 'positions': positions, + 'dashboard_title': dash.dashboard_title, + 'default_filters': default_filters + } + + url = '/superset/save_dash/{}/'.format(dash.id) + resp = self.get_resp(url, data=dict(data=json.dumps(data))) + self.assertIn("SUCCESS", resp) + + updatedDash = db.session.query(models.Dashboard).filter_by( + slug="world_health").first() + new_url = updatedDash.url + self.assertIn("region", new_url) + + resp = self.get_resp(new_url) + self.assertIn("North America", resp) + def test_save_dash_with_dashboard_title(self, username='admin'): self.login(username=username) dash = (