[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

@@ -39,6 +39,7 @@ import {
} from '../../../../src/dashboard/actions/dashboardLayout';
import { setUnsavedChanges } from '../../../../src/dashboard/actions/dashboardState';
import * as dashboardFilters from '../../../../src/dashboard/actions/dashboardFilters';
import {
addWarningToast,
ADD_TOAST,
@@ -80,6 +81,12 @@ describe('dashboardLayout actions', () => {
return { getState, dispatch, state };
}
beforeEach(() => {
sinon.spy(dashboardFilters, 'updateLayoutComponents');
});
afterEach(() => {
dashboardFilters.updateLayoutComponents.restore();
});
describe('updateComponents', () => {
it('should dispatch an updateLayout action', () => {
@@ -92,6 +99,9 @@ describe('dashboardLayout actions', () => {
type: UPDATE_COMPONENTS,
payload: { nextComponents },
});
// update component should not trigger action for dashboardFilters
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(0);
});
it('should dispatch a setUnsavedChanges action if hasUnsavedChanges=false', () => {
@@ -101,8 +111,10 @@ describe('dashboardLayout actions', () => {
const nextComponents = { 1: {} };
const thunk = updateComponents(nextComponents);
thunk(dispatch, getState);
expect(dispatch.callCount).toBe(2);
expect(dispatch.getCall(1).args[0]).toEqual(setUnsavedChanges(true));
// update component should not trigger action for dashboardFilters
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(0);
});
});
@@ -111,11 +123,13 @@ describe('dashboardLayout actions', () => {
const { getState, dispatch } = setup();
const thunk = deleteComponent('id', 'parentId');
thunk(dispatch, getState);
expect(dispatch.callCount).toBe(1);
expect(dispatch.getCall(0).args[0]).toEqual({
type: DELETE_COMPONENT,
payload: { id: 'id', parentId: 'parentId' },
});
// delete components should trigger action for dashboardFilters
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(1);
});
it('should dispatch a setUnsavedChanges action if hasUnsavedChanges=false', () => {
@@ -124,8 +138,10 @@ describe('dashboardLayout actions', () => {
});
const thunk = deleteComponent('id', 'parentId');
thunk(dispatch, getState);
expect(dispatch.callCount).toBe(2);
expect(dispatch.getCall(1).args[0]).toEqual(setUnsavedChanges(true));
expect(dispatch.getCall(2).args[0]).toEqual(setUnsavedChanges(true));
// delete components should trigger action for dashboardFilters
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(1);
});
});
@@ -149,7 +165,8 @@ describe('dashboardLayout actions', () => {
},
});
expect(dispatch.callCount).toBe(2);
// update dashboard title should not trigger action for dashboardFilters
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(0);
});
});
@@ -159,11 +176,13 @@ describe('dashboardLayout actions', () => {
const dropResult = {};
const thunk = createTopLevelTabs(dropResult);
thunk(dispatch, getState);
expect(dispatch.callCount).toBe(1);
expect(dispatch.getCall(0).args[0]).toEqual({
type: CREATE_TOP_LEVEL_TABS,
payload: { dropResult },
});
// create top level tabs should trigger action for dashboardFilters
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(1);
});
it('should dispatch a setUnsavedChanges action if hasUnsavedChanges=false', () => {
@@ -173,8 +192,10 @@ describe('dashboardLayout actions', () => {
const dropResult = {};
const thunk = createTopLevelTabs(dropResult);
thunk(dispatch, getState);
expect(dispatch.callCount).toBe(2);
expect(dispatch.getCall(1).args[0]).toEqual(setUnsavedChanges(true));
expect(dispatch.getCall(2).args[0]).toEqual(setUnsavedChanges(true));
// create top level tabs should trigger action for dashboardFilters
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(1);
});
});
@@ -184,11 +205,13 @@ describe('dashboardLayout actions', () => {
const dropResult = {};
const thunk = deleteTopLevelTabs(dropResult);
thunk(dispatch, getState);
expect(dispatch.callCount).toBe(1);
expect(dispatch.getCall(0).args[0]).toEqual({
type: DELETE_TOP_LEVEL_TABS,
payload: {},
});
// delete top level tabs should trigger action for dashboardFilters
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(1);
});
it('should dispatch a setUnsavedChanges action if hasUnsavedChanges=false', () => {
@@ -198,8 +221,10 @@ describe('dashboardLayout actions', () => {
const dropResult = {};
const thunk = deleteTopLevelTabs(dropResult);
thunk(dispatch, getState);
expect(dispatch.callCount).toBe(2);
expect(dispatch.getCall(1).args[0]).toEqual(setUnsavedChanges(true));
expect(dispatch.getCall(2).args[0]).toEqual(setUnsavedChanges(true));
// delete top level tabs should trigger action for dashboardFilters
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(1);
});
});
@@ -261,6 +286,9 @@ describe('dashboardLayout actions', () => {
thunk2(dispatch, getState);
expect(dispatch.callCount).toBe(3);
// resize components should not trigger action for dashboardFilters
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(0);
});
});
@@ -286,7 +314,8 @@ describe('dashboardLayout actions', () => {
},
});
expect(dispatch.callCount).toBe(2);
// create components should trigger action for dashboardFilters
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(1);
});
it('should move a component if the component is not new', () => {
@@ -315,7 +344,8 @@ describe('dashboardLayout actions', () => {
},
});
expect(dispatch.callCount).toBe(2);
// create components should trigger action for dashboardFilters
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(1);
});
it('should dispatch a toast if the drop overflows the destination', () => {
@@ -379,9 +409,6 @@ describe('dashboardLayout actions', () => {
parentId: 'parentId',
},
});
// move thunk, delete thunk, delete result actions
expect(dispatch.callCount).toBe(3);
});
it('should create top-level tabs if dropped on root', () => {
@@ -404,8 +431,6 @@ describe('dashboardLayout actions', () => {
dropResult,
},
});
expect(dispatch.callCount).toBe(2);
});
it('should dispatch a toast if drop top-level tab into nested tab', () => {
@@ -497,8 +522,10 @@ describe('dashboardLayout actions', () => {
const thunk = redoLayoutAction();
thunk(dispatch, getState);
expect(dispatch.callCount).toBe(1);
expect(dispatch.getCall(0).args[0]).toEqual(UndoActionCreators.redo());
// redo/undo should trigger action for dashboardFilters
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(1);
});
it('should dispatch a setUnsavedChanges(true) action if hasUnsavedChanges=false', () => {
@@ -507,9 +534,10 @@ describe('dashboardLayout actions', () => {
});
const thunk = redoLayoutAction();
thunk(dispatch, getState);
expect(dispatch.getCall(2).args[0]).toEqual(setUnsavedChanges(true));
expect(dispatch.callCount).toBe(2);
expect(dispatch.getCall(1).args[0]).toEqual(setUnsavedChanges(true));
// redo/undo should trigger action for dashboardFilters
expect(dashboardFilters.updateLayoutComponents.callCount).toEqual(1);
});
});
});