[Feature] Dashboard scoped filter (#8590)

* [WIP][dashboard scoped filter] part 1: scope selector modal (#8557)

* filter scope selector modal

* add single-field-edit in multi-edit mode switch

* fix code review comments (round 1)

* refactory after design review

* fix a few props initial value

* [WIP][dashboard scoped filter] part 2: add algorithm to convert checked ids to scope object (#8564)

* convert ids to scope object

* use lodash helpers to make code readable

* [WIP][dashboard scoped filter] part 3: merge filter scope settings into dashboard redux state (#8522)

* merge filter scope settings into dashboard redux state

* fix/add unit tests

* minor bug fixes

* fix save filter Scopes behavior

* resolve review comments

* fix save filter scope settings

* minor comments

* [dashboard scoped filter] Improve scrollbar inside modal (#8553)

* improve scroll inside modal

* make left pane and right pane scroll separately

* fix review comments

* force show filter_box as unchecked (#8587)
This commit is contained in:
Grace Guo
2019-11-18 13:02:25 -08:00
committed by GitHub
parent c87a140734
commit ff6ab10893
69 changed files with 2967 additions and 446 deletions

View File

@@ -17,10 +17,16 @@
* under the License.
*/
import getDashboardUrl from '../../../../src/dashboard/util/getDashboardUrl';
import { DASHBOARD_FILTER_SCOPE_GLOBAL } from '../../../../src/dashboard/reducers/dashboardFilters';
describe('getChartIdsFromLayout', () => {
it('should encode filters', () => {
const filters = { 35: { key: ['value'] } };
const filters = {
'35_key': {
values: ['value'],
scope: DASHBOARD_FILTER_SCOPE_GLOBAL,
},
};
const url = getDashboardUrl('path', filters);
expect(url).toBe(
'path?preselect_filters=%7B%2235%22%3A%7B%22key%22%3A%5B%22value%22%5D%7D%7D',

View File

@@ -0,0 +1,216 @@
/**
* 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 getFilterScopeFromNodesTree from '../../../../src/dashboard/util/getFilterScopeFromNodesTree';
describe('getFilterScopeFromNodesTree', () => {
it('should return empty scope', () => {
const nodes = [];
expect(
getFilterScopeFromNodesTree({
filterKey: '107_region',
nodes,
checkedChartIds: [],
}),
).toEqual({});
});
it('should return scope for simple grid', () => {
const nodes = [
{
label: 'All dashboard',
type: 'ROOT',
value: 'ROOT_ID',
children: [
{
value: 104,
label: 'Life Expectancy VS Rural %',
type: 'CHART',
},
{ value: 105, label: 'Rural Breakdown', type: 'CHART' },
{
value: 106,
label: "World's Pop Growth",
type: 'CHART',
},
{
label: 'Time Filter',
showCheckbox: false,
type: 'CHART',
value: 108,
},
],
},
];
const checkedChartIds = [104, 106];
expect(
getFilterScopeFromNodesTree({
filterKey: '108___time_range',
nodes,
checkedChartIds,
}),
).toEqual({
scope: ['ROOT_ID'],
immune: [105],
});
});
describe('should return scope for tabbed dashboard', () => {
const nodes = [
{
label: 'All dashboard',
type: 'ROOT',
value: 'ROOT_ID',
children: [
{
label: 'Tab 1',
type: 'TAB',
value: 'TAB-Rb5aaqKWgG',
children: [
{
label: 'Geo Filters',
showCheckbox: false,
type: 'CHART',
value: 107,
},
{
label: "World's Pop Growth",
showCheckbox: true,
type: 'CHART',
value: 106,
},
],
},
{
label: 'Tab 2',
type: 'TAB',
value: 'TAB-w5Fp904Rs',
children: [
{
label: 'Time Filter',
showCheckbox: true,
type: 'CHART',
value: 108,
},
{
label: 'Life Expectancy VS Rural %',
showCheckbox: true,
type: 'CHART',
value: 104,
},
{
label: 'Row Tab 1',
type: 'TAB',
value: 'TAB-E4mJaZ-uQM',
children: [
{
value: 105,
label: 'Rural Breakdown',
type: 'CHART',
showCheckbox: true,
},
{
value: 103,
label: '% Rural',
type: 'CHART',
showCheckbox: true,
},
],
},
{
value: 'TAB-rLYu-Cryu',
label: 'New Tab',
type: 'TAB',
children: [
{
value: 102,
label: 'Most Populated Countries',
type: 'CHART',
showCheckbox: true,
},
{
value: 101,
label: "World's Population",
type: 'CHART',
showCheckbox: true,
},
],
},
],
},
],
},
];
it('root level tab scope', () => {
const checkedChartIds = [106];
expect(
getFilterScopeFromNodesTree({
filterKey: '107_region',
nodes,
checkedChartIds,
}),
).toEqual({
scope: ['TAB-Rb5aaqKWgG'],
immune: [],
});
});
it('global scope', () => {
const checkedChartIds = [106, 104, 101, 102, 103, 105];
expect(
getFilterScopeFromNodesTree({
filterKey: '107_country_name',
nodes,
checkedChartIds,
}),
).toEqual({
scope: ['ROOT_ID'],
immune: [108],
});
});
it('row level tab scope', () => {
const checkedChartIds = [103, 105];
expect(
getFilterScopeFromNodesTree({
filterKey: '108___time_range',
nodes,
checkedChartIds,
}),
).toEqual({
scope: ['TAB-E4mJaZ-uQM'],
immune: [],
});
});
it('mixed row level and root level scope', () => {
const checkedChartIds = [103, 105, 106];
expect(
getFilterScopeFromNodesTree({
filterKey: '107_region',
nodes,
checkedChartIds,
}),
).toEqual({
scope: ['TAB-Rb5aaqKWgG', 'TAB-E4mJaZ-uQM'],
immune: [],
});
});
});
});

View File

@@ -33,15 +33,9 @@ describe('getFormDataWithExtraFilters', () => {
],
},
},
dashboardMetadata: {
filterImmuneSlices: [],
filterImmuneSliceFields: {},
},
filters: {
filterId: {
region: ['Spain'],
color: ['pink', 'purple'],
},
region: ['Spain'],
color: ['pink', 'purple'],
},
sliceId: chartId,
};
@@ -60,26 +54,4 @@ describe('getFormDataWithExtraFilters', () => {
val: ['pink', 'purple'],
});
});
it('should not add additional filters if the slice is immune to them', () => {
const result = getFormDataWithExtraFilters({
...mockArgs,
dashboardMetadata: {
filterImmuneSlices: [chartId],
},
});
expect(result.extra_filters).toHaveLength(0);
});
it('should not add additional filters for fields to which the slice is immune', () => {
const result = getFormDataWithExtraFilters({
...mockArgs,
dashboardMetadata: {
filterImmuneSliceFields: {
[chartId]: ['region'],
},
},
});
expect(result.extra_filters).toHaveLength(1);
});
});