import * as R from 'rambda'; import { IStoreState } from '../index.data'; import { getScrollByMoveSelectIntoView, getScrollYMadeBottomVisible_whenBottomExceed, getScrollYMadeTopVisible_whenTopExceed, } from './helper'; const ROWHEIGHT = 100; function getState(overrides: Partial): IStoreState { const state0: Partial = { rows: R.range(0, 10).map(i => ({ id: i })), columns: [{ id: 0, width: 30 }], rowDimensions: R.range(0, 10).reduce( (acc, item) => ({ ...acc, [item]: { height: ROWHEIGHT, top: item * ROWHEIGHT }, }), {} as IStoreState['rowDimensions'], ), colDimensions: { 0: { left: 0, width: 30, offsetLeft: 0, offsetWidth: 0 } }, padding: { bottom: 0, right: 0 }, // 以下留给每个test自定义 freezeAt: { i: -1, j: -1 }, containerBox: { w: 0, h: 0 }, scrollX: 0, scrollY: 0, collapsedRowIds: [], }; const state = { ...state0, ...overrides }; return state as IStoreState; } describe('getScrollByMoveSelectIntoView', () => { test('容器高200,无冻结,当前选中第2行,不用滚动', () => { const state = getState({ selectionType: 'row', selectedRowRange: [1, 1], containerBox: { w: 30, h: 200 }, }); const dim = { left: 0, top: 100, width: 30, height: 100, offsetLeft: 0, offsetTop: 100, offsetWidth: 30, offsetHeight: 100, }; const result = getScrollByMoveSelectIntoView(state, dim); expect(result).toEqual({ scrollX: 0, scrollY: 0 }); }); test('容器高200,无冻结,当前选中第3行', () => { const state = getState({ selectionType: 'row', selectedRowRange: [2, 2], containerBox: { w: 30, h: 200 }, }); const dim = { left: 0, top: 200, width: 30, height: 100, offsetLeft: 0, offsetTop: 200, offsetWidth: 30, offsetHeight: 100, }; const result = getScrollByMoveSelectIntoView(state, dim); expect(result).toEqual({ scrollX: 0, scrollY: 200 }); }); test('容器高200,无冻结,当前选中第4行', () => { const state = getState({ selectionType: 'row', selectedRowRange: [3, 3], containerBox: { w: 30, h: 200 }, }); const dim = { left: 0, top: 300, width: 30, height: 100, offsetLeft: 0, offsetTop: 300, offsetWidth: 30, offsetHeight: 100, }; const result = getScrollByMoveSelectIntoView(state, dim); expect(result).toEqual({ scrollX: 0, scrollY: 300 }); }); test('容器高200,冻结1行,当前选中第4行', () => { const state = getState({ selectionType: 'row', selectedRowRange: [3, 3], containerBox: { w: 30, h: 200 }, freezeAt: { i: 0, j: -1 }, }); const dim = { left: 0, top: 300, width: 30, height: 100, offsetLeft: 0, offsetTop: 300, offsetWidth: 30, offsetHeight: 100, }; const result = getScrollByMoveSelectIntoView(state, dim); // 画一下图, 确实是300, 此时当前行整个滚动隐藏到顶部 expect(result).toEqual({ scrollX: 0, scrollY: 300 }); }); test('容器高80,当前选中第1行, 应该无动作, 否则会上下跳动死循环', () => { const state = getState({ selectionType: 'row', selectedRowRange: [1, 1], containerBox: { w: 30, h: 80 }, }); const dim = { left: 0, top: 0, width: 30, height: 100, offsetLeft: 0, offsetTop: 0, offsetWidth: 30, offsetHeight: 100, }; const result = getScrollByMoveSelectIntoView(state, dim); expect(result).toEqual({ scrollX: 0, scrollY: 0 }); }); test('容器高390,无冻结,当前选中第6行', () => { const state = getState({ selectionType: 'row', selectedRowRange: [5, 5], containerBox: { w: 30, h: 390 }, }); const dim = { left: 0, top: 500, width: 30, height: 100, offsetLeft: 0, offsetTop: 500, offsetWidth: 30, offsetHeight: 100, }; const result = getScrollByMoveSelectIntoView(state, dim); expect(result).toEqual({ scrollX: 0, scrollY: 300 }); }); }); describe('getScrollYMadeBottomVisible_whenBottomExceed', () => { test('容器高380, 冻结第1行, 当前选中第4行', () => { // 意义是: 如果第2行顶部在视口外, 要把第2行顶部, 显示到距viewport顶部 preserveV的位置或更少, 此时顶部显然是第1行(因为冻结), 但第2行就是第2行 const state = getState({ freezeAt: { i: 0, j: -1 }, }); const result = getScrollYMadeBottomVisible_whenBottomExceed(state, { w: 30, h: 380 }, 3); expect(result).toEqual(100); }); }); describe('getScrollYMadeTopVisible_whenTopExceed', () => { test('容器高200, 无冻结, 当前选中第1行', () => { // 意义是: 如果第1行顶部在视口外, 要把第1行顶部, 显示到距viewport顶部 preserveV的位置或更少, 此时顶部就是第1行 const state = getState({}); const result = getScrollYMadeTopVisible_whenTopExceed(state, { w: 30, h: 200 }, 0); expect(result).toEqual(0); }); test('容器高200, 无冻结, 当前选中第2行', () => { // 意义是: 如果第2行顶部在视口外, 要把第2行顶部, 显示到距viewport顶部 preserveV的位置或更少 const state = getState({}); const result = getScrollYMadeTopVisible_whenTopExceed(state, { w: 30, h: 200 }, 1); expect(result).toEqual(0); }); test('容器高200, 冻结第1行, 当前选中第2行', () => { // 意义是: 如果第2行顶部在视口外, 要把第2行顶部, 显示到距viewport顶部 preserveV的位置或更少, 此时顶部显然是第1行(因为冻结), 但第2行就是第2行 const state = getState({ freezeAt: { i: 0, j: -1 } }); const result = getScrollYMadeTopVisible_whenTopExceed(state, { w: 30, h: 200 }, 1); expect(result).toEqual(0); }); test('容器高400, 无冻结, 当前选中第3行', () => { // 意义是: 如果第3行顶部在视口外, 要把第3行顶部, 显示到距viewport顶部 preserveV的位置或更少, 此时顶部就是第3行 const state = getState({}); const result = getScrollYMadeTopVisible_whenTopExceed(state, { w: 30, h: 400 }, 2); expect(result).toEqual(100); }); test('容器高400, 冻结第1行, 当前选中第3行', () => { // 意义是: 如果第3行顶部在视口外, 要把第3行顶部, 显示到距viewport顶部 preserveV的位置或更少, 此时顶部显然是第1行(因为冻结), 接下来就是第3行 const state = getState({ freezeAt: { i: 0, j: -1 } }); const result = getScrollYMadeTopVisible_whenTopExceed(state, { w: 30, h: 400 }, 2); expect(result).toEqual(0); }); });