/** * 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 layoutReducer from 'src/dashboard/reducers/dashboardLayout'; import { UPDATE_COMPONENTS, DELETE_COMPONENT, CREATE_COMPONENT, MOVE_COMPONENT, CREATE_TOP_LEVEL_TABS, DELETE_TOP_LEVEL_TABS, } from 'src/dashboard/actions/dashboardLayout'; import { CHART_TYPE, DASHBOARD_GRID_TYPE, DASHBOARD_ROOT_TYPE, ROW_TYPE, TAB_TYPE, TABS_TYPE, } from 'src/dashboard/util/componentTypes'; import { DASHBOARD_ROOT_ID, DASHBOARD_GRID_ID, NEW_COMPONENTS_SOURCE_ID, NEW_TABS_ID, NEW_ROW_ID, } from 'src/dashboard/util/constants'; describe('dashboardLayout reducer', () => { it('should return initial state for unrecognized actions', () => { expect(layoutReducer(undefined, {})).toEqual({}); }); it('should delete a component, remove its reference in its parent, and recursively all of its children', () => { expect( layoutReducer( { toDelete: { id: 'toDelete', children: ['child1'], }, child1: { id: 'child1', children: ['child2'], }, child2: { id: 'child2', children: [], }, parentId: { id: 'parentId', type: ROW_TYPE, children: ['toDelete', 'anotherId'], }, }, { type: DELETE_COMPONENT, payload: { id: 'toDelete', parentId: 'parentId' }, }, ), ).toEqual({ parentId: { id: 'parentId', children: ['anotherId'], type: ROW_TYPE, }, }); }); it('should delete a parent if the parent was a row and no longer has children', () => { expect( layoutReducer( { grandparentId: { id: 'grandparentId', children: ['parentId'], }, parentId: { id: 'parentId', type: ROW_TYPE, children: ['toDelete'], }, toDelete: { id: 'toDelete', children: ['child1'], }, child1: { id: 'child1', children: [], }, }, { type: DELETE_COMPONENT, payload: { id: 'toDelete', parentId: 'parentId' }, }, ), ).toEqual({ grandparentId: { id: 'grandparentId', children: [], }, }); }); it('should update components', () => { expect( layoutReducer( { update: { id: 'update', children: [], }, update2: { id: 'update2', children: [], }, dontUpdate: { id: 'dontUpdate', something: 'something', children: ['abcd'], }, }, { type: UPDATE_COMPONENTS, payload: { nextComponents: { update: { id: 'update', newField: 'newField', }, update2: { id: 'update2', newField: 'newField', }, }, }, }, ), ).toEqual({ update: { id: 'update', newField: 'newField', }, update2: { id: 'update2', newField: 'newField', }, dontUpdate: { id: 'dontUpdate', something: 'something', children: ['abcd'], }, }); }); it('should move a component', () => { const layout = { source: { id: 'source', type: ROW_TYPE, children: ['dontMove', 'toMove'], }, destination: { id: 'destination', type: ROW_TYPE, children: ['anotherChild'], }, toMove: { id: 'toMove', type: CHART_TYPE, children: [], }, }; const dropResult = { source: { id: 'source', type: ROW_TYPE, index: 1 }, destination: { id: 'destination', type: ROW_TYPE, index: 0 }, dragging: { id: 'toMove', type: CHART_TYPE }, }; expect( layoutReducer(layout, { type: MOVE_COMPONENT, payload: { dropResult }, }), ).toEqual({ source: { id: 'source', type: ROW_TYPE, children: ['dontMove'], }, destination: { id: 'destination', type: ROW_TYPE, children: ['toMove', 'anotherChild'], }, toMove: { id: 'toMove', type: CHART_TYPE, children: [], }, }); }); it('should wrap a moved component in a row if need be', () => { const layout = { source: { id: 'source', type: ROW_TYPE, children: ['dontMove', 'toMove'], }, destination: { id: 'destination', type: DASHBOARD_GRID_TYPE, children: [], }, toMove: { id: 'toMove', type: CHART_TYPE, children: [], }, }; const dropResult = { source: { id: 'source', type: ROW_TYPE, index: 1 }, destination: { id: 'destination', type: DASHBOARD_GRID_TYPE, index: 0 }, dragging: { id: 'toMove', type: CHART_TYPE }, }; const result = layoutReducer(layout, { type: MOVE_COMPONENT, payload: { dropResult }, }); const newRow = Object.values(result).find( component => ['source', 'destination', 'toMove'].indexOf(component.id) === -1, ); expect(newRow.children[0]).toBe('toMove'); expect(result.destination.children[0]).toBe(newRow.id); expect(Object.keys(result)).toHaveLength(4); }); it('should add top-level tabs from a new tabs component, moving grid children to new tab', () => { const layout = { [DASHBOARD_ROOT_ID]: { id: DASHBOARD_ROOT_ID, children: [DASHBOARD_GRID_ID], }, [DASHBOARD_GRID_ID]: { id: DASHBOARD_GRID_ID, children: ['child'], }, child: { id: 'child', children: [], }, }; const dropResult = { source: { id: NEW_COMPONENTS_SOURCE_ID, type: '' }, destination: { id: DASHBOARD_ROOT_ID, type: DASHBOARD_ROOT_TYPE, index: 0, }, dragging: { id: NEW_TABS_ID, type: TABS_TYPE }, }; const result = layoutReducer(layout, { type: CREATE_TOP_LEVEL_TABS, payload: { dropResult }, }); const tabComponent = Object.values(result).find( component => component.type === TAB_TYPE, ); const tabsComponent = Object.values(result).find( component => component.type === TABS_TYPE, ); expect(Object.keys(result)).toHaveLength(5); // initial + Tabs + Tab expect(result[DASHBOARD_ROOT_ID].children[0]).toBe(tabsComponent.id); expect(result[tabsComponent.id].children[0]).toBe(tabComponent.id); expect(result[tabComponent.id].children[0]).toBe('child'); expect(result[DASHBOARD_GRID_ID].children).toHaveLength(0); }); it('should add top-level tabs from an existing tabs component, moving grid children to new tab', () => { const layout = { [DASHBOARD_ROOT_ID]: { id: DASHBOARD_ROOT_ID, children: [DASHBOARD_GRID_ID], }, [DASHBOARD_GRID_ID]: { id: DASHBOARD_GRID_ID, children: ['child', 'tabs', 'child2'], }, child: { id: 'child', children: [], }, child2: { id: 'child2', children: [], }, tabs: { id: 'tabs', type: TABS_TYPE, children: ['tab'], }, tab: { id: 'tab', type: TAB_TYPE, children: [], }, }; const dropResult = { source: { id: DASHBOARD_GRID_ID, type: DASHBOARD_GRID_TYPE, index: 1 }, destination: { id: DASHBOARD_ROOT_ID, type: DASHBOARD_ROOT_TYPE, index: 0, }, dragging: { id: 'tabs', type: TABS_TYPE }, }; const result = layoutReducer(layout, { type: CREATE_TOP_LEVEL_TABS, payload: { dropResult }, }); expect(Object.keys(result)).toHaveLength(Object.keys(layout).length); expect(result[DASHBOARD_ROOT_ID].children[0]).toBe('tabs'); expect(result.tabs.children[0]).toBe('tab'); expect(result.tab.children).toEqual(['child', 'child2']); expect(result[DASHBOARD_GRID_ID].children).toHaveLength(0); }); it('should remove top-level tabs, moving children to the grid', () => { const layout = { [DASHBOARD_ROOT_ID]: { id: DASHBOARD_ROOT_ID, children: ['tabs'], }, [DASHBOARD_GRID_ID]: { id: DASHBOARD_GRID_ID, children: [], }, child: { id: 'child', children: [], }, child2: { id: 'child2', children: [], }, tabs: { id: 'tabs', type: TABS_TYPE, children: ['tab'], }, tab: { id: 'tab', type: TAB_TYPE, children: ['child', 'child2'], }, }; const dropResult = { source: { id: DASHBOARD_GRID_ID, type: DASHBOARD_GRID_TYPE, index: 1 }, destination: { id: DASHBOARD_ROOT_ID, type: DASHBOARD_ROOT_TYPE, index: 0, }, dragging: { id: 'tabs', type: TABS_TYPE }, }; const result = layoutReducer(layout, { type: DELETE_TOP_LEVEL_TABS, payload: { dropResult }, }); expect(result).toEqual({ [DASHBOARD_ROOT_ID]: { id: DASHBOARD_ROOT_ID, children: [DASHBOARD_GRID_ID], }, [DASHBOARD_GRID_ID]: { id: DASHBOARD_GRID_ID, children: ['child', 'child2'], }, child: { id: 'child', children: [], }, child2: { id: 'child2', children: [], }, }); }); it('should create a component', () => { const layout = { [DASHBOARD_ROOT_ID]: { id: DASHBOARD_ROOT_ID, children: [DASHBOARD_GRID_ID], }, [DASHBOARD_GRID_ID]: { id: DASHBOARD_GRID_ID, children: ['child'], }, child: { id: 'child' }, }; const dropResult = { source: { id: NEW_COMPONENTS_SOURCE_ID, type: '' }, destination: { id: DASHBOARD_GRID_ID, type: DASHBOARD_GRID_TYPE, index: 1, }, dragging: { id: NEW_ROW_ID, type: ROW_TYPE }, }; const result = layoutReducer(layout, { type: CREATE_COMPONENT, payload: { dropResult }, }); const newId = result[DASHBOARD_GRID_ID].children[1]; expect(result[DASHBOARD_GRID_ID].children).toHaveLength(2); expect(result[newId].type).toBe(ROW_TYPE); }); });