mirror of
https://github.com/apache/superset.git
synced 2026-04-19 08:04:53 +00:00
[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:
@@ -24,7 +24,7 @@ import Dashboard from '../../../../src/dashboard/components/Dashboard';
|
||||
import DashboardBuilder from '../../../../src/dashboard/containers/DashboardBuilder';
|
||||
|
||||
// mock data
|
||||
import chartQueries, { sliceId as chartId } from '../fixtures/mockChartQueries';
|
||||
import chartQueries from '../fixtures/mockChartQueries';
|
||||
import datasources from '../../../fixtures/mockDatasource';
|
||||
import dashboardInfo from '../fixtures/mockDashboardInfo';
|
||||
import { dashboardLayout } from '../fixtures/mockDashboardLayout';
|
||||
@@ -46,7 +46,7 @@ describe('Dashboard', () => {
|
||||
dashboardState,
|
||||
dashboardInfo,
|
||||
charts: chartQueries,
|
||||
filters: {},
|
||||
activeFilters: {},
|
||||
slices: sliceEntities.slices,
|
||||
datasources,
|
||||
layout: dashboardLayout.present,
|
||||
@@ -61,10 +61,12 @@ describe('Dashboard', () => {
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
// activeFilters map use id_column) as key
|
||||
const OVERRIDE_FILTERS = {
|
||||
1: { region: [] },
|
||||
2: { country_name: ['USA'] },
|
||||
3: { region: [], country_name: ['USA'] },
|
||||
'1_region': [],
|
||||
'2_country_name': ['USA'],
|
||||
'3_region': [],
|
||||
'3_country_name': ['USA'],
|
||||
};
|
||||
|
||||
it('should render a DashboardBuilder', () => {
|
||||
@@ -72,85 +74,6 @@ describe('Dashboard', () => {
|
||||
expect(wrapper.find(DashboardBuilder)).toHaveLength(1);
|
||||
});
|
||||
|
||||
describe('refreshExcept', () => {
|
||||
const overrideDashboardInfo = {
|
||||
...dashboardInfo,
|
||||
metadata: {
|
||||
...dashboardInfo.metadata,
|
||||
filterImmuneSliceFields: { [chartQueries[chartId].id]: ['region'] },
|
||||
},
|
||||
};
|
||||
|
||||
const overrideCharts = {
|
||||
...chartQueries,
|
||||
1001: {
|
||||
...chartQueries[chartId],
|
||||
id: 1001,
|
||||
},
|
||||
};
|
||||
|
||||
const overrideSlices = {
|
||||
...props.slices,
|
||||
1001: {
|
||||
...props.slices[chartId],
|
||||
slice_id: 1001,
|
||||
},
|
||||
};
|
||||
|
||||
it('should call triggerQuery for all non-exempt slices', () => {
|
||||
const wrapper = setup({ charts: overrideCharts, slices: overrideSlices });
|
||||
const spy = sinon.spy(props.actions, 'triggerQuery');
|
||||
wrapper.instance().refreshExcept('1001');
|
||||
spy.restore();
|
||||
expect(spy.callCount).toBe(Object.keys(overrideCharts).length - 1);
|
||||
});
|
||||
|
||||
it('should not call triggerQuery for filterImmuneSlices', () => {
|
||||
const wrapper = setup({
|
||||
charts: overrideCharts,
|
||||
dashboardInfo: {
|
||||
...dashboardInfo,
|
||||
metadata: {
|
||||
...dashboardInfo.metadata,
|
||||
filterImmuneSlices: Object.keys(overrideCharts).map(id =>
|
||||
Number(id),
|
||||
),
|
||||
},
|
||||
},
|
||||
});
|
||||
const spy = sinon.spy(props.actions, 'triggerQuery');
|
||||
wrapper.instance().refreshExcept();
|
||||
spy.restore();
|
||||
expect(spy.callCount).toBe(0);
|
||||
});
|
||||
|
||||
it('should not call triggerQuery for filterImmuneSliceFields', () => {
|
||||
const wrapper = setup({
|
||||
filters: OVERRIDE_FILTERS,
|
||||
dashboardInfo: overrideDashboardInfo,
|
||||
});
|
||||
const spy = sinon.spy(props.actions, 'triggerQuery');
|
||||
wrapper.instance().refreshExcept('1');
|
||||
expect(spy.callCount).toBe(0);
|
||||
spy.restore();
|
||||
});
|
||||
|
||||
it('should call triggerQuery if filter has more filter-able fields', () => {
|
||||
const wrapper = setup({
|
||||
filters: OVERRIDE_FILTERS,
|
||||
dashboardInfo: overrideDashboardInfo,
|
||||
});
|
||||
const spy = sinon.spy(props.actions, 'triggerQuery');
|
||||
|
||||
// if filter have additional fields besides immune ones,
|
||||
// should apply filter.
|
||||
wrapper.instance().refreshExcept('3');
|
||||
expect(spy.callCount).toBe(1);
|
||||
|
||||
spy.restore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('componentWillReceiveProps', () => {
|
||||
const layoutWithExtraChart = {
|
||||
...props.layout,
|
||||
@@ -186,17 +109,17 @@ describe('Dashboard', () => {
|
||||
describe('componentDidUpdate', () => {
|
||||
let wrapper;
|
||||
let prevProps;
|
||||
let refreshExceptSpy;
|
||||
let refreshSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
wrapper = setup({ filters: OVERRIDE_FILTERS });
|
||||
wrapper = setup({ activeFilters: OVERRIDE_FILTERS });
|
||||
wrapper.instance().appliedFilters = OVERRIDE_FILTERS;
|
||||
prevProps = wrapper.instance().props;
|
||||
refreshExceptSpy = sinon.spy(wrapper.instance(), 'refreshExcept');
|
||||
refreshSpy = sinon.spy(wrapper.instance(), 'refreshCharts');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
refreshExceptSpy.restore();
|
||||
refreshSpy.restore();
|
||||
});
|
||||
|
||||
it('should not call refresh when is editMode', () => {
|
||||
@@ -207,15 +130,15 @@ describe('Dashboard', () => {
|
||||
},
|
||||
});
|
||||
wrapper.instance().componentDidUpdate(prevProps);
|
||||
expect(refreshExceptSpy.callCount).toBe(0);
|
||||
expect(refreshSpy.callCount).toBe(0);
|
||||
});
|
||||
|
||||
it('should not call refresh when there is no change', () => {
|
||||
wrapper.setProps({
|
||||
filters: OVERRIDE_FILTERS,
|
||||
activeFilters: OVERRIDE_FILTERS,
|
||||
});
|
||||
wrapper.instance().componentDidUpdate(prevProps);
|
||||
expect(refreshExceptSpy.callCount).toBe(0);
|
||||
expect(refreshSpy.callCount).toBe(0);
|
||||
expect(wrapper.instance().appliedFilters).toBe(OVERRIDE_FILTERS);
|
||||
});
|
||||
|
||||
@@ -224,12 +147,12 @@ describe('Dashboard', () => {
|
||||
gender: ['boy', 'girl'],
|
||||
};
|
||||
wrapper.setProps({
|
||||
filters: {
|
||||
activeFilters: {
|
||||
...OVERRIDE_FILTERS,
|
||||
...newFilter,
|
||||
},
|
||||
});
|
||||
expect(refreshExceptSpy.callCount).toBe(1);
|
||||
expect(refreshSpy.callCount).toBe(1);
|
||||
expect(wrapper.instance().appliedFilters).toEqual({
|
||||
...OVERRIDE_FILTERS,
|
||||
...newFilter,
|
||||
@@ -238,23 +161,23 @@ describe('Dashboard', () => {
|
||||
|
||||
it('should call refresh if a filter is removed', () => {
|
||||
wrapper.setProps({
|
||||
filters: {},
|
||||
activeFilters: {},
|
||||
});
|
||||
expect(refreshExceptSpy.callCount).toBe(1);
|
||||
expect(refreshSpy.callCount).toBe(1);
|
||||
expect(wrapper.instance().appliedFilters).toEqual({});
|
||||
});
|
||||
|
||||
it('should call refresh if a filter is changed', () => {
|
||||
wrapper.setProps({
|
||||
filters: {
|
||||
activeFilters: {
|
||||
...OVERRIDE_FILTERS,
|
||||
region: ['Canada'],
|
||||
'1_region': ['Canada'],
|
||||
},
|
||||
});
|
||||
expect(refreshExceptSpy.callCount).toBe(1);
|
||||
expect(refreshSpy.callCount).toBe(1);
|
||||
expect(wrapper.instance().appliedFilters).toEqual({
|
||||
...OVERRIDE_FILTERS,
|
||||
region: ['Canada'],
|
||||
'1_region': ['Canada'],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,28 +20,34 @@ import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import { dashboardFilters } from '../fixtures/mockDashboardFilters';
|
||||
import { sliceId as chartId } from '../fixtures/mockChartQueries';
|
||||
import { filterId, column } from '../fixtures/mockSliceEntities';
|
||||
import FilterIndicatorsContainer from '../../../../src/dashboard/components/FilterIndicatorsContainer';
|
||||
import FilterIndicator from '../../../../src/dashboard/components/FilterIndicator';
|
||||
import * as colorMap from '../../../../src/dashboard/util/dashboardFiltersColorMap';
|
||||
import { buildActiveFilters } from '../../../../src/dashboard/util/activeDashboardFilters';
|
||||
import { getDashboardFilterKey } from '../../../../src/dashboard/util/getDashboardFilterKey';
|
||||
import { DASHBOARD_ROOT_ID } from '../../../../src/dashboard/util/constants';
|
||||
import { dashboardWithFilter } from '../fixtures/mockDashboardLayout';
|
||||
|
||||
describe('FilterIndicatorsContainer', () => {
|
||||
const chartId = 1;
|
||||
const mockedProps = {
|
||||
dashboardFilters,
|
||||
chartId,
|
||||
chartStatus: 'success',
|
||||
filterImmuneSlices: [],
|
||||
filterImmuneSliceFields: {},
|
||||
setDirectPathToChild: () => {},
|
||||
filterFieldOnFocus: {},
|
||||
};
|
||||
|
||||
colorMap.getFilterColorKey = jest.fn(() => 'id_column');
|
||||
colorMap.getFilterColorMap = jest.fn(() => ({
|
||||
id_column: 'badge-1',
|
||||
[getDashboardFilterKey({ chartId, column })]: 'badge-1',
|
||||
}));
|
||||
|
||||
buildActiveFilters({
|
||||
dashboardFilters,
|
||||
components: dashboardWithFilter,
|
||||
});
|
||||
|
||||
function setup(overrideProps) {
|
||||
return shallow(
|
||||
<FilterIndicatorsContainer {...mockedProps} {...overrideProps} />,
|
||||
@@ -58,18 +64,25 @@ describe('FilterIndicatorsContainer', () => {
|
||||
expect(wrapper.find(FilterIndicator)).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should not show indicator when chart is immune', () => {
|
||||
const wrapper = setup({ filterImmuneSlices: [chartId] });
|
||||
expect(wrapper.find(FilterIndicator)).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should not show indicator when chart field is immune', () => {
|
||||
const wrapper = setup({ filterImmuneSliceFields: { [chartId]: [column] } });
|
||||
expect(wrapper.find(FilterIndicator)).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should show indicator', () => {
|
||||
const wrapper = setup();
|
||||
expect(wrapper.find(FilterIndicator)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should not show indicator when chart is immune', () => {
|
||||
const overwriteDashboardFilters = {
|
||||
...dashboardFilters,
|
||||
[filterId]: {
|
||||
...dashboardFilters[filterId],
|
||||
scopes: {
|
||||
region: {
|
||||
scope: [DASHBOARD_ROOT_ID],
|
||||
immune: [chartId],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const wrapper = setup({ dashboardFilters: overwriteDashboardFilters });
|
||||
expect(wrapper.find(FilterIndicator)).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user