import _classCallCheck from "@babel/runtime/helpers/classCallCheck"; import _createClass from "@babel/runtime/helpers/createClass"; import _possibleConstructorReturn from "@babel/runtime/helpers/possibleConstructorReturn"; import _getPrototypeOf from "@babel/runtime/helpers/getPrototypeOf"; import _inherits from "@babel/runtime/helpers/inherits"; import _regeneratorRuntime from "@babel/runtime/regenerator"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; import _extends from "@babel/runtime/helpers/extends"; function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(source, true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(source).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } import * as React from 'react'; import { findDOMNode } from 'react-dom'; import { Simulate } from 'react-dom/test-utils'; import TestRenderer from 'react-test-renderer'; import { render } from '../TestUtils'; import Grid, { DEFAULT_SCROLLING_RESET_TIME_INTERVAL } from './Grid'; import defaultCellRangeRenderer from './defaultCellRangeRenderer'; import { CellMeasurer, CellMeasurerCache } from '../CellMeasurer'; import { SCROLL_DIRECTION_BACKWARD, SCROLL_DIRECTION_FORWARD } from './defaultOverscanIndicesGetter'; import { getMaxElementSize } from './utils/maxElementSize.js'; var DEFAULT_COLUMN_WIDTH = 50; var DEFAULT_HEIGHT = 100; var DEFAULT_ROW_HEIGHT = 20; var DEFAULT_WIDTH = 200; var NUM_ROWS = 100; var NUM_COLUMNS = 50; function getScrollbarSize0() { return 0; } function getScrollbarSize20() { return 20; } describe('Grid', function () { function defaultCellRenderer(_ref) { var columnIndex = _ref.columnIndex, key = _ref.key, rowIndex = _ref.rowIndex, style = _ref.style; return React.createElement("div", { className: "gridItem", key: key, style: style }, "row:".concat(rowIndex, ", column:").concat(columnIndex)); } function simulateScroll(_ref2) { var grid = _ref2.grid, _ref2$scrollLeft = _ref2.scrollLeft, scrollLeft = _ref2$scrollLeft === void 0 ? 0 : _ref2$scrollLeft, _ref2$scrollTop = _ref2.scrollTop, scrollTop = _ref2$scrollTop === void 0 ? 0 : _ref2$scrollTop; var target = { scrollLeft: scrollLeft, scrollTop: scrollTop }; grid._scrollingContainer = target; // HACK to work around _onScroll target check Simulate.scroll(findDOMNode(grid), { target: target }); } function getMarkup() { var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; return React.createElement(Grid, _extends({ cellRenderer: defaultCellRenderer, columnCount: NUM_COLUMNS, columnWidth: DEFAULT_COLUMN_WIDTH, getScrollbarSize: getScrollbarSize0, height: DEFAULT_HEIGHT, overscanColumnCount: 0, overscanRowCount: 0, autoHeight: false, rowHeight: DEFAULT_ROW_HEIGHT, rowCount: NUM_ROWS, width: DEFAULT_WIDTH }, props)); } describe('number of rendered children', function () { it('should render enough children to fill the available area', function () { var rendered = findDOMNode(render(getMarkup())); expect(rendered.querySelectorAll('.gridItem').length).toEqual(20); // 5 rows x 4 columns }); it('should not render more rows than available if the area is not filled', function () { var rendered = findDOMNode(render(getMarkup({ rowCount: 2 }))); expect(rendered.querySelectorAll('.gridItem').length).toEqual(8); // 2 rows x 4 columns }); it('should not render more columns than available if the area is not filled', function () { var rendered = findDOMNode(render(getMarkup({ columnCount: 2 }))); expect(rendered.querySelectorAll('.gridItem').length).toEqual(10); // 5 rows x 2 columns }); // Small performance tweak added in 5.5.6 it('should not render/parent cells that are null or false', function () { function cellRenderer(_ref3) { var columnIndex = _ref3.columnIndex, key = _ref3.key, rowIndex = _ref3.rowIndex, style = _ref3.style; if (columnIndex === 0) { return null; } else if (rowIndex === 0) { return false; } else { return React.createElement("div", { className: "cell", key: key, style: style }, "row:".concat(rowIndex, ", column:").concat(columnIndex)); } } var rendered = findDOMNode(render(getMarkup({ columnCount: 3, overscanColumnCount: 0, overscanRowCount: 0, rowCount: 3, cellRenderer: cellRenderer }))); expect(rendered.querySelectorAll('.cell').length).toEqual(4); // [1,1], [1,2], [2,1], and [2,2] expect(rendered.textContent).not.toContain('column:0'); expect(rendered.textContent).not.toContain('row:0'); }); it('should scroll to the last existing point when rows are removed', function () { var grid = render(getMarkup({ rowCount: 15 })); simulateScroll({ grid: grid, scrollTop: 200 }); var updatedGrid = render(getMarkup({ rowCount: 10 })); expect(updatedGrid.state.scrollTop).toEqual(100); }); it('should scroll to the last existing point when columns are removed', function () { var grid = render(getMarkup({ columnCount: 12 })); simulateScroll({ grid: grid, scrollLeft: 400 }); var updatedGrid = render(getMarkup({ columnCount: 8 })); expect(updatedGrid.state.scrollLeft).toEqual(200); }); it('should not scroll unseen rows are removed', function () { render(getMarkup({ rowCount: 15 })); var updatedGrid = render(getMarkup({ rowCount: 10 })); expect(updatedGrid.state.scrollTop).toEqual(0); }); it('should not scroll when unseen columns are removed', function () { render(getMarkup({ columnCount: 12 })); var updatedGrid = render(getMarkup({ columnCount: 8 })); expect(updatedGrid.state.scrollLeft).toEqual(0); }); }); describe('shows and hides scrollbars based on rendered content', function () { it('should set overflowX:hidden if columns fit within the available width and y-axis has no scrollbar', function () { var rendered = findDOMNode(render(getMarkup({ columnCount: 4, getScrollbarSize: getScrollbarSize20, rowCount: 5 }))); expect(rendered.style.overflowX).toEqual('hidden'); }); it('should set overflowX:hidden if columns and y-axis scrollbar fit within the available width', function () { var rendered = findDOMNode(render(getMarkup({ columnCount: 4, getScrollbarSize: getScrollbarSize20, width: 200 + getScrollbarSize20() }))); expect(rendered.style.overflowX).toEqual('hidden'); }); it('should leave overflowX:auto if columns require more than the available width', function () { var rendered = findDOMNode(render(getMarkup({ columnCount: 4, getScrollbarSize: getScrollbarSize20, width: 200 - 1, rowCount: 5 }))); expect(rendered.style.overflowX).not.toEqual('hidden'); }); it('should leave overflowX:auto if columns and y-axis scrollbar require more than the available width', function () { var rendered = findDOMNode(render(getMarkup({ columnCount: 4, getScrollbarSize: getScrollbarSize20, width: 200 + getScrollbarSize20() - 1 }))); expect(rendered.style.overflowX).not.toEqual('hidden'); }); it('should set overflowY:hidden if rows fit within the available width and xaxis has no scrollbar', function () { var rendered = findDOMNode(render(getMarkup({ getScrollbarSize: getScrollbarSize20, rowCount: 5, columnCount: 4 }))); expect(rendered.style.overflowY).toEqual('hidden'); }); it('should set overflowY:hidden if rows and x-axis scrollbar fit within the available width', function () { var rendered = findDOMNode(render(getMarkup({ getScrollbarSize: getScrollbarSize20, rowCount: 5, height: 100 + getScrollbarSize20() }))); expect(rendered.style.overflowY).toEqual('hidden'); }); it('should leave overflowY:auto if rows require more than the available width', function () { var rendered = findDOMNode(render(getMarkup({ getScrollbarSize: getScrollbarSize20, rowCount: 5, height: 100 - 1, columnCount: 4 }))); expect(rendered.style.overflowY).not.toEqual('hidden'); }); it('should leave overflowY:auto if rows and x-axis scrollbar require more than the available width', function () { var rendered = findDOMNode(render(getMarkup({ getScrollbarSize: getScrollbarSize20, rowCount: 5, height: 100 + getScrollbarSize20() - 1 }))); expect(rendered.style.overflowY).not.toEqual('hidden'); }); it('should accept styles that overwrite calculated ones', function () { var rendered = findDOMNode(render(getMarkup({ columnCount: 1, getScrollbarSize: getScrollbarSize20, height: 1, rowCount: 1, style: { overflowY: 'visible', overflowX: 'visible' }, width: 1 }))); expect(rendered.style.overflowY).toEqual('visible'); expect(rendered.style.overflowX).toEqual('visible'); }); }); /** Tests scrolling via initial props */ describe(':scrollToColumn and :scrollToRow', function () { it('should scroll to the left', function () { var grid = render(getMarkup({ scrollToColumn: 0 })); expect(grid.state.scrollLeft).toEqual(0); }); it('should scroll over to the middle', function () { var grid = render(getMarkup({ scrollToColumn: 24 })); // 50 columns * 50 item width = 2,500 total item width // 4 columns can be visible at a time and :scrollLeft is initially 0, // So the minimum amount of scrolling leaves the 25th item at the right (just scrolled into view). expect(grid.state.scrollLeft).toEqual(1050); }); it('should scroll to the far right', function () { var grid = render(getMarkup({ scrollToColumn: 49 })); // 50 columns * 50 item width = 2,500 total item width // Target offset for the last item then is 2,500 - 200 expect(grid.state.scrollLeft).toEqual(2300); }); it('should scroll to the top', function () { var grid = render(getMarkup({ scrollToRow: 0 })); expect(grid.state.scrollTop).toEqual(0); }); it('should scroll down to the middle', function () { var grid = render(getMarkup({ scrollToRow: 49 })); // 100 rows * 20 item height = 2,000 total item height // 5 rows can be visible at a time and :scrollTop is initially 0, // So the minimum amount of scrolling leaves the 50th item at the bottom (just scrolled into view). expect(grid.state.scrollTop).toEqual(900); }); it('should scroll to the bottom', function () { var grid = render(getMarkup({ scrollToRow: 99 })); // 100 rows * 20 item height = 2,000 total item height // Target offset for the last item then is 2,000 - 100 expect(grid.state.scrollTop).toEqual(1900); }); it('should scroll to a row and column just added', function () { var grid = render(getMarkup()); expect(grid.state.scrollLeft).toEqual(0); expect(grid.state.scrollTop).toEqual(0); grid = render(getMarkup({ columnCount: NUM_COLUMNS + 1, rowCount: NUM_ROWS + 1, scrollToColumn: NUM_COLUMNS, scrollToRow: NUM_ROWS })); expect(grid.state.scrollLeft).toEqual(2350); expect(grid.state.scrollTop).toEqual(1920); }); it('should scroll back to a newly-added cell without a change in prop', function () { var grid = render(getMarkup({ columnCount: NUM_COLUMNS, rowCount: NUM_ROWS, scrollToColumn: NUM_COLUMNS, scrollToRow: NUM_ROWS })); grid = render(getMarkup({ columnCount: NUM_COLUMNS + 1, rowCount: NUM_ROWS + 1, scrollToColumn: NUM_COLUMNS, scrollToRow: NUM_ROWS })); expect(grid.state.scrollLeft).toEqual(2350); expect(grid.state.scrollTop).toEqual(1920); }); it('should scroll to the correct position for :scrollToAlignment "start"', function () { var grid = render(getMarkup({ scrollToAlignment: 'start', scrollToColumn: 24, scrollToRow: 49 })); // 50 columns * 50 item width = 2,500 total item width // 100 rows * 20 item height = 2,000 total item height // 4 columns and 5 rows can be visible at a time. // The minimum amount of scrolling leaves the specified cell in the bottom/right corner (just scrolled into view). // Since alignment is set to "start" we should scroll past this point until the cell is aligned top/left. expect(grid.state.scrollLeft).toEqual(1200); expect(grid.state.scrollTop).toEqual(980); }); it('should scroll to the correct position for :scrollToAlignment "end"', function () { render(getMarkup({ scrollToColumn: 99, scrollToRow: 99 })); var grid = render(getMarkup({ scrollToAlignment: 'end', scrollToColumn: 24, scrollToRow: 49 })); // 50 columns * 50 item width = 2,500 total item width // 100 rows * 20 item height = 2,000 total item height // We first scroll past the specified cell and then back. // The minimum amount of scrolling then should leave the specified cell in the top/left corner (just scrolled into view). // Since alignment is set to "end" we should scroll past this point until the cell is aligned bottom/right. expect(grid.state.scrollLeft).toEqual(1050); expect(grid.state.scrollTop).toEqual(900); }); it('should scroll to the correct position for :scrollToAlignment "center"', function () { render(getMarkup({ scrollToColumn: 99, scrollToRow: 99 })); var grid = render(getMarkup({ scrollToAlignment: 'center', scrollToColumn: 24, scrollToRow: 49 })); // 50 columns * 50 item width = 2,500 total item width // Viewport width is 200 // Column 24 starts at 1,200, center point at 1,225, so... expect(grid.state.scrollLeft).toEqual(1125); // 100 rows * 20 item height = 2,000 total item height // Viewport height is 100 // Row 49 starts at 980, center point at 990, so... expect(grid.state.scrollTop).toEqual(940); }); // Tests issue #691 it('should set the correct :scrollLeft after height increases from 0', function () { render.unmount(); expect(findDOMNode(render(getMarkup({ height: 0, scrollToColumn: 24 }))).scrollLeft || 0).toEqual(0); expect(findDOMNode(render(getMarkup({ height: 100, scrollToColumn: 24 }))).scrollLeft).toEqual(1050); }); // Tests issue #691 it('should set the correct :scrollTop after width increases from 0', function () { render.unmount(); expect(findDOMNode(render(getMarkup({ scrollToRow: 49, width: 0 }))).scrollTop || 0).toEqual(0); expect(findDOMNode(render(getMarkup({ scrollToRow: 49, width: 100 }))).scrollTop).toEqual(900); }); // Tests issue #218 it('should set the correct :scrollTop after row and column counts increase from 0', function () { var expectedScrollTop = 100 * DEFAULT_ROW_HEIGHT - DEFAULT_HEIGHT + DEFAULT_ROW_HEIGHT; render(getMarkup({ columnCount: 0, rowCount: 150, scrollToRow: 100 })); expect(findDOMNode(render(getMarkup({ columnCount: 150, rowCount: 150, scrollToRow: 100 }))).scrollTop).toEqual(expectedScrollTop); }); it('should support scrollToCell() public method', function () { var grid = render(getMarkup()); expect(grid.state.scrollLeft).toEqual(0); expect(grid.state.scrollTop).toEqual(0); grid.scrollToCell({ columnIndex: 24, rowIndex: 49 }); // 50 columns * 50 item width = 2,500 total item width // 4 columns can be visible at a time and :scrollLeft is initially 0, // So the minimum amount of scrolling leaves the 25th item at the right (just scrolled into view). expect(grid.state.scrollLeft).toEqual(1050); // 100 rows * 20 item height = 2,000 total item height // 5 rows can be visible at a time and :scrollTop is initially 0, // So the minimum amount of scrolling leaves the 50th item at the bottom (just scrolled into view). expect(grid.state.scrollTop).toEqual(900); // Change column without affecting row grid.scrollToCell({ columnIndex: 49 }); expect(grid.state.scrollLeft).toEqual(2300); expect(grid.state.scrollTop).toEqual(900); // Change row without affecting column grid.scrollToCell({ rowIndex: 99 }); expect(grid.state.scrollLeft).toEqual(2300); expect(grid.state.scrollTop).toEqual(1900); }); it('should support scrollToPosition() public method', function () { var grid = render(getMarkup()); expect(grid.state.scrollLeft).toEqual(0); expect(grid.state.scrollTop).toEqual(0); grid.scrollToPosition({ scrollLeft: 50, scrollTop: 100 }); expect(grid.state.scrollLeft).toEqual(50); expect(grid.state.scrollTop).toEqual(100); // Change column without affecting row grid.scrollToPosition({ scrollLeft: 25 }); expect(grid.state.scrollLeft).toEqual(25); expect(grid.state.scrollTop).toEqual(100); // Change row without affecting column grid.scrollToPosition({ scrollTop: 50 }); expect(grid.state.scrollLeft).toEqual(25); expect(grid.state.scrollTop).toEqual(50); }); it('should support handleScrollEvent() public method', function () { var grid = render(getMarkup()); expect(grid.state.scrollLeft).toEqual(0); expect(grid.state.scrollTop).toEqual(0); grid.handleScrollEvent({ scrollLeft: 50, scrollTop: 100 }); expect(grid.state.isScrolling).toEqual(true); expect(grid.state.scrollLeft).toEqual(50); expect(grid.state.scrollTop).toEqual(100); }); it('should support getOffsetForCell() public method', function () { var grid = render(getMarkup()); var _grid$getOffsetForCel = grid.getOffsetForCell({ columnIndex: 24, rowIndex: 49 }), scrollLeft = _grid$getOffsetForCel.scrollLeft, scrollTop = _grid$getOffsetForCel.scrollTop; // 50 columns * 50 item width = 2,500 total item width // 4 columns can be visible at a time and :scrollLeft is initially 0, // So the minimum amount of scrolling leaves the 25th item at the right (just scrolled into view). expect(scrollLeft).toEqual(1050); // 100 rows * 20 item height = 2,000 total item height // 5 rows can be visible at a time and :scrollTop is initially 0, // So the minimum amount of scrolling leaves the 50th item at the bottom (just scrolled into view). expect(scrollTop).toEqual(900); }); it('should support getTotalRowsHeight() public method', function () { var grid = render(getMarkup()); grid.recomputeGridSize(); var totalHeight = grid.getTotalRowsHeight(); // 100 rows * 20 item height = 2,000 total item height expect(totalHeight).toEqual(2000); }); it('should support getTotalColumnsWidth() public method', function () { var grid = render(getMarkup()); grid.recomputeGridSize(); var totalWidth = grid.getTotalColumnsWidth(); // 50 columns * 50 item width = 2,500 total item width expect(totalWidth).toEqual(2500); }); // See issue #565 it('should update scroll position to account for changed cell sizes within a function prop wrapper', function () { var _rowHeight = 20; var props = { height: 100, rowCount: 100, rowHeight: function rowHeight(_ref4) { var index = _ref4.index; return index === 99 ? _rowHeight : 20; }, scrollToRow: 99 }; var grid = render(getMarkup(props)); var node = findDOMNode(grid); expect(node.scrollTop).toBe(1900); _rowHeight = 40; grid.recomputeGridSize({ rowIndex: 99 }); expect(node.scrollTop).toBe(1920); }); it('should not restore scrollLeft when scrolling left and recomputeGridSize with columnIndex smaller than scrollToColumn', function () { var props = { columnWidth: 50, columnCount: 100, height: 100, rowCount: 100, rowHeight: 20, scrollToColumn: 50, scrollToRow: 50, width: 100 }; var grid = render(getMarkup(props)); expect(grid.state.scrollLeft).toEqual(2450); simulateScroll({ grid: grid, scrollLeft: 2250 }); expect(grid.state.scrollLeft).toEqual(2250); expect(grid.state.scrollDirectionHorizontal).toEqual(SCROLL_DIRECTION_BACKWARD); grid.recomputeGridSize({ columnIndex: 30 }); expect(grid.state.scrollLeft).toEqual(2250); }); it('should not restore scrollTop when scrolling up and recomputeGridSize with rowIndex smaller than scrollToRow', function () { var props = { columnWidth: 50, columnCount: 100, height: 100, rowCount: 100, rowHeight: 20, scrollToColumn: 50, scrollToRow: 50, width: 100 }; var grid = render(getMarkup(props)); expect(grid.state.scrollTop).toEqual(920); simulateScroll({ grid: grid, scrollTop: 720 }); expect(grid.state.scrollTop).toEqual(720); expect(grid.state.scrollDirectionVertical).toEqual(SCROLL_DIRECTION_BACKWARD); grid.recomputeGridSize({ rowIndex: 20 }); expect(grid.state.scrollTop).toEqual(720); }); it('should restore scroll offset for column when row count increases from 0 (and vice versa)', function () { var props = { columnWidth: 50, columnCount: 100, height: 100, rowCount: 100, rowHeight: 20, scrollToColumn: 50, scrollToRow: 50, width: 100 }; var grid = render(getMarkup(props)); expect(grid.state.scrollLeft).toEqual(2450); expect(grid.state.scrollTop).toEqual(920); render(getMarkup(_objectSpread({}, props, { columnCount: 0 }))); expect(grid.state.scrollLeft).toEqual(0); expect(grid.state.scrollTop).toEqual(0); render(getMarkup(props)); expect(grid.state.scrollLeft).toEqual(2450); expect(grid.state.scrollTop).toEqual(920); render(getMarkup(_objectSpread({}, props, { rowCount: 0 }))); expect(grid.state.scrollLeft).toEqual(0); expect(grid.state.scrollTop).toEqual(0); render(getMarkup(props)); expect(grid.state.scrollLeft).toEqual(2450); expect(grid.state.scrollTop).toEqual(920); }); it('should take scrollbar size into account when aligning cells', function () { var grid = render(getMarkup({ columnWidth: 50, columnCount: 100, getScrollbarSize: getScrollbarSize20, height: 100, rowCount: 100, rowHeight: 20, scrollToColumn: 50, scrollToRow: 50, width: 100 })); expect(grid.state.scrollLeft).toEqual(2450 + getScrollbarSize20()); expect(grid.state.scrollTop).toEqual(920 + getScrollbarSize20()); }); }); describe('property updates', function () { it('should update :scrollToColumn position when :columnWidth changes', function () { var grid = findDOMNode(render(getMarkup({ scrollToColumn: 25 }))); expect(grid.textContent).toContain('column:25'); // Making columns taller pushes name off/beyond the scrolled area grid = findDOMNode(render(getMarkup({ scrollToColumn: 25, columnWidth: 20 }))); expect(grid.textContent).toContain('column:25'); }); it('should update :scrollToRow position when :rowHeight changes', function () { var grid = findDOMNode(render(getMarkup({ scrollToRow: 50 }))); expect(grid.textContent).toContain('row:50'); // Making rows taller pushes name off/beyond the scrolled area grid = findDOMNode(render(getMarkup({ scrollToRow: 50, rowHeight: 20 }))); expect(grid.textContent).toContain('row:50'); }); it('should update :scrollToColumn position when :width changes', function () { var grid = findDOMNode(render(getMarkup({ scrollToColumn: 25 }))); expect(grid.textContent).toContain('column:25'); // Making the grid narrower leaves only room for 1 item grid = findDOMNode(render(getMarkup({ scrollToColumn: 25, width: 50 }))); expect(grid.textContent).toContain('column:25'); }); it('should update :scrollToRow position when :height changes', function () { var grid = findDOMNode(render(getMarkup({ scrollToRow: 50 }))); expect(grid.textContent).toContain('row:50'); // Making the grid shorter leaves only room for 1 item grid = findDOMNode(render(getMarkup({ scrollToRow: 50, height: 20 }))); expect(grid.textContent).toContain('row:50'); }); it('should update :scrollToColumn position when :scrollToColumn changes', function () { var grid = findDOMNode(render(getMarkup())); expect(grid.textContent).not.toContain('column:25'); grid = findDOMNode(render(getMarkup({ scrollToColumn: 25 }))); expect(grid.textContent).toContain('column:25'); }); it('should update :scrollToRow position when :scrollToRow changes', function () { var grid = findDOMNode(render(getMarkup())); expect(grid.textContent).not.toContain('row:50'); grid = findDOMNode(render(getMarkup({ scrollToRow: 50 }))); expect(grid.textContent).toContain('row:50'); }); it('should update scroll position if size shrinks smaller than the current scroll', function () { var grid = findDOMNode(render(getMarkup({ scrollToColumn: 250 }))); grid = findDOMNode(render(getMarkup())); grid = findDOMNode(render(getMarkup({ scrollToColumn: 250, columnCount: 10 }))); expect(grid.textContent).toContain('column:9'); }); it('should update scroll position if size shrinks smaller than the current scroll', function () { var grid = findDOMNode(render(getMarkup({ scrollToRow: 500 }))); grid = findDOMNode(render(getMarkup())); grid = findDOMNode(render(getMarkup({ scrollToRow: 500, rowCount: 10 }))); expect(grid.textContent).toContain('row:9'); }); }); describe('noContentRenderer', function () { it('should call :noContentRenderer if :columnCount is 0', function () { var list = findDOMNode(render(getMarkup({ noContentRenderer: function noContentRenderer() { return React.createElement("div", null, "No data"); }, columnCount: 0 }))); expect(list.textContent).toEqual('No data'); }); it('should call :noContentRenderer if :rowCount is 0', function () { var list = findDOMNode(render(getMarkup({ noContentRenderer: function noContentRenderer() { return React.createElement("div", null, "No data"); }, rowCount: 0 }))); expect(list.textContent).toEqual('No data'); }); // Sanity check for bvaughn/react-virtualized/pull/348 it('should render an empty body if :rowCount or :columnCount changes to 0', function () { function noContentRenderer() { return React.createElement("div", null, "No data"); } var list = findDOMNode(render(getMarkup({ noContentRenderer: noContentRenderer }))); expect(list.textContent).not.toEqual('No data'); list = findDOMNode(render(getMarkup({ noContentRenderer: noContentRenderer, rowCount: 0 }))); expect(list.textContent).toEqual('No data'); list = findDOMNode(render(getMarkup({ noContentRenderer: noContentRenderer }))); expect(list.textContent).not.toEqual('No data'); list = findDOMNode(render(getMarkup({ columnCount: 0, noContentRenderer: noContentRenderer }))); expect(list.textContent).toEqual('No data'); }); it('should render an empty body if :columnCount is 0 and there is no :noContentRenderer', function () { var list = findDOMNode(render(getMarkup({ columnCount: 0 }))); expect(list.textContent).toEqual(''); }); it('should render an empty body if :rowCount is 0 and there is no :noContentRenderer', function () { var list = findDOMNode(render(getMarkup({ rowCount: 0 }))); expect(list.textContent).toEqual(''); }); it('should render an empty body there is a :noContentRenderer but :height or :width are 0', function () { var list = findDOMNode(render(getMarkup({ height: 0, noContentRenderer: function noContentRenderer() { return React.createElement("div", null, "No data"); } }))); expect(list.textContent).toEqual(''); list = findDOMNode(render(getMarkup({ noContentRenderer: function noContentRenderer() { return React.createElement("div", null, "No data"); }, width: 0 }))); expect(list.textContent).toEqual(''); }); }); describe('onSectionRendered', function () { it('should call :onSectionRendered if at least one cell is rendered', function () { var columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex; render(getMarkup({ onSectionRendered: function onSectionRendered(params) { var _params; return _params = params, columnStartIndex = _params.columnStartIndex, columnStopIndex = _params.columnStopIndex, rowStartIndex = _params.rowStartIndex, rowStopIndex = _params.rowStopIndex, _params; } })); expect(columnStartIndex).toEqual(0); expect(columnStopIndex).toEqual(3); expect(rowStartIndex).toEqual(0); expect(rowStopIndex).toEqual(4); }); it('should not call :onSectionRendered unless the column or row start or stop indices have changed', function () { var numCalls = 0; var columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex; var onSectionRendered = function onSectionRendered(params) { columnStartIndex = params.columnStartIndex; columnStopIndex = params.columnStopIndex; rowStartIndex = params.rowStartIndex; rowStopIndex = params.rowStopIndex; numCalls++; }; render(getMarkup({ onSectionRendered: onSectionRendered })); expect(numCalls).toEqual(1); expect(columnStartIndex).toEqual(0); expect(columnStopIndex).toEqual(3); expect(rowStartIndex).toEqual(0); expect(rowStopIndex).toEqual(4); render(getMarkup({ onSectionRendered: onSectionRendered })); expect(numCalls).toEqual(1); expect(columnStartIndex).toEqual(0); expect(columnStopIndex).toEqual(3); expect(rowStartIndex).toEqual(0); expect(rowStopIndex).toEqual(4); }); it('should call :onSectionRendered if the row or column start or stop indices have changed', function () { var numCalls = 0; var columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex; var onSectionRendered = function onSectionRendered(params) { columnStartIndex = params.columnStartIndex; columnStopIndex = params.columnStopIndex; rowStartIndex = params.rowStartIndex; rowStopIndex = params.rowStopIndex; numCalls++; }; render(getMarkup({ onSectionRendered: onSectionRendered })); expect(columnStartIndex).toEqual(0); expect(columnStopIndex).toEqual(3); expect(rowStartIndex).toEqual(0); expect(rowStopIndex).toEqual(4); render(getMarkup({ height: 50, onSectionRendered: onSectionRendered })); expect(numCalls).toEqual(2); expect(columnStartIndex).toEqual(0); expect(columnStopIndex).toEqual(3); expect(rowStartIndex).toEqual(0); expect(rowStopIndex).toEqual(2); render(getMarkup({ height: 50, onSectionRendered: onSectionRendered, width: 100 })); expect(numCalls).toEqual(3); expect(columnStartIndex).toEqual(0); expect(columnStopIndex).toEqual(1); expect(rowStartIndex).toEqual(0); expect(rowStopIndex).toEqual(2); }); it('should not call :onSectionRendered if no cells are rendered', function () { var numCalls = 0; render(getMarkup({ height: 0, onSectionRendered: function onSectionRendered() { return numCalls++; } })); expect(numCalls).toEqual(0); }); }); describe(':scrollLeft and :scrollTop properties', function () { it('should render correctly when an initial :scrollLeft and :scrollTop properties are specified', function () { var columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex; findDOMNode(render(getMarkup({ onSectionRendered: function onSectionRendered(params) { var _params2; return _params2 = params, columnStartIndex = _params2.columnStartIndex, columnStopIndex = _params2.columnStopIndex, rowStartIndex = _params2.rowStartIndex, rowStopIndex = _params2.rowStopIndex, _params2; }, scrollLeft: 250, scrollTop: 100 }))); expect(rowStartIndex).toEqual(5); expect(rowStopIndex).toEqual(9); expect(columnStartIndex).toEqual(5); expect(columnStopIndex).toEqual(8); }); it('should render correctly when :scrollLeft and :scrollTop properties are updated', function () { var columnStartIndex, columnStopIndex, rowStartIndex, rowStopIndex; render(getMarkup({ onSectionRendered: function onSectionRendered(params) { var _params3; return _params3 = params, columnStartIndex = _params3.columnStartIndex, columnStopIndex = _params3.columnStopIndex, rowStartIndex = _params3.rowStartIndex, rowStopIndex = _params3.rowStopIndex, _params3; } })); expect(rowStartIndex).toEqual(0); expect(rowStopIndex).toEqual(4); expect(columnStartIndex).toEqual(0); expect(columnStopIndex).toEqual(3); render(getMarkup({ onSectionRendered: function onSectionRendered(params) { var _params4; return _params4 = params, columnStartIndex = _params4.columnStartIndex, columnStopIndex = _params4.columnStopIndex, rowStartIndex = _params4.rowStartIndex, rowStopIndex = _params4.rowStopIndex, _params4; }, scrollLeft: 250, scrollTop: 100 })); expect(rowStartIndex).toEqual(5); expect(rowStopIndex).toEqual(9); expect(columnStartIndex).toEqual(5); expect(columnStopIndex).toEqual(8); }); }); describe('styles, classNames, and ids', function () { it('should use the expected global CSS classNames', function () { var rendered = findDOMNode(render(getMarkup())); expect(rendered.className).toEqual('ReactVirtualized__Grid'); }); it('should use a custom :className if specified', function () { var rendered = findDOMNode(render(getMarkup({ className: 'foo' }))); expect(rendered.className).toContain('foo'); }); it('should use a custom :id if specified', function () { var rendered = findDOMNode(render(getMarkup({ id: 'bar' }))); expect(rendered.getAttribute('id')).toEqual('bar'); }); it('should use a custom :style if specified', function () { var style = { backgroundColor: 'red' }; var rendered = findDOMNode(render(getMarkup({ style: style }))); expect(rendered.style.backgroundColor).toEqual('red'); }); it('should use a custom :containerStyle if specified', function () { var containerStyle = { backgroundColor: 'red' }; var rendered = findDOMNode(render(getMarkup({ containerStyle: containerStyle }))); expect(rendered.querySelector('.ReactVirtualized__Grid__innerScrollContainer').style.backgroundColor).toEqual('red'); }); }); describe('onScroll', function () { it('should trigger callback when component is mounted', function () { var onScrollCalls = []; render(getMarkup({ onScroll: function onScroll(params) { return onScrollCalls.push(params); }, scrollLeft: 50, scrollTop: 100 })); expect(onScrollCalls).toEqual([{ clientHeight: 100, clientWidth: 200, scrollHeight: 2000, scrollLeft: 50, scrollTop: 100, scrollWidth: 2500 }]); }); it('should trigger callback when component scrolls horizontally', function () { var onScrollCalls = []; var grid = render(getMarkup({ onScroll: function onScroll(params) { return onScrollCalls.push(params); } })); simulateScroll({ grid: grid, scrollLeft: 100, scrollTop: 0 }); expect(onScrollCalls.length).toEqual(2); expect(onScrollCalls[1]).toEqual({ clientHeight: 100, clientWidth: 200, scrollHeight: 2000, scrollLeft: 100, scrollTop: 0, scrollWidth: 2500 }); }); it('should trigger callback when component scrolls vertically', function () { var onScrollCalls = []; var grid = render(getMarkup({ onScroll: function onScroll(params) { return onScrollCalls.push(params); } })); simulateScroll({ grid: grid, scrollLeft: 0, scrollTop: 100 }); expect(onScrollCalls.length).toEqual(2); expect(onScrollCalls[1]).toEqual({ clientHeight: 100, clientWidth: 200, scrollHeight: 2000, scrollLeft: 0, scrollTop: 100, scrollWidth: 2500 }); }); it('should trigger callback with scrollLeft of 0 when total columns width is less than width', function () { var onScrollCalls = []; var grid = render(getMarkup({ columnCount: 1, columnWidth: 50, onScroll: function onScroll(params) { return onScrollCalls.push(params); }, scrollLeft: 0, scrollTop: 10, width: 200 })); simulateScroll({ grid: grid, scrollLeft: 0, scrollTop: 0 }); expect(onScrollCalls.length).toEqual(2); expect(onScrollCalls[1]).toEqual({ clientHeight: 100, clientWidth: 200, scrollHeight: 2000, scrollLeft: 0, scrollTop: 0, scrollWidth: 50 }); }); it('should trigger callback with scrollTop of 0 when total rows height is less than height', function () { var onScrollCalls = []; var grid = render(getMarkup({ rowCount: 1, rowHeight: 50, onScroll: function onScroll(params) { return onScrollCalls.push(params); }, scrollLeft: 0, scrollTop: 10, height: 200 })); simulateScroll({ grid: grid, scrollLeft: 0, scrollTop: 0 }); expect(onScrollCalls.length).toEqual(2); expect(onScrollCalls[1]).toEqual({ clientHeight: 200, clientWidth: 200, scrollHeight: 50, scrollLeft: 0, scrollTop: 0, scrollWidth: 2500 }); }); // Support use-cases like WindowScroller; enable them to stay in sync with scroll-to-cell changes. it('should trigger when :scrollToColumn or :scrollToRow are changed via props', function () { var onScrollCalls = []; render(getMarkup()); render(getMarkup({ onScroll: function onScroll(params) { return onScrollCalls.push(params); }, scrollToColumn: 24, scrollToRow: 49 })); expect(onScrollCalls).toEqual([{ clientHeight: 100, clientWidth: 200, scrollHeight: 2000, scrollLeft: 1050, scrollTop: 900, scrollWidth: 2500 }]); }); }); describe('overscanColumnCount & overscanRowCount', function () { function createHelper() { var _columnOverscanStartIndex, _columnOverscanStopIndex, _columnStartIndex, _columnStopIndex, _rowOverscanStartIndex, _rowOverscanStopIndex, _rowStartIndex, _rowStopIndex; function onSectionRendered(params) { _columnOverscanStartIndex = params.columnOverscanStartIndex; _columnOverscanStopIndex = params.columnOverscanStopIndex; _columnStartIndex = params.columnStartIndex; _columnStopIndex = params.columnStopIndex; _rowOverscanStartIndex = params.rowOverscanStartIndex; _rowOverscanStopIndex = params.rowOverscanStopIndex; _rowStartIndex = params.rowStartIndex; _rowStopIndex = params.rowStopIndex; } return { columnOverscanStartIndex: function columnOverscanStartIndex() { return _columnOverscanStartIndex; }, columnOverscanStopIndex: function columnOverscanStopIndex() { return _columnOverscanStopIndex; }, columnStartIndex: function columnStartIndex() { return _columnStartIndex; }, columnStopIndex: function columnStopIndex() { return _columnStopIndex; }, onSectionRendered: onSectionRendered, rowOverscanStartIndex: function rowOverscanStartIndex() { return _rowOverscanStartIndex; }, rowOverscanStopIndex: function rowOverscanStopIndex() { return _rowOverscanStopIndex; }, rowStartIndex: function rowStartIndex() { return _rowStartIndex; }, rowStopIndex: function rowStopIndex() { return _rowStopIndex; } }; } it('should not overscan if disabled', function () { var helper = createHelper(); render(getMarkup({ onSectionRendered: helper.onSectionRendered })); expect(helper.columnOverscanStartIndex()).toEqual(helper.columnStartIndex()); expect(helper.columnOverscanStopIndex()).toEqual(helper.columnStopIndex()); expect(helper.rowOverscanStartIndex()).toEqual(helper.rowStartIndex()); expect(helper.rowOverscanStopIndex()).toEqual(helper.rowStopIndex()); }); it('should overscan the specified amount', function () { var helper = createHelper(); render(getMarkup({ onSectionRendered: helper.onSectionRendered, overscanColumnCount: 2, overscanRowCount: 5, scrollToColumn: 25, scrollToRow: 50 })); expect(helper.columnOverscanStartIndex()).toEqual(22); expect(helper.columnOverscanStopIndex()).toEqual(27); expect(helper.columnStartIndex()).toEqual(22); expect(helper.columnStopIndex()).toEqual(25); expect(helper.rowOverscanStartIndex()).toEqual(46); expect(helper.rowOverscanStopIndex()).toEqual(55); expect(helper.rowStartIndex()).toEqual(46); expect(helper.rowStopIndex()).toEqual(50); }); it('should not overscan beyond the bounds of the grid', function () { var helper = createHelper(); render(getMarkup({ onSectionRendered: helper.onSectionRendered, columnCount: 6, overscanColumnCount: 10, overscanRowCount: 10, rowCount: 5 })); expect(helper.columnOverscanStartIndex()).toEqual(0); expect(helper.columnOverscanStopIndex()).toEqual(5); expect(helper.columnStartIndex()).toEqual(0); expect(helper.columnStopIndex()).toEqual(3); expect(helper.rowOverscanStartIndex()).toEqual(0); expect(helper.rowOverscanStopIndex()).toEqual(4); expect(helper.rowStartIndex()).toEqual(0); expect(helper.rowStopIndex()).toEqual(4); }); it('should set the correct scroll direction', function () { // Do not pass in the initial state as props, otherwise the internal state is forbidden from updating itself var grid = render(getMarkup()); // Simulate a scroll to set the initial internal state simulateScroll({ grid: grid, scrollLeft: 50, scrollTop: 50 }); expect(grid.state.scrollDirectionHorizontal).toEqual(SCROLL_DIRECTION_FORWARD); expect(grid.state.scrollDirectionVertical).toEqual(SCROLL_DIRECTION_FORWARD); simulateScroll({ grid: grid, scrollLeft: 0, scrollTop: 0 }); expect(grid.state.scrollDirectionHorizontal).toEqual(SCROLL_DIRECTION_BACKWARD); expect(grid.state.scrollDirectionVertical).toEqual(SCROLL_DIRECTION_BACKWARD); simulateScroll({ grid: grid, scrollLeft: 100, scrollTop: 100 }); expect(grid.state.scrollDirectionHorizontal).toEqual(SCROLL_DIRECTION_FORWARD); expect(grid.state.scrollDirectionVertical).toEqual(SCROLL_DIRECTION_FORWARD); }); it('should set the correct scroll direction when scroll position is updated from props', function () { var grid = render(getMarkup({ scrollLeft: 50, scrollTop: 50 })); expect(grid.state.scrollDirectionHorizontal).toEqual(SCROLL_DIRECTION_FORWARD); expect(grid.state.scrollDirectionVertical).toEqual(SCROLL_DIRECTION_FORWARD); grid = render(getMarkup({ scrollLeft: 0, scrollTop: 0 })); expect(grid.state.scrollDirectionHorizontal).toEqual(SCROLL_DIRECTION_BACKWARD); expect(grid.state.scrollDirectionVertical).toEqual(SCROLL_DIRECTION_BACKWARD); grid = render(getMarkup({ scrollLeft: 100, scrollTop: 100 })); expect(grid.state.scrollDirectionHorizontal).toEqual(SCROLL_DIRECTION_FORWARD); expect(grid.state.scrollDirectionVertical).toEqual(SCROLL_DIRECTION_FORWARD); }); it('should not reset scroll direction for one axis when scrolled in another', function () { // Do not pass in the initial state as props, otherwise the internal state is forbidden from updating itself var grid = render(getMarkup()); // Simulate a scroll to set the initial internal state simulateScroll({ grid: grid, scrollLeft: 0, scrollTop: 5 }); expect(grid.state.scrollDirectionHorizontal).toEqual(SCROLL_DIRECTION_FORWARD); expect(grid.state.scrollDirectionVertical).toEqual(SCROLL_DIRECTION_FORWARD); simulateScroll({ grid: grid, scrollLeft: 5, scrollTop: 5 }); expect(grid.state.scrollDirectionHorizontal).toEqual(SCROLL_DIRECTION_FORWARD); expect(grid.state.scrollDirectionVertical).toEqual(SCROLL_DIRECTION_FORWARD); simulateScroll({ grid: grid, scrollLeft: 5, scrollTop: 0 }); expect(grid.state.scrollDirectionHorizontal).toEqual(SCROLL_DIRECTION_FORWARD); expect(grid.state.scrollDirectionVertical).toEqual(SCROLL_DIRECTION_BACKWARD); simulateScroll({ grid: grid, scrollLeft: 0, scrollTop: 0 }); expect(grid.state.scrollDirectionHorizontal).toEqual(SCROLL_DIRECTION_BACKWARD); expect(grid.state.scrollDirectionVertical).toEqual(SCROLL_DIRECTION_BACKWARD); }); it('should overscan in the direction being scrolled', function _callee(done) { var helper, onSectionRenderedResolve, onSectionRendered, grid, onSectionRenderedPromise; return _regeneratorRuntime.async(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: onSectionRendered = function _ref5(params) { helper.onSectionRendered(params); if (onSectionRenderedResolve) { onSectionRenderedResolve(); } }; helper = createHelper(); grid = render(getMarkup({ onSectionRendered: onSectionRendered, overscanColumnCount: 2, overscanRowCount: 5 })); // Wait until the onSectionRendered handler / debouncer has processed onSectionRenderedPromise = new Promise(function (resolve) { onSectionRenderedResolve = resolve; }); simulateScroll({ grid: grid, scrollLeft: 200, scrollTop: 200 }); _context.next = 7; return _regeneratorRuntime.awrap(onSectionRenderedPromise); case 7: // It should overscan in the direction being scrolled while scroll is in progress expect(helper.columnOverscanStartIndex()).toEqual(4); expect(helper.columnOverscanStopIndex()).toEqual(9); expect(helper.columnStartIndex()).toEqual(4); expect(helper.columnStopIndex()).toEqual(7); expect(helper.rowOverscanStartIndex()).toEqual(10); expect(helper.rowOverscanStopIndex()).toEqual(19); expect(helper.rowStartIndex()).toEqual(10); expect(helper.rowStopIndex()).toEqual(14); // Wait until the onSectionRendered handler / debouncer has processed onSectionRenderedPromise = new Promise(function (resolve) { onSectionRenderedResolve = resolve; }); simulateScroll({ grid: grid, scrollLeft: 100, scrollTop: 100 }); _context.next = 19; return _regeneratorRuntime.awrap(onSectionRenderedPromise); case 19: // It reset overscan once scrolling has finished expect(helper.columnOverscanStartIndex()).toEqual(0); expect(helper.columnOverscanStopIndex()).toEqual(5); expect(helper.columnStartIndex()).toEqual(2); expect(helper.columnStopIndex()).toEqual(5); expect(helper.rowOverscanStartIndex()).toEqual(0); expect(helper.rowOverscanStopIndex()).toEqual(9); expect(helper.rowStartIndex()).toEqual(5); expect(helper.rowStopIndex()).toEqual(9); done(); case 28: case "end": return _context.stop(); } } }); }); }); describe('cellRangeRenderer', function () { it('should use a custom :cellRangeRenderer if specified', function () { var cellRangeRendererCalled = 0; var cellRangeRendererParams; var rendered = findDOMNode(render(getMarkup({ cellRangeRenderer: function cellRangeRenderer(params) { cellRangeRendererParams = params; cellRangeRendererCalled++; return [React.createElement("div", { key: "0" }, "Fake content")]; } }))); expect(cellRangeRendererCalled).toEqual(1); expect(cellRangeRendererParams.columnStartIndex).toEqual(0); expect(cellRangeRendererParams.columnStopIndex).toEqual(3); expect(cellRangeRendererParams.rowStartIndex).toEqual(0); expect(cellRangeRendererParams.rowStopIndex).toEqual(4); expect(rendered.textContent).toContain('Fake content'); }); }); describe('estimated row and column sizes', function () { it('should not estimate sizes if actual sizes are numbers', function () { var grid = render(getMarkup({ columnWidth: 100, estimatedColumnSize: 150, estimatedRowSize: 15, rowHeight: 20 })); expect(Grid._getEstimatedColumnSize(grid.props)).toEqual(100); expect(Grid._getEstimatedRowSize(grid.props)).toEqual(20); }); it('should estimate row and column sizes if actual sizes are functions', function () { var grid = render(getMarkup({ columnWidth: function columnWidth() { return 100; }, estimatedColumnSize: 150, estimatedRowSize: 15, rowHeight: function rowHeight() { return 20; } })); expect(Grid._getEstimatedColumnSize(grid.props)).toEqual(150); expect(Grid._getEstimatedRowSize(grid.props)).toEqual(15); }); }); it('should pass the cellRenderer an :isScrolling flag when scrolling is in progress', function _callee2(done) { var cellRendererCalls, cellRenderer, grid; return _regeneratorRuntime.async(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: cellRenderer = function _ref7(_ref6) { var columnIndex = _ref6.columnIndex, isScrolling = _ref6.isScrolling, key = _ref6.key, rowIndex = _ref6.rowIndex, style = _ref6.style; cellRendererCalls.push(isScrolling); return defaultCellRenderer({ columnIndex: columnIndex, key: key, rowIndex: rowIndex, style: style }); }; cellRendererCalls = []; grid = render(getMarkup({ cellRenderer: cellRenderer })); expect(cellRendererCalls[0]).toEqual(false); cellRendererCalls.splice(0); // Give React time to process the queued setState() _context2.next = 7; return _regeneratorRuntime.awrap(new Promise(function (resolve) { return setTimeout(resolve, 1); })); case 7: simulateScroll({ grid: grid, scrollTop: 100 }); expect(cellRendererCalls[0]).toEqual(true); done(); case 10: case "end": return _context2.stop(); } } }); }); it('should pass the cellRenderer an :isScrolling flag based on props override', function () { var cellRenderer = jest.fn(); cellRenderer.mockImplementation(function (_ref8) { var key = _ref8.key, style = _ref8.style; return React.createElement("div", { key: key, style: style }); }); render(getMarkup({ cellRenderer: cellRenderer, isScrolling: true })); expect(cellRenderer).toHaveBeenCalled(); expect(cellRenderer.mock.calls[0][0].isScrolling).toBe(true); cellRenderer.mockReset(); render(getMarkup({ cellRenderer: cellRenderer, isScrolling: false, width: DEFAULT_WIDTH + 1 })); expect(cellRenderer).toHaveBeenCalled(); expect(cellRenderer.mock.calls[0][0].isScrolling).toBe(false); }); it('should pass the cellRenderer an :isVisible flag', function () { var cellRendererCalls = []; function cellRenderer(props) { cellRendererCalls.push(props); return defaultCellRenderer(props); } render(getMarkup({ cellRenderer: cellRenderer, height: DEFAULT_ROW_HEIGHT, overscanColumnCount: 1, overscanRowCount: 1, width: DEFAULT_COLUMN_WIDTH })); cellRendererCalls.forEach(function (props) { expect(props.isVisible).toEqual(props.columnIndex === 0 && props.rowIndex === 0); // Only the first cell is visible }); }); describe('cell caching', function () { it('should not cache cells if the Grid is not scrolling', function () { var cellRendererCalls = []; function cellRenderer(_ref9) { var columnIndex = _ref9.columnIndex, key = _ref9.key, rowIndex = _ref9.rowIndex, style = _ref9.style; cellRendererCalls.push({ columnIndex: columnIndex, rowIndex: rowIndex }); return defaultCellRenderer({ columnIndex: columnIndex, key: key, rowIndex: rowIndex, style: style }); } var props = { cellRenderer: cellRenderer, columnWidth: 100, height: 40, rowHeight: 20, scrollToRow: 0, width: 100 }; render(getMarkup(_objectSpread({}, props, { scrollToRow: 0 }))); expect(cellRendererCalls).toEqual([{ columnIndex: 0, rowIndex: 0 }, { columnIndex: 0, rowIndex: 1 }]); cellRendererCalls.splice(0); render(getMarkup(_objectSpread({}, props, { scrollToRow: 1 }))); expect(cellRendererCalls).toEqual([{ columnIndex: 0, rowIndex: 0 }, { columnIndex: 0, rowIndex: 1 }]); }); it('should not cache cells if the offsets are not adjusted', function () { var cellRendererCalls = []; function cellRenderer(_ref10) { var columnIndex = _ref10.columnIndex, key = _ref10.key, rowIndex = _ref10.rowIndex, style = _ref10.style; cellRendererCalls.push({ columnIndex: columnIndex, rowIndex: rowIndex }); return defaultCellRenderer({ columnIndex: columnIndex, key: key, rowIndex: rowIndex, style: style }); } var props = { cellRenderer: cellRenderer, columnWidth: 100, height: 40, rowHeight: 20, rowCount: 100000, scrollToRow: 0, width: 100 }; render(getMarkup(_objectSpread({}, props, { scrollToRow: 0 }))); expect(cellRendererCalls).toEqual([{ columnIndex: 0, rowIndex: 0 }, { columnIndex: 0, rowIndex: 1 }]); cellRendererCalls.splice(0); render(getMarkup(_objectSpread({}, props, { scrollToRow: 1 }))); expect(cellRendererCalls).toEqual([{ columnIndex: 0, rowIndex: 0 }, { columnIndex: 0, rowIndex: 1 }]); }); it('should cache a cell once it has been rendered while scrolling', function () { var cellRendererCalls = []; function cellRenderer(_ref11) { var columnIndex = _ref11.columnIndex, key = _ref11.key, rowIndex = _ref11.rowIndex, style = _ref11.style; cellRendererCalls.push({ columnIndex: columnIndex, rowIndex: rowIndex }); return defaultCellRenderer({ columnIndex: columnIndex, key: key, rowIndex: rowIndex, style: style }); } var props = { cellRenderer: cellRenderer, columnWidth: 100, height: 40, rowHeight: 20, width: 100 }; var grid = render(getMarkup(_objectSpread({}, props, { scrollToRow: 0 }))); expect(cellRendererCalls).toEqual([{ columnIndex: 0, rowIndex: 0 }, { columnIndex: 0, rowIndex: 1 }]); simulateScroll({ grid: grid, scrollTop: 1 }); cellRendererCalls.splice(0); // Rows 0-2 have already rendered but row 3 is not yet visible // This means that only row 3 should be newly-created // The others should come from the cache render(getMarkup(_objectSpread({}, props, { scrollToRow: 3 }))); expect(cellRendererCalls).toEqual([{ columnIndex: 0, rowIndex: 3 }]); }); it('should clear cache once :isScrolling is false', function _callee3(done) { var cellRendererCalls, cellRenderer, props, grid; return _regeneratorRuntime.async(function _callee3$(_context3) { while (1) { switch (_context3.prev = _context3.next) { case 0: cellRenderer = function _ref13(_ref12) { var columnIndex = _ref12.columnIndex, key = _ref12.key, rowIndex = _ref12.rowIndex, style = _ref12.style; cellRendererCalls.push({ columnIndex: columnIndex, rowIndex: rowIndex }); return defaultCellRenderer({ columnIndex: columnIndex, key: key, rowIndex: rowIndex, style: style }); }; cellRendererCalls = []; props = { cellRenderer: cellRenderer, columnWidth: 100, height: 40, rowHeight: 20, scrollToRow: 0, width: 100 }; grid = render(getMarkup(props)); expect(cellRendererCalls).toEqual([{ columnIndex: 0, rowIndex: 0 }, { columnIndex: 0, rowIndex: 1 }]); simulateScroll({ grid: grid, scrollTop: 1 }); // Allow scrolling timeout to complete so that cell cache is reset _context3.next = 8; return _regeneratorRuntime.awrap(new Promise(function (resolve) { return setTimeout(resolve, DEFAULT_SCROLLING_RESET_TIME_INTERVAL * 2); })); case 8: cellRendererCalls.splice(0); render(getMarkup(_objectSpread({}, props, { scrollToRow: 1 }))); expect(cellRendererCalls.length).not.toEqual(0); done(); case 12: case "end": return _context3.stop(); } } }); }); it('should clear cache once :isScrolling via props is false', function _callee4() { var cellRenderer, props, scrollingStyle; return _regeneratorRuntime.async(function _callee4$(_context4) { while (1) { switch (_context4.prev = _context4.next) { case 0: cellRenderer = jest.fn(); cellRenderer.mockImplementation(function (params) { return React.createElement("div", { key: params.key, style: params.style }); }); props = { autoHeight: true, cellRenderer: cellRenderer, columnCount: 1, isScrolling: true, rowCount: 1 }; render(getMarkup(props)); render(getMarkup(props)); expect(cellRenderer).toHaveBeenCalledTimes(1); // Due to cell cache scrollingStyle = cellRenderer.mock.calls[0][0].style; cellRenderer.mockReset(); render(getMarkup(_objectSpread({}, props, { isScrolling: false }))); expect(cellRenderer.mock.calls[0][0].style).toBe(scrollingStyle); expect(cellRenderer).toHaveBeenCalledTimes(1); // Reset cache cellRenderer.mockReset(); render(getMarkup(_objectSpread({}, props, { isScrolling: true }))); expect(cellRenderer.mock.calls[0][0].style).not.toBe(scrollingStyle); expect(cellRenderer).toHaveBeenCalledTimes(1); // Only cached when scrolling case 15: case "end": return _context4.stop(); } } }); }); it('should clear cache if :recomputeGridSize is called', function () { var cellRendererCalls = []; function cellRenderer(_ref14) { var columnIndex = _ref14.columnIndex, key = _ref14.key, rowIndex = _ref14.rowIndex, style = _ref14.style; cellRendererCalls.push({ columnIndex: columnIndex, rowIndex: rowIndex }); return defaultCellRenderer({ columnIndex: columnIndex, key: key, rowIndex: rowIndex, style: style }); } var props = { cellRenderer: cellRenderer, columnWidth: 100, height: 40, rowHeight: 20, scrollTop: 0, width: 100 }; var grid = render(getMarkup(props)); expect(cellRendererCalls).toEqual([{ columnIndex: 0, rowIndex: 0 }, { columnIndex: 0, rowIndex: 1 }]); simulateScroll({ grid: grid, scrollTop: 1 }); cellRendererCalls.splice(0); grid.recomputeGridSize(); expect(cellRendererCalls.length).not.toEqual(0); }); it('should not clear cache if :isScrollingOptOut is true', function () { var cellRendererCalls = []; function cellRenderer(_ref15) { var columnIndex = _ref15.columnIndex, key = _ref15.key, rowIndex = _ref15.rowIndex, style = _ref15.style; cellRendererCalls.push({ columnIndex: columnIndex, rowIndex: rowIndex }); return defaultCellRenderer({ columnIndex: columnIndex, key: key, rowIndex: rowIndex, style: style }); } var props = { cellRenderer: cellRenderer, columnWidth: 100, height: 40, rowHeight: 20, scrollTop: 0, width: 100, isScrollingOptOut: true }; render(getMarkup(props)); render(getMarkup(props)); expect(cellRendererCalls).toEqual([{ columnIndex: 0, rowIndex: 0 }, { columnIndex: 0, rowIndex: 1 }]); cellRendererCalls.splice(0); render(getMarkup(_objectSpread({}, props, { isScrolling: false }))); // Visible cells are cached expect(cellRendererCalls.length).toEqual(0); render(getMarkup(_objectSpread({}, props, { isScrolling: true }))); // Only cleared non-visible cells expect(cellRendererCalls.length).toEqual(0); }); it('should not trigger render by _debounceScrollEndedCallback if process slow table', function _callee5() { var scrollingResetTimeInterval, cellRangeRendererCalls, cellRangeRenderer, props, grid, i; return _regeneratorRuntime.async(function _callee5$(_context5) { while (1) { switch (_context5.prev = _context5.next) { case 0: cellRangeRenderer = function _ref16(props) { var startTime = Date.now(); while (Date.now() - startTime <= scrollingResetTimeInterval) { ; } // imitate very slow render cellRangeRendererCalls++; return defaultCellRangeRenderer(props); }; scrollingResetTimeInterval = 50; cellRangeRendererCalls = 0; props = { scrollingResetTimeInterval: scrollingResetTimeInterval, cellRangeRenderer: cellRangeRenderer }; grid = render(getMarkup(props)); render(getMarkup(props)); expect(cellRangeRendererCalls).toEqual(1); i = 1; case 8: if (!(i <= 5)) { _context5.next = 17; break; } cellRangeRendererCalls = 0; simulateScroll({ grid: grid, scrollTop: i }); // small wait for maybe early _debounceScrollEndedCallback _context5.next = 13; return _regeneratorRuntime.awrap(new Promise(function (resolve) { return setTimeout(resolve, scrollingResetTimeInterval / 2); })); case 13: expect(cellRangeRendererCalls).toEqual(1); case 14: i++; _context5.next = 8; break; case 17: cellRangeRendererCalls = 0; // wait for real _debounceScrollEndedCallback _context5.next = 20; return _regeneratorRuntime.awrap(new Promise(function (resolve) { return setTimeout(resolve, scrollingResetTimeInterval * 1.5); })); case 20: expect(cellRangeRendererCalls).toEqual(1); case 21: case "end": return _context5.stop(); } } }); }); it('should support a custom :scrollingResetTimeInterval prop', function _callee6(done) { var cellRendererCalls, scrollingResetTimeInterval, cellRenderer, props, grid; return _regeneratorRuntime.async(function _callee6$(_context6) { while (1) { switch (_context6.prev = _context6.next) { case 0: cellRenderer = function _ref18(_ref17) { var columnIndex = _ref17.columnIndex, key = _ref17.key, rowIndex = _ref17.rowIndex, style = _ref17.style; cellRendererCalls.push({ columnIndex: columnIndex, rowIndex: rowIndex }); return defaultCellRenderer({ columnIndex: columnIndex, key: key, rowIndex: rowIndex, style: style }); }; cellRendererCalls = []; scrollingResetTimeInterval = DEFAULT_SCROLLING_RESET_TIME_INTERVAL * 2; props = { cellRenderer: cellRenderer, scrollingResetTimeInterval: scrollingResetTimeInterval }; grid = render(getMarkup(props)); expect(cellRendererCalls.length > 0).toEqual(true); simulateScroll({ grid: grid, scrollTop: 1 }); _context6.next = 9; return _regeneratorRuntime.awrap(new Promise(function (resolve) { return setTimeout(resolve, DEFAULT_SCROLLING_RESET_TIME_INTERVAL); })); case 9: cellRendererCalls.splice(0); render(getMarkup(_objectSpread({}, props, { className: 'foo' }))); expect(cellRendererCalls.length).toEqual(0); _context6.next = 14; return _regeneratorRuntime.awrap(new Promise(function (resolve) { return setTimeout(resolve, DEFAULT_SCROLLING_RESET_TIME_INTERVAL * 2); })); case 14: cellRendererCalls.splice(0); render(getMarkup(_objectSpread({}, props, { className: 'bar' }))); expect(cellRendererCalls.length).not.toEqual(0); done(); case 18: case "end": return _context6.stop(); } } }); }); }); describe('measureAllCells', function () { it('should measure any unmeasured columns and rows', function () { var grid = render(getMarkup({ columnCount: 10, columnWidth: function columnWidth() { return 100; }, estimatedColumnSize: 150, estimatedRowSize: 15, height: 0, rowCount: 10, rowHeight: function rowHeight() { return 20; }, width: 0 })); expect(grid.state.instanceProps.columnSizeAndPositionManager.getTotalSize()).toEqual(1500); expect(grid.state.instanceProps.rowSizeAndPositionManager.getTotalSize()).toEqual(150); grid.measureAllCells(); expect(grid.state.instanceProps.columnSizeAndPositionManager.getTotalSize()).toEqual(1000); expect(grid.state.instanceProps.rowSizeAndPositionManager.getTotalSize()).toEqual(200); }); }); describe('recomputeGridSize', function () { it('should recompute cell sizes and other values when called', function () { var columnIndices = []; var rowIndices = []; function columnWidth(_ref19) { var index = _ref19.index; columnIndices.push(index); return 10; } function rowHeight(_ref20) { var index = _ref20.index; rowIndices.push(index); return 10; } var props = { columnCount: 50, columnWidth: columnWidth, height: 50, rowHeight: rowHeight, rowCount: 50, width: 100 }; var component = render(getMarkup(props)); columnIndices.splice(0); rowIndices.splice(0); component.recomputeGridSize(); // Only the rows required to fill the current viewport will be rendered expect(columnIndices[0]).toEqual(0); expect(columnIndices[columnIndices.length - 1]).toEqual(9); expect(rowIndices[0]).toEqual(0); expect(rowIndices[rowIndices.length - 1]).toEqual(4); columnIndices.splice(0); rowIndices.splice(0); component.recomputeGridSize({ columnIndex: 4, rowIndex: 2 }); // Only the rows required to fill the current viewport will be rendered expect(columnIndices[0]).toEqual(4); expect(columnIndices[columnIndices.length - 1]).toEqual(9); expect(rowIndices[0]).toEqual(2); expect(rowIndices[rowIndices.length - 1]).toEqual(4); }); }); describe('autoContainerWidth', function () { it('should set the innerScrollContainer width to auto to better support single-column HOCs', function () { var props = { autoContainerWidth: true }; var rendered = findDOMNode(render(getMarkup(props))); expect(rendered.querySelector('.ReactVirtualized__Grid__innerScrollContainer').style.width).toEqual('auto'); }); it('should set the innerScrollContainer width to :totalColumnsWidth unless :autoContainerWidth', function () { var props = { autoContainerWidth: false }; var rendered = findDOMNode(render(getMarkup(props))); expect(rendered.querySelector('.ReactVirtualized__Grid__innerScrollContainer').style.width).toEqual('2500px'); // 50 columns x 50px }); }); describe('autoHeight', function () { it('should set the container height to auto to adjust to innerScrollContainer height', function () { var props = { autoHeight: true }; var rendered = findDOMNode(render(getMarkup(props))); expect(rendered.style.height).toEqual('auto'); }); it('should have container height still affecting number of rows rendered', function () { var props = { height: 500, autoHeight: true }; var rendered = findDOMNode(render(getMarkup(props))); expect(rendered.querySelectorAll('.gridItem').length).toEqual(100); // 25 rows x 4 columns }); it('should have innerScrollContainer height to be equal number of rows * rowHeight', function () { var props = { autoHeight: true }; var grid = render(getMarkup(props)); var rendered = findDOMNode(grid); expect(rendered.querySelector('.ReactVirtualized__Grid__innerScrollContainer').style.height).toEqual('2000px'); // 100 rows * 20px rowHeight expect(grid.state.instanceProps.rowSizeAndPositionManager.getTotalSize()).toEqual(2000); }); }); describe('autoWidth', function () { it('should set the container width to auto to adjust to innerScrollContainer width', function () { var props = { autoWidth: true }; var rendered = findDOMNode(render(getMarkup(props))); expect(rendered.style.width).toEqual('auto'); }); it('should have container width still affecting number of columns rendered', function () { var props = { width: 500, autoWidth: true }; var rendered = findDOMNode(render(getMarkup(props))); expect(rendered.querySelectorAll('.gridItem').length).toEqual(50); // 5 rows x 10 columns }); it('should have innerScrollContainer width to be equal number of columns * columnWidth', function () { var props = { autoWidth: true }; var grid = render(getMarkup(props)); var rendered = findDOMNode(grid); expect(rendered.querySelector('.ReactVirtualized__Grid__innerScrollContainer').style.width).toEqual('2500px'); // 50 columns * 50px columnWidth expect(grid.state.instanceProps.columnSizeAndPositionManager.getTotalSize()).toEqual(2500); }); }); describe('tabIndex', function () { it('should be focusable by default', function () { var rendered = findDOMNode(render(getMarkup())); expect(rendered.tabIndex).toEqual(0); }); it('should allow tabIndex to be overridden', function () { var rendered = findDOMNode(render(getMarkup({ tabIndex: -1 }))); expect(rendered.tabIndex).toEqual(-1); }); }); describe('role', function () { it('should have grid role by default', function () { var rendered = findDOMNode(render(getMarkup())); expect(rendered.getAttribute('role')).toEqual('grid'); }); it('should allow role to be overridden', function () { var role = null; var rendered = findDOMNode(render(getMarkup({ role: role }))); expect(rendered.getAttribute('role')).toEqual(role); }); }); describe('pure', function () { it('should not re-render unless props have changed', function () { var cellRendererCalled = false; function cellRenderer(_ref21) { var key = _ref21.key, style = _ref21.style; cellRendererCalled = true; return React.createElement("div", { key: key, style: style }); } var markup = getMarkup({ cellRenderer: cellRenderer }); render(markup); expect(cellRendererCalled).toEqual(true); cellRendererCalled = false; render(markup); expect(cellRendererCalled).toEqual(false); }); it('should not re-render grid components if they extend PureComponent', function () { var componentUpdates = 0; var GridComponent = /*#__PURE__*/ function (_React$PureComponent) { _inherits(GridComponent, _React$PureComponent); function GridComponent() { _classCallCheck(this, GridComponent); return _possibleConstructorReturn(this, _getPrototypeOf(GridComponent).apply(this, arguments)); } _createClass(GridComponent, [{ key: "componentDidUpdate", value: function componentDidUpdate() { componentUpdates++; } }, { key: "render", value: function render() { var _this$props = this.props, columnIndex = _this$props.columnIndex, rowIndex = _this$props.rowIndex, style = _this$props.style; return React.createElement("div", { className: "gridItem", style: style }, "row:".concat(rowIndex, ", column:").concat(columnIndex)); } }]); return GridComponent; }(React.PureComponent); function cellRenderer(_ref22) { var columnIndex = _ref22.columnIndex, key = _ref22.key, rowIndex = _ref22.rowIndex, style = _ref22.style; return React.createElement(GridComponent, { key: key, columnIndex: columnIndex, rowIndex: rowIndex, style: style }); } var props = { cellRenderer: cellRenderer, columnWidth: 100, height: 40, rowHeight: 20, scrollTop: 0, width: 100 }; var grid = render(getMarkup(props)); simulateScroll({ grid: grid, scrollToIndex: 1 }); expect(componentUpdates).toEqual(0); }); it('should clear all but the visible rows from the style cache once :isScrolling is false', function _callee7(done) { var props, grid; return _regeneratorRuntime.async(function _callee7$(_context7) { while (1) { switch (_context7.prev = _context7.next) { case 0: props = { columnWidth: 50, height: 100, overscanColumnCount: 0, overscanRowCount: 0, rowHeight: 50, width: 100 }; grid = render(getMarkup(props)); expect(Object.keys(grid._styleCache).length).toBe(4); simulateScroll({ grid: grid, scrollTop: 50 }); expect(Object.keys(grid._styleCache).length).toBe(6); // Allow scrolling timeout to complete so that cell cache is reset _context7.next = 7; return _regeneratorRuntime.awrap(new Promise(function (resolve) { return setTimeout(resolve, DEFAULT_SCROLLING_RESET_TIME_INTERVAL * 2); })); case 7: expect(Object.keys(grid._styleCache).length).toBe(4); done(); case 9: case "end": return _context7.stop(); } } }); }); it('should clear style cache if :recomputeGridSize is called', function () { var props = { columnWidth: 50, height: 100, overscanColumnCount: 0, overscanRowCount: 0, rowHeight: 50, width: 100 }; var grid = render(getMarkup(props)); expect(Object.keys(grid._styleCache).length).toBe(4); render(getMarkup(_objectSpread({}, props, { scrollTop: 50 }))); expect(Object.keys(grid._styleCache).length).toBe(6); grid.recomputeGridSize(); expect(Object.keys(grid._styleCache).length).toBe(4); }); it('should clear style cache if cell sizes change', function () { var cellRendererCalls = []; function cellRenderer(params) { cellRendererCalls.push(params); return React.createElement("div", { key: params.key, style: params.style }); } var props = { cellRenderer: cellRenderer, columnWidth: 100, height: 100, overscanColumnCount: 0, overscanRowCount: 0, rowHeight: 100, width: 100 }; render(getMarkup(props)); expect(cellRendererCalls.length).toEqual(1); expect(cellRendererCalls[0].style.width).toEqual(100); render(getMarkup(_objectSpread({}, props, { columnWidth: 50, width: 50 }))); expect(cellRendererCalls.length).toEqual(2); expect(cellRendererCalls[1].style.width).toEqual(50); }); }); it('should not pull from the style cache while scrolling if there is an offset adjustment', function () { var cellRendererCalls = []; function cellRenderer(params) { cellRendererCalls.push(params); return React.createElement("div", { key: params.key, style: params.style }); } var grid = render(getMarkup({ cellRenderer: cellRenderer, width: 100, height: 100, rowHeight: 100, columnWidth: 100, rowCount: getMaxElementSize() * 2 / 100, // lots of offset scrollTop: 2000 })); simulateScroll({ grid: grid, scrollTop: 2100 }); // cellRendererCalls[0] is the element at rowIndex 0 // only two calls. Since the scrollTop is updated in getDerivedStateFromProps var firstProps = cellRendererCalls[0]; var secondProps = cellRendererCalls[1]; expect(cellRendererCalls.length).toEqual(2); expect(firstProps.style).not.toBe(secondProps.style); }); it('should only cache styles when a :deferredMeasurementCache is provided if the cell has already been measured', function () { var cache = new CellMeasurerCache({ fixedWidth: true }); cache.set(0, 0, 100, 100); cache.set(1, 1, 100, 100); var grid = render(getMarkup({ columnCount: 2, deferredMeasurementCache: cache, rowCount: 2 })); var keys = Object.keys(grid._styleCache); expect(keys).toEqual(['0-0', '1-1']); }); describe('DEV warnings', function () { it('should warn about cells that forget to include the :style property', function () { spyOn(console, 'warn'); function cellRenderer(params) { return React.createElement("div", { key: params.key }); } render(getMarkup({ cellRenderer: cellRenderer })); expect(console.warn).toHaveBeenCalledWith('Rendered cell should include style property for positioning.'); expect(console.warn).toHaveBeenCalledTimes(1); }); it('should warn about CellMeasurer measured cells that forget to include the :style property', function () { spyOn(console, 'warn'); var cache = new CellMeasurerCache({ fixedWidth: true }); var cellRenderer = jest.fn(); cellRenderer.mockImplementation(function (params) { return React.createElement(CellMeasurer, { cache: cache, columnIndex: params.columnIndex, key: params.key, parent: params.parent, rowIndex: params.rowIndex, style: params.style }, React.createElement("div", null)); }); render(getMarkup({ cellRenderer: cellRenderer, columnCount: 1, deferredMeasurementCache: cache, rowCount: 1 })); expect(console.warn).toHaveBeenCalledWith('Rendered cell should include style property for positioning.'); expect(console.warn).toHaveBeenCalledTimes(1); }); }); describe('deferredMeasurementCache', function () { it('invalidateCellSizeAfterRender should invalidate cache and refresh displayed cells after mount', function () { var cache = new CellMeasurerCache({ fixedWidth: true }); var invalidateCellSizeAfterRender = true; var cellRenderer = jest.fn(); cellRenderer.mockImplementation(function (params) { // Don't get stuck in a loop if (invalidateCellSizeAfterRender) { invalidateCellSizeAfterRender = false; params.parent.invalidateCellSizeAfterRender({ columnIndex: 1, rowIndex: 0 }); } return React.createElement("div", { key: params.key, style: params.style }); }); var props = { cellRenderer: cellRenderer, columnCount: 2, deferredMeasurementCache: cache, rowCount: 2 }; render(getMarkup(props)); // 4 times for initial render + 4 once cellCache was cleared expect(cellRenderer).toHaveBeenCalledTimes(8); }); it('should invalidate cache and refresh displayed cells after update', function () { var cache = new CellMeasurerCache({ fixedWidth: true }); var cellRenderer = jest.fn(); cellRenderer.mockImplementation(function (params) { return React.createElement("div", { key: params.key, style: params.style }); }); var props = { cellRenderer: cellRenderer, columnCount: 2, deferredMeasurementCache: cache, rowCount: 2 }; var grid = render(getMarkup(props)); expect(cellRenderer).toHaveBeenCalledTimes(4); var invalidateCellSizeAfterRender = false; cellRenderer.mockReset(); cellRenderer.mockImplementation(function (params) { // Don't get stuck in a loop if (invalidateCellSizeAfterRender) { invalidateCellSizeAfterRender = false; params.parent.invalidateCellSizeAfterRender({ columnIndex: 1, rowIndex: 0 }); } return React.createElement("div", { key: params.key, style: params.style }); }); invalidateCellSizeAfterRender = true; grid.recomputeGridSize(); // 4 times for initial render + 4 once cellCache was cleared expect(cellRenderer).toHaveBeenCalledTimes(8); }); it('should not cache cells until they have been measured by CellMeasurer', function () { var cache = new CellMeasurerCache({ fixedWidth: true }); // Fake measure cell 0,0 but not cell 0,1 cache.set(0, 0, 100, 30); var cellRenderer = jest.fn(); cellRenderer.mockImplementation(function (params) { return React.createElement("div", { key: params.key, style: params.style }); }); var props = { cellRenderer: cellRenderer, columnCount: 2, deferredMeasurementCache: cache, rowCount: 1 }; // Trigger 2 renders // The second render should re-use the style for cell 0,0 // But should not re-use the style for cell 0,1 since it was not measured var grid = render(getMarkup(props)); grid.forceUpdate(); // 0,0 - 0,1 - 0,0 - 0,1 expect(cellRenderer).toHaveBeenCalledTimes(4); var style00A = cellRenderer.mock.calls[0][0].style; var style01A = cellRenderer.mock.calls[1][0].style; var style00B = cellRenderer.mock.calls[2][0].style; var style01B = cellRenderer.mock.calls[3][0].style; expect(style00A).toBe(style00B); expect(style01A).not.toBe(style01B); }); }); describe('onScrollbarPresenceChange', function () { it('should not trigger on-mount if scrollbars are hidden', function () { var onScrollbarPresenceChange = jest.fn(); render(getMarkup({ columnCount: 1, getScrollbarSize: getScrollbarSize20, onScrollbarPresenceChange: onScrollbarPresenceChange, rowCount: 1 })); expect(onScrollbarPresenceChange).not.toHaveBeenCalled(); }); it('should trigger on-mount if scrollbars are visible', function () { var onScrollbarPresenceChange = jest.fn(); render(getMarkup({ columnCount: 100, getScrollbarSize: getScrollbarSize20, onScrollbarPresenceChange: onScrollbarPresenceChange, rowCount: 100 })); expect(onScrollbarPresenceChange).toHaveBeenCalled(); var args = onScrollbarPresenceChange.mock.calls[0][0]; expect(args.horizontal).toBe(true); expect(args.size).toBe(getScrollbarSize20()); expect(args.vertical).toBe(true); }); it('should trigger on-update if scrollbar visibility has changed', function () { var onScrollbarPresenceChange = jest.fn(); render(getMarkup({ columnCount: 1, getScrollbarSize: getScrollbarSize20, onScrollbarPresenceChange: onScrollbarPresenceChange, rowCount: 1 })); expect(onScrollbarPresenceChange).not.toHaveBeenCalled(); render(getMarkup({ columnCount: 100, getScrollbarSize: getScrollbarSize20, onScrollbarPresenceChange: onScrollbarPresenceChange, rowCount: 100 })); expect(onScrollbarPresenceChange).toHaveBeenCalled(); var args = onScrollbarPresenceChange.mock.calls[0][0]; expect(args.horizontal).toBe(true); expect(args.size).toBe(getScrollbarSize20()); expect(args.vertical).toBe(true); }); it('should not trigger on-update if scrollbar visibility does not change', function () { var onScrollbarPresenceChange = jest.fn(); render(getMarkup({ columnCount: 1, getScrollbarSize: getScrollbarSize20, onScrollbarPresenceChange: onScrollbarPresenceChange, rowCount: 1 })); expect(onScrollbarPresenceChange).not.toHaveBeenCalled(); render(getMarkup({ columnCount: 2, getScrollbarSize: getScrollbarSize20, onScrollbarPresenceChange: onScrollbarPresenceChange, rowCount: 2 })); expect(onScrollbarPresenceChange).not.toHaveBeenCalled(); }); }); it('should not complain when using react-test-renderer', function () { var instance = TestRenderer.create(getMarkup()).getInstance(); expect(instance).toBeTruthy(); }); });