fixed-react-data-grid-custom
Version:
Excel-like grid component built with React, with editors, keyboard navigation, copy & paste, and the like
332 lines (280 loc) • 14.5 kB
JavaScript
import { getGridState, getNonFrozenRenderedColumnCount, getVisibleBoundaries, getNonFrozenVisibleColStartIdx, getScrollDirection, SCROLL_DIRECTION, getRowOverscanStartIdx, getRowOverscanEndIdx, OVERSCAN_ROWS, getColOverscanStartIdx, getColOverscanEndIdx } from '../viewportUtils';
describe('viewportUtils', () => {
const getColumns = () => [{ width: 100, left: 0 }, { width: 100, left: 200 }, { width: 100, left: 300 }, { width: 100, left: 400 }, { width: 100, left: 500 }, { width: 100, left: 600 }];
describe('getGridState', () => {
const getState = (propsOverrides = {}) => {
const props = Object.assign({
columnMetrics: {
columns: [
{ key: 'col1' },
{ key: 'col2' }
]
},
minHeight: 100,
rowOffsetHeight: 5,
rowHeight: 20,
rowsCount: 100
}, propsOverrides);
const state = getGridState(props);
return { props, state };
};
it('should correctly set canvas height', () => {
const { state } = getState();
expect(state.height).toBe(95);
});
it('should correctly set visible rows count when rowsCount is greater than the rendered rows count', () => {
const { state } = getState();
expect(state.rowOverscanEndIdx).toBe(8);
});
it('should correctly set visible rows count when rowsCount is less than the rendered rows count', () => {
const { state } = getState({ rowsCount: 8 });
expect(state.rowOverscanEndIdx).toBe(8);
});
it('should correctly set visible column count', () => {
const { state, props } = getState();
expect(state.colVisibleEndIdx).toBe(props.columnMetrics.columns.length);
});
});
describe('getNonFrozenRenderedColumnCount', () => {
const viewportWidth = 100;
const getCols = () => [
{ key: 'col1', width: 20, left: 0 },
{ key: 'col2', width: 20, left: 20 },
{ key: 'col3', width: 20, left: 40 },
{ key: 'col4', width: 20, left: 60 },
{ key: 'col5', width: 20, left: 80 },
{ key: 'col6', width: 1, left: 100 },
{ key: 'col7', width: 1, left: 101 },
{ key: 'col8', width: 1, left: 102 },
{ key: 'col9', width: 1, left: 103 }
];
const verifyNonFrozenRenderedColumnCount = (width = viewportWidth, columns = getCols(), scrollLeft = 0) => {
const columnMetrics = {
columns,
totalWidth: 0
};
return getNonFrozenRenderedColumnCount(columnMetrics, width, scrollLeft);
};
it('correctly set rendered columns count for a set width', () => {
const count = verifyNonFrozenRenderedColumnCount();
expect(count).toBe(5); // col1, col2, col3, col4, col5
});
it('should return all columns that fit width after last frozen column', () => {
const columns = getCols();
columns[1].frozen = true;
const count = verifyNonFrozenRenderedColumnCount(100, columns);
expect(count).toBe(3); // col3, col4, col5
});
it('should return all columns that fit width after last frozen column when grid is scrolled', () => {
// 10 px of first non frozen column are hidden, with remaining 10 px visible
const scrollLeft = 50;
// This means that there are 10px of extra space to fill with columns
const columns = getCols();
columns[1].frozen = true;
const count = verifyNonFrozenRenderedColumnCount(100, columns, scrollLeft);
expect(count).toBe(5); // col5, col6, col7, col8, col9
});
});
describe('getVisibleBoundaries', () => {
const GRID_HEIGHT = 350;
const ROW_HEIGHT = 35;
const TOTAL_ROWS = 100;
const EXPECTED_NUMBER_VISIBLE_ROWS = 10;
describe('When scroll top is 0', () => {
it('should set the rowVisibleStartIdx to be 0', () => {
const scrollTop = 0;
const { rowVisibleStartIdx } = getVisibleBoundaries(GRID_HEIGHT, ROW_HEIGHT, scrollTop, TOTAL_ROWS);
expect(rowVisibleStartIdx).toBe(0);
});
it('should set the rowVisibleEndIdx to be last rendered row', () => {
const scrollTop = 0;
const { rowVisibleEndIdx } = getVisibleBoundaries(GRID_HEIGHT, ROW_HEIGHT, scrollTop, TOTAL_ROWS);
expect(rowVisibleEndIdx).toBe(EXPECTED_NUMBER_VISIBLE_ROWS);
});
});
describe('When scrolling', () => {
const scrollDown = (getScrollTop, assert) => {
const NUMBER_OF_TESTS = 10;
for (let n = 1; n < NUMBER_OF_TESTS; n++) {
const boundaries = getVisibleBoundaries(GRID_HEIGHT, ROW_HEIGHT, getScrollTop(n), TOTAL_ROWS);
assert(n, boundaries);
}
};
describe('When incrementing scroll by n*rowHeight', () => {
it('should increase rowVisibleStartIdx by n rows', () => {
const getScrollTop = n => n * ROW_HEIGHT;
scrollDown(getScrollTop, (n, { rowVisibleStartIdx }) => {
expect(rowVisibleStartIdx).toBe(n);
});
});
it('should increase rowVisibleEndIdx by (n + total rendered rows)', () => {
const getScrollTop = n => n * ROW_HEIGHT;
scrollDown(getScrollTop, (n, { rowVisibleEndIdx }) => {
expect(rowVisibleEndIdx).toBe(EXPECTED_NUMBER_VISIBLE_ROWS + n);
});
});
});
describe('When incrementing scroll by a decimal number within 0.5 buffer of n*rowHeight', () => {
it('should increase rowVisibleEndIdx by (n + total rendered rows)', () => {
const clientScrollError = 0.5;
const getScrollTop = n => (n * ROW_HEIGHT) - clientScrollError;
scrollDown(getScrollTop, (n, { rowVisibleEndIdx }) => {
expect(rowVisibleEndIdx).toBe(EXPECTED_NUMBER_VISIBLE_ROWS + n);
});
});
it('should increase rowVisibleEndIdx by n rows', () => {
const clientScrollError = 0.5;
const getScrollTop = n => (n * ROW_HEIGHT) - clientScrollError;
scrollDown(getScrollTop, (n, { rowVisibleStartIdx }) => {
expect(rowVisibleStartIdx).toBe(n);
});
});
});
});
describe('getNonFrozenVisibleColStartIdx', () => {
it('should return 0 if no frozen columns and grid not scrolled left', () => {
const scrollLeft = 0;
const colVisibleStartIdx = getNonFrozenVisibleColStartIdx(getColumns(), scrollLeft);
expect(colVisibleStartIdx).toBe(0);
});
it('should return first fully visible column when scrolled left', () => {
const scrollLeft = 100;
const colVisibleStartIdx = getNonFrozenVisibleColStartIdx(getColumns(), scrollLeft);
expect(colVisibleStartIdx).toBe(1);
});
it('should return first partially visible column when scrolled left (left bound)', () => {
const scrollLeft = 99;
const colVisibleStartIdx = getNonFrozenVisibleColStartIdx(getColumns(), scrollLeft);
expect(colVisibleStartIdx).toBe(0);
});
it('should return first partially visible column when scrolled left (right bound)', () => {
const scrollLeft = 101;
const colVisibleStartIdx = getNonFrozenVisibleColStartIdx(getColumns(), scrollLeft);
expect(colVisibleStartIdx).toBe(1);
});
const expectIdxWhenColumsFrozen = (scrollLeft) => {
const columns = getColumns();
columns[1].frozen = true;
const colVisibleStartIdx = getNonFrozenVisibleColStartIdx(columns, scrollLeft);
return expect(colVisibleStartIdx);
};
it('should return first non frozen column that appears after last frozen column', () => {
const scrollLeft = 0;
expectIdxWhenColumsFrozen(scrollLeft).toBe(2);
});
it('should return first fully visible non frozen column that appears after last frozen column when scrolled left', () => {
const scrollLeft = 200;
expectIdxWhenColumsFrozen(scrollLeft).toBe(4);
});
it('should return first partially visible non frozen column that appears after last frozen column when scrolled left', () => {
const scrollLeft = 201;
expectIdxWhenColumsFrozen(scrollLeft).toBe(4);
});
it('should return first partially visible non frozen column that appears after last frozen column when scrolled left', () => {
const scrollLeft = 199;
expectIdxWhenColumsFrozen(scrollLeft).toBe(3);
});
});
describe('getScrollDirection', () => {
it('should return SCROLL_DIRECTION.DOWN iF previous scrollTop is less than current scrollTop', () => {
const prevScroll = { scrollTop: 100 };
const currentScrollTop = 200;
const currentScrollLeft = 0;
expect(getScrollDirection(prevScroll, currentScrollTop, currentScrollLeft)).toBe(SCROLL_DIRECTION.DOWN);
});
it('should return SCROLL_DIRECTION.UP iF previous scrollTop is greater than current scrollTop', () => {
const prevScroll = { scrollTop: 200 };
const currentScrollTop = 0;
const currentScrollLeft = 0;
expect(getScrollDirection(prevScroll, currentScrollTop, currentScrollLeft)).toBe(SCROLL_DIRECTION.UP);
});
it('should return SCROLL_DIRECTION.RIGHT iF previous scrollLeft is less than current scrollLeft', () => {
const prevScroll = { scrollLeft: 200 };
const currentScrollTop = 0;
const currentScrollLeft = 400;
expect(getScrollDirection(prevScroll, currentScrollTop, currentScrollLeft)).toBe(SCROLL_DIRECTION.RIGHT);
});
it('should return SCROLL_DIRECTION.LEFT iF previous scrollLeft is greater than current scrollLeft', () => {
const prevScroll = { scrollLeft: 200 };
const currentScrollTop = 0;
const currentScrollLeft = 0;
expect(getScrollDirection(prevScroll, currentScrollTop, currentScrollLeft)).toBe(SCROLL_DIRECTION.LEFT);
});
it('should return SCROLL_DIRECTION.NONE if current scroll is equal to previous scroll', () => {
const prevScroll = { scrollLeft: 200, scrollTop: 0 };
const currentScrollTop = 0;
const currentScrollLeft = 200;
expect(getScrollDirection(prevScroll, currentScrollTop, currentScrollLeft)).toBe(SCROLL_DIRECTION.NONE);
});
});
describe('getRowOverscanStartIdx', () => {
const rowVisibleStartIdx = 2;
it('should return rowVisibleStartIdx - OVERSCAN_ROWS if scroll direction is upwards', () => {
expect(getRowOverscanStartIdx(SCROLL_DIRECTION.UP, rowVisibleStartIdx)).toBe(rowVisibleStartIdx - OVERSCAN_ROWS);
});
it('should return rowVisibleStartIdx if scroll direction is left', () => {
expect(getRowOverscanStartIdx(SCROLL_DIRECTION.LEFT, rowVisibleStartIdx)).toBe(rowVisibleStartIdx);
});
it('should return rowVisibleStartIdx if scroll direction is right', () => {
expect(getRowOverscanStartIdx(SCROLL_DIRECTION.RIGHT, rowVisibleStartIdx)).toBe(rowVisibleStartIdx);
});
it('should return rowVisibleStartIdx if scroll direction is downwards', () => {
expect(getRowOverscanStartIdx(SCROLL_DIRECTION.DOWN, rowVisibleStartIdx)).toBe(rowVisibleStartIdx);
});
it('should return 0 if rowVisibleStartIdx negative', () => {
expect(getRowOverscanStartIdx(SCROLL_DIRECTION.DOWN, -1)).toBe(0);
});
});
describe('getRowOverscanEndIdx', () => {
const rowVisibleEndIdx = 20;
const totalNumberOfRows = 30;
it('should return rowVisibleStartIdx + OVERSCAN_ROWS if scroll direction is downward', () => {
expect(getRowOverscanEndIdx(SCROLL_DIRECTION.DOWN, rowVisibleEndIdx, totalNumberOfRows)).toBe(rowVisibleEndIdx + OVERSCAN_ROWS);
});
it('should return totalNumberOfRows if scroll direction is downward and rowVisibleEndIdx == totalNumberRows', () => {
expect(getRowOverscanEndIdx(SCROLL_DIRECTION.DOWN, totalNumberOfRows, totalNumberOfRows)).toBe(totalNumberOfRows);
});
it('should return rowVisibleEndIdx if scroll direction is left', () => {
expect(getRowOverscanEndIdx(SCROLL_DIRECTION.LEFT, rowVisibleEndIdx, totalNumberOfRows)).toBe(rowVisibleEndIdx);
});
it('should return rowVisibleEndIdx if scroll direction is right', () => {
expect(getRowOverscanEndIdx(SCROLL_DIRECTION.RIGHT, rowVisibleEndIdx, totalNumberOfRows)).toBe(rowVisibleEndIdx);
});
it('should return rowVisibleEndIdx if scroll direction is upwards', () => {
expect(getRowOverscanEndIdx(SCROLL_DIRECTION.UP, rowVisibleEndIdx, totalNumberOfRows)).toBe(rowVisibleEndIdx);
});
});
describe('getColOverscanStartIdx', () => {
const colVisibleStartIdx = 5;
const lastFrozenColumnIdx = -1;
it('should return colVisibleStartIdx if scroll direction is downward', () => {
expect(getColOverscanStartIdx(SCROLL_DIRECTION.DOWN, colVisibleStartIdx, lastFrozenColumnIdx)).toBe(colVisibleStartIdx);
});
it('should return colVisibleStartIdx if scroll direction is upwards', () => {
expect(getColOverscanStartIdx(SCROLL_DIRECTION.UP, colVisibleStartIdx, lastFrozenColumnIdx)).toBe(colVisibleStartIdx);
});
it('should return 0 if scroll direction is left', () => {
expect(getColOverscanStartIdx(SCROLL_DIRECTION.LEFT, colVisibleStartIdx, lastFrozenColumnIdx)).toBe(0);
});
it('should return 0 if scroll direction is right', () => {
expect(getColOverscanStartIdx(SCROLL_DIRECTION.RIGHT, colVisibleStartIdx, lastFrozenColumnIdx)).toBe(0);
});
});
describe('getColOverscanEndIdx', () => {
const colVisibleEndIdx = 20;
const totalNumberOfColumns = 30;
it('should return colVisibleStartIdx if scroll direction is downward', () => {
expect(getColOverscanEndIdx(SCROLL_DIRECTION.DOWN, colVisibleEndIdx, totalNumberOfColumns)).toBe(colVisibleEndIdx);
});
it('should return colVisibleEndIdx if scroll direction is upwards', () => {
expect(getColOverscanEndIdx(SCROLL_DIRECTION.UP, colVisibleEndIdx, totalNumberOfColumns)).toBe(colVisibleEndIdx);
});
it('should return totalNumberOfColumns if scroll direction is left', () => {
expect(getColOverscanEndIdx(SCROLL_DIRECTION.LEFT, colVisibleEndIdx, totalNumberOfColumns)).toBe(totalNumberOfColumns);
});
it('should return totalNumberOfColumns if scroll direction is right', () => {
expect(getColOverscanEndIdx(SCROLL_DIRECTION.RIGHT, colVisibleEndIdx, totalNumberOfColumns)).toBe(totalNumberOfColumns);
});
});
});
});