import type { GridRowState } from './row' import reducerConfig, { generateSelectors, selectRow, selectRowMeta, selectIsRowExpandable, selectRowLevel, selectIsRowLoaded, selectAreExpandedRowsControlled, selectExpandedRows, selectIsRowExpanded, selectRowLevelMap, selectCanDragRow, } from './row' import type { ApplyPropsAction } from '../actions' const { reducer: rowReducer } = reducerConfig const getStateMock = (state: Partial = {}): GridRowState => ({ ...reducerConfig.initialState, ...state, }) describe('rowReducer', () => { describe('unknown action', () => { it('should not modify the state', () => { const state = getStateMock() const newState = rowReducer(state, { type: 'nope' } as any) expect(state).toBe(newState) }) }) describe('applyProps', () => { const ROW_ONE = { id: '123', firstName: 'John' } const ROW_TWO = { id: '234', firstName: 'Jane' } const ROW_THREE = { id: '345', firstName: 'Hal' } let state: GridRowState describe('loading new data', () => { beforeEach(() => { state = rowReducer(getStateMock(), { type: 'applyProps', payload: { columns: [], rows: [ROW_ONE, ROW_TWO], }, }) }) it('should store ids', () => { expect(state.collection.ids).toEqual(['123', '234']) }) it('should store an entity for each row', () => { expect(selectRow(state, '123')).toBe(ROW_ONE) expect(selectRow(state, '234')).toBe(ROW_TWO) }) }) describe('updating other prop', () => { let initialState: GridRowState beforeEach(() => { const rows = [ROW_ONE, ROW_TWO] initialState = rowReducer(getStateMock(), { type: 'applyProps', payload: { columns: [], rows, }, }) state = rowReducer(initialState, { type: 'applyProps', payload: { columns: [], rows, selectionMode: 'none', }, }) }) it('should not process the rows at all', () => { expect(initialState.collection.entities).toBe( state.collection.entities ) }) }) describe('adding an extra row', () => { let initialState: GridRowState beforeEach(() => { initialState = rowReducer(getStateMock(), { type: 'applyProps', payload: { columns: [], rows: [ROW_ONE, ROW_TWO], }, }) state = rowReducer(initialState, { type: 'applyProps', payload: { columns: [], rows: [ROW_TWO, ROW_THREE, ROW_ONE], }, }) }) it('should create a new entries collection', () => { expect(initialState.collection.entities).not.toBe( state.collection.entities ) }) it('should not re-create previous rows', () => { expect(selectRow(initialState, '123')).toBe( selectRow(state, '123') ) expect(selectRow(initialState, '234')).toBe( selectRow(state, '234') ) }) it('should use the updated order', () => { expect(state.collection.ids).toEqual(['234', '345', '123']) }) it('should add the new row', () => { expect(selectRow(state, '345')).toBe(ROW_THREE) }) }) describe('when loading a tree', () => { beforeEach(() => { state = rowReducer(getStateMock(), { type: 'applyProps', payload: { columns: [], rows: { ids: ['123', '234'], data: new Map([ ['123', { id: '123' }], ['234', { id: '234' }], ]), meta: new Map([ ['123', { type: 'tree', children: ['234'] }], ['234', { type: 'leaf' }], ]), }, }, }) }) it('should be collapsed by default', () => { expect(state.expanded).toEqual(new Set()) }) }) describe('when loading a group', () => { beforeEach(() => { state = rowReducer(getStateMock(), { type: 'applyProps', payload: { columns: [], rows: { ids: ['123', '234'], data: new Map([ ['123', { id: '123' }], ['234', { id: '234' }], ]), meta: new Map([ ['123', { type: 'group', children: ['234'] }], ['234', { type: 'leaf' }], ]), }, }, }) }) it('should be expanded by default', () => { expect(state.expanded).toEqual(new Set(['123'])) }) it('should not re-open if data is reloaded', () => { state = rowReducer(state, { type: 'updateExpandedRows', payload: new Set(), }) state = rowReducer(state, { type: 'applyProps', payload: { columns: [], rows: { ids: ['123', '234'], data: new Map([ ['123', { id: '123' }], ['234', { id: '234' }], ]), meta: new Map([ ['123', { type: 'group', children: ['234'] }], ['234', { type: 'leaf' }], ]), }, }, }) expect(state.expanded).toEqual(new Set()) }) }) describe('when called with the alternate structure', () => { describe('when using a map', () => { it('should store the ids and data in the store', () => { const state = rowReducer(getStateMock(), { type: 'applyProps', payload: { columns: [], rows: { ids: ['123', '234'], data: new Map([ ['123', { id: '123' }], ['234', { id: '234' }], ]), meta: new Map([ ['123', { type: 'leaf' }], ['234', { type: 'leaf' }], ]), }, }, }) expect(selectRow(state, '123')).toEqual({ id: '123' }) expect(selectRowMeta(state, '123')).toEqual({ type: 'leaf', }) }) }) describe('when using an object', () => { it('should store the ids and data in the store', () => { const state = rowReducer(getStateMock(), { type: 'applyProps', payload: { columns: [], rows: { ids: ['123', '234'], data: { '123': { id: '123' }, '234': { id: '234' }, }, meta: { '123': { type: 'leaf' }, '234': { type: 'leaf' }, }, }, }, }) expect(selectRow(state, '123')).toEqual({ id: '123' }) expect(selectRowMeta(state, '123')).toEqual({ type: 'leaf', }) }) }) }) }) describe('updateExpandedRows', () => { let state: GridRowState it('should update the state', () => { state = rowReducer(getStateMock(), { type: 'updateExpandedRows', payload: new Set(['1', '2']), }) expect(selectExpandedRows(state)).toEqual(new Set(['1', '2'])) }) }) describe('selectors', () => { let state: GridRowState beforeEach(() => { state = getStateMock({ collection: { ids: ['1', '2', '3'], entities: new Map( Object.entries({ 1: { id: '1', loaded: true, data: { id: '1' } }, 2: { id: '2', loaded: true, data: { id: '2' } }, 3: { id: '3', loaded: true, data: { id: '3' } }, }) ), meta: new Map( Object.entries({ 1: { type: 'tree' }, 2: { type: 'leaf' }, }) ), }, }) }) describe('selectIsRowLoaded', () => { it('should return true for a row with data', () => { expect(selectIsRowLoaded(state, '2')).toBe(true) }) it('should return false for a row without data', () => { expect(selectIsRowLoaded(state, '5')).toBe(false) }) }) describe('selectRow', () => { it('should return the specified row', () => { expect(selectRow(state, '2')).toEqual({ id: '2', loaded: true, data: { id: '2' }, }) }) }) describe('selectRowMeta', () => { it('should return the specified meta or an empty object', () => { expect(selectRowMeta(state, '2')).toEqual({ type: 'leaf', }) expect(selectRowMeta(state, '3')).toEqual({}) }) }) describe('selectIsRowExpandable', () => { it('should return if the row can be expanded', () => { expect(selectIsRowExpandable(state, '1')).toBe(true) expect(selectIsRowExpandable(state, '2')).toBe(false) }) }) describe('selectRowLevel', () => { describe('with no treeCache', () => { it('should return the default level for a row ', () => { expect(selectRowLevel(state, '2')).toBe(-1) }) }) describe('with a treeCache', () => { beforeEach(() => { state = { ...state, treeCache: { rowCount: 3, levels: new Map( Object.entries({ 1: 0, 2: 1, 3: 2, }) ), }, } }) it('should return the correct level for a row where it is at level 0', () => { expect(selectRowLevel(state, '1')).toBe(0) }) it('should return the correct level for a row ', () => { expect(selectRowLevel(state, '3')).toBe(2) }) it('should return the default level for a row not in the cache', () => { expect(selectRowLevel(state, '4')).toBe(-1) }) }) }) describe('selectAreExpandedRowsControlled', () => { it('should return the value', () => { expect(selectAreExpandedRowsControlled(state)).toBe(false) }) }) describe('selectExpandedRows', () => { it('should return the expanded set', () => { state = { ...state, expanded: new Set(['1']), } expect(selectExpandedRows(state)).toEqual(new Set(['1'])) }) }) describe('selectIsRowExpanded', () => { beforeEach(() => { state = getStateMock({ expanded: new Set(['1']), }) }) it('should return true when expanded', () => { expect(selectIsRowExpanded(state, '1')).toBe(true) }) it('should return false when not expanded', () => { expect(selectIsRowExpanded(state, '2')).toBe(false) }) }) describe('selectAllExpandableDescendants', () => { beforeEach(() => { state = getStateMock({ collection: { ...reducerConfig.initialState.collection, meta: new Map( Object.entries({ 1: { type: 'group', children: ['2'] }, 2: { type: 'group', children: ['3'] }, 3: { type: 'tree', children: ['4', '5'] }, 4: { type: 'tree', children: [] }, }) ), }, }) }) it('should return all descendants', () => { const { selectAllExpandableDescendants } = generateSelectors() expect(selectAllExpandableDescendants(state, '1')).toEqual( new Set(['1', '2', '3', '4']) ) }) }) describe('selectExpandedState', () => { beforeEach(() => { state = getStateMock({ collection: { ...reducerConfig.initialState.collection, meta: new Map( Object.entries({ 1: { type: 'group', children: ['2'] }, 2: { type: 'group', children: ['3'] }, }) ), }, }) }) it('should return none with nothing expanded', () => { state = { ...state, expanded: new Set(), } const { selectExpandedState } = generateSelectors() expect(selectExpandedState(state)).toBe('none') }) it('should return some if some are expanded', () => { state = { ...state, expanded: new Set(['1']), } const { selectExpandedState } = generateSelectors() expect(selectExpandedState(state)).toBe('some') }) it('should return all if all are expanded', () => { state = { ...state, expanded: new Set(['1', '2']), } const { selectExpandedState } = generateSelectors() expect(selectExpandedState(state)).toBe('all') }) it('should ignore extra ids in the list', () => { state = { ...state, expanded: new Set(['1', '2', '4', '5']), } const { selectExpandedState } = generateSelectors() expect(selectExpandedState(state)).toBe('all') }) }) describe('selectRowLevelMap', () => { describe('when no levelMap is present', () => { it('should return undefined', () => { expect(selectRowLevelMap(getStateMock())).toBeUndefined() }) }) describe('when a levelMap is present', () => { it('should return the levelMap', () => { const levelMap = new Map() const state = getStateMock({ treeCache: { levels: levelMap, rowCount: 5, }, }) expect(selectRowLevelMap(state)).toBe(levelMap) }) }) }) describe('selectCanDragRow', () => { it('should return true for a row with no meta', () => { const state = getStateMock({ collection: { ...reducerConfig.initialState.collection, meta: new Map(), }, }) expect(selectCanDragRow(state, '1')).toBe(true) }) it('should return false for a group row by default', () => { const state = getStateMock({ collection: { ...reducerConfig.initialState.collection, meta: new Map([['1', { type: 'group', children: [] }]]), }, }) expect(selectCanDragRow(state, '1')).toBe(false) }) it('should return true for a group row when preventDrag is false', () => { const state = getStateMock({ collection: { ...reducerConfig.initialState.collection, meta: new Map([ [ '1', { type: 'group', children: [], preventDrag: false, }, ], ]), }, }) expect(selectCanDragRow(state, '1')).toBe(true) }) it('should return false for a group row when preventDrag is true', () => { const state = getStateMock({ collection: { ...reducerConfig.initialState.collection, meta: new Map([ [ '1', { type: 'group', children: [], preventDrag: true, }, ], ]), }, }) expect(selectCanDragRow(state, '1')).toBe(false) }) it('should return true for a non-group row by default', () => { const state = getStateMock({ collection: { ...reducerConfig.initialState.collection, meta: new Map([['1', { type: 'leaf' }]]), }, }) expect(selectCanDragRow(state, '1')).toBe(true) }) it('should return false for a non-group row when preventDrag is true', () => { const state = getStateMock({ collection: { ...reducerConfig.initialState.collection, meta: new Map([ [ '1', { type: 'tree', children: [], preventDrag: true, }, ], ]), }, }) expect(selectCanDragRow(state, '1')).toBe(false) }) it('should return true for a non-group row when preventDrag is false', () => { const state = getStateMock({ collection: { ...reducerConfig.initialState.collection, meta: new Map([ [ '1', { type: 'tree', children: [], preventDrag: false, }, ], ]), }, }) expect(selectCanDragRow(state, '1')).toBe(true) }) }) describe('selectAllDescendantsForIds', () => { beforeEach(() => { state = rowReducer(getStateMock(), { type: 'applyProps', payload: { actionsMenuPresent: false, footerEnabled: false, columns: [], rowDrag: { enabled: false, }, rows: { ids: ['1', '2', '3'], data: new Map([ ['1', { id: '1' }], ['2', { id: '2' }], ['3', { id: '3' }], ['11', { id: '11' }], ['12', { id: '12' }], ['121', { id: '121' }], ['122', { id: '122' }], ]), meta: new Map([ ['1', { type: 'tree', children: ['11', '12'] }], [ '12', { type: 'tree', children: ['121', '122'] }, ], ]), }, }, } satisfies ApplyPropsAction) }) it('should return an empty set if no descendants', () => { const { selectAllDescendantsForIds } = generateSelectors() expect(selectAllDescendantsForIds(state, ['2'])).toEqual( new Set() ) }) it('should return all descendants', () => { const { selectAllDescendantsForIds } = generateSelectors() expect(selectAllDescendantsForIds(state, ['1', '3'])).toEqual( new Set(['11', '12', '121', '122']) ) }) }) }) })