import type { ApplyPropsAction, CancelRowDragAction, CompleteRowDragAction, RemoveRecentlyDraggedAction, StartRowDragAction, UpdateRowDragAction, } from '../actions' import type { GridDragState } from './drag' import reducerConfig, { selectCanConvertLeaf, selectCanDragMultiple, selectDragCollisionDetails, selectDragMode, selectDraggingRowIds, selectDropPredicate, selectIsDraggingEnabled, selectIsRowDragging, selectRecentlyDroppedRowIds, selectDropIsAllowed, selectDragIsActive, selectDragMoveReceived, selectDragMessage, selectDragNeedsFinalRowOffset, selectConfiguredPreviewColumnId, } from './drag' const { reducer: dragReducer } = reducerConfig const getStateMock = () => reducerConfig.initialState describe('dragReducer', () => { describe('unknown action', () => { it('should not modify the state', () => { const state = getStateMock() const newState = dragReducer(state, { type: 'nope' } as any) expect(state).toBe(newState) }) }) describe('applyProps', () => { it('should set defaults when optional items are omitted', () => { const state = dragReducer(getStateMock(), { type: 'applyProps', payload: { columns: [], rows: [], actionsMenuPresent: false, footerEnabled: false, rowDrag: { enabled: true, }, }, } satisfies ApplyPropsAction) expect(selectIsDraggingEnabled(state)).toBe(true) expect(selectCanDragMultiple(state)).toBe(false) expect(selectDragMode(state)).toBe('default') expect(selectCanConvertLeaf(state)).toBe(false) expect(selectDropPredicate(state)({} as any)).toEqual({ allowed: true, }) expect(selectConfiguredPreviewColumnId(state)).toBe(null) }) it('should set values when optional items are provided', () => { const canDrop = jest .fn() .mockReturnValue({ allowed: false, message: 'nope' }) const state = dragReducer(getStateMock(), { type: 'applyProps', payload: { columns: [], rows: [], actionsMenuPresent: false, footerEnabled: false, rowDrag: { enabled: true, mode: 'parent', enableLeafConversion: true, multiple: true, canDrop, previewColumnId: 'col1', }, }, } satisfies ApplyPropsAction) expect(selectIsDraggingEnabled(state)).toBe(true) expect(selectCanDragMultiple(state)).toBe(true) expect(selectDragMode(state)).toBe('parent') expect(selectCanConvertLeaf(state)).toBe(true) expect(selectDropPredicate(state)({} as any)).toEqual({ allowed: false, message: 'nope', }) expect(selectConfiguredPreviewColumnId(state)).toBe('col1') }) }) describe('startRowDrag', () => { it('should update state', () => { const state = dragReducer(getStateMock(), { type: 'startRowDrag', payload: { ids: new Set(['1', '2']), }, } satisfies StartRowDragAction) expect(selectDragIsActive(state)).toBe(true) expect(selectDraggingRowIds(state)).toEqual(new Set(['1', '2'])) }) }) describe('completeRowDrag', () => { let newState: GridDragState beforeEach(() => { newState = dragReducer(getStateMock(), { type: 'startRowDrag', payload: { ids: new Set(['1', '2']), }, } satisfies StartRowDragAction) newState = dragReducer(newState, { type: 'updateRowDrag', payload: { allowed: true, level: 0, operation: 'between', parentId: null, prevId: '1', prevInsertId: '1', }, } satisfies UpdateRowDragAction) }) it('should reset state', () => { const state = dragReducer(newState, { type: 'completeRowDrag', payload: null, } satisfies CompleteRowDragAction) expect(selectDragIsActive(state)).toBe(false) expect(selectDraggingRowIds(state)).toEqual(new Set([])) expect(selectDragCollisionDetails(state)).toEqual({ prevId: null, prevInsertId: null, parentId: null, level: 0, operation: 'between', }) }) it('should store recently dropped ids', () => { const state = dragReducer(newState, { type: 'completeRowDrag', payload: null, } satisfies CompleteRowDragAction) expect(selectRecentlyDroppedRowIds(state)).toEqual( new Set(['1', '2']) ) }) }) describe('cancelRowDrag', () => { let newState: GridDragState beforeEach(() => { newState = dragReducer(getStateMock(), { type: 'startRowDrag', payload: { ids: new Set(['1', '2']), }, } satisfies StartRowDragAction) newState = dragReducer(newState, { type: 'updateRowDrag', payload: { allowed: true, level: 0, operation: 'between', parentId: null, prevId: '1', prevInsertId: '1', }, } satisfies UpdateRowDragAction) }) it('should reset state', () => { const state = dragReducer(newState, { type: 'cancelRowDrag', payload: null, } satisfies CancelRowDragAction) expect(selectDragIsActive(state)).toBe(false) expect(selectDraggingRowIds(state)).toEqual(new Set([])) expect(selectDragCollisionDetails(state)).toEqual({ prevId: null, prevInsertId: null, parentId: null, level: 0, operation: 'between', }) }) }) describe('updateRowDrag', () => { let newState: GridDragState beforeEach(() => { newState = dragReducer(getStateMock(), { type: 'startRowDrag', payload: { ids: new Set(['1', '2']), }, } satisfies StartRowDragAction) }) it('should store changes', () => { const state = dragReducer(newState, { type: 'updateRowDrag', payload: { allowed: true, level: 0, operation: 'between', parentId: null, prevId: '1', prevInsertId: '1', }, } satisfies UpdateRowDragAction) expect(selectDragMoveReceived(state)).toBe(true) expect(selectDropIsAllowed(state)).toEqual(true) expect(selectDragCollisionDetails(state)).toEqual({ level: 0, operation: 'between', parentId: null, prevId: '1', prevInsertId: '1', }) }) }) describe('removeRecentlyDragged', () => { let newState: GridDragState beforeEach(() => { newState = dragReducer(getStateMock(), { type: 'startRowDrag', payload: { ids: new Set(['1', '2']), }, } satisfies StartRowDragAction) newState = dragReducer(newState, { type: 'updateRowDrag', payload: { allowed: true, level: 0, operation: 'between', parentId: null, prevId: '1', prevInsertId: '1', }, } satisfies UpdateRowDragAction) newState = dragReducer(newState, { type: 'completeRowDrag', payload: null, } satisfies CompleteRowDragAction) }) it('should reset recently dropped', () => { const state = dragReducer(newState, { type: 'removeRecentlyDragged', payload: null, } satisfies RemoveRecentlyDraggedAction) expect(selectRecentlyDroppedRowIds(state)).toEqual(new Set([])) }) }) describe('selectors', () => { let state: GridDragState describe('selectIsRowDragging', () => { describe('when dragging is not active', () => { it('should return false', () => { expect(selectIsRowDragging(getStateMock(), '1')).toBe(false) }) }) describe('when dragging is active', () => { beforeEach(() => { state = dragReducer(getStateMock(), { type: 'startRowDrag', payload: { ids: new Set(['1']), }, } satisfies StartRowDragAction) }) it('should return true if row is being dragged', () => { expect(selectIsRowDragging(state, '1')).toBe(true) }) it('should return false if row is not being dragged', () => { expect(selectIsRowDragging(state, '2')).toBe(false) }) }) }) describe('selectDragMessage', () => { describe('when dragging is not active', () => { it('should return empty string', () => { expect(selectDragMessage(getStateMock())).toBe('') }) }) describe('when dragging is active', () => { beforeEach(() => { state = dragReducer(getStateMock(), { type: 'startRowDrag', payload: { ids: new Set(['1']), }, } satisfies StartRowDragAction) }) it('should return the error message if provided', () => { state = dragReducer(state, { type: 'updateRowDrag', payload: { allowed: false, message: 'Error!', level: 0, operation: 'between', parentId: null, prevId: null, prevInsertId: null, }, } satisfies UpdateRowDragAction) expect(selectDragMessage(state)).toBe('Error!') }) it('should return empty string if error message is provided', () => { expect(selectDragMessage(state)).toBe('') }) }) }) describe('selectDragNeedsFinalRowOffset', () => { it('should return false when drag is not active', () => { expect(selectDragNeedsFinalRowOffset(getStateMock())).toBe( false ) }) it('should return false when drag is active in parent mode', () => { let state = dragReducer(getStateMock(), { type: 'applyProps', payload: { columns: [], rows: [], actionsMenuPresent: false, footerEnabled: false, rowDrag: { enabled: true, mode: 'parent', }, }, } satisfies ApplyPropsAction) state = dragReducer(state, { type: 'startRowDrag', payload: { ids: new Set(['1']), }, } satisfies StartRowDragAction) expect(selectDragNeedsFinalRowOffset(state)).toBe(false) }) it('should return true when drag is active in default mode', () => { let state = dragReducer(getStateMock(), { type: 'applyProps', payload: { columns: [], rows: [], actionsMenuPresent: false, footerEnabled: false, rowDrag: { enabled: true, mode: 'default', }, }, } satisfies ApplyPropsAction) state = dragReducer(state, { type: 'startRowDrag', payload: { ids: new Set(['1']), }, } satisfies StartRowDragAction) expect(selectDragNeedsFinalRowOffset(state)).toBe(true) }) }) }) })