UNPKG

react-virtualized

Version:

React components for efficiently rendering large, scrollable lists and tabular data

1,287 lines (1,273 loc) 95.4 kB
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 _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; import _extends from "@babel/runtime/helpers/extends"; function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); } function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } import _regeneratorRuntime from "@babel/runtime/regenerator"; function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } 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 /*#__PURE__*/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 /*#__PURE__*/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 /*#__PURE__*/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(_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(_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 /*#__PURE__*/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 /*#__PURE__*/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 /*#__PURE__*/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 /*#__PURE__*/React.createElement("div", null, "No data"); } }))); expect(list.textContent).toEqual(''); list = findDOMNode(render(getMarkup({ noContentRenderer: function noContentRenderer() { return /*#__PURE__*/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, ids, and roles', 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'); }); it('should have the gridcell role', function () { var containerStyle = { backgroundColor: 'red' }; var rendered = findDOMNode(render(getMarkup({ containerStyle: containerStyle }))); expect(rendered.querySelectorAll('[role="gridcell"]').length).toEqual(20); }); }); 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', /*#__PURE__*/function () { var _ref5 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime.mark(function _callee(done) { var helper, onSectionRenderedResolve, onSectionRendered, grid, o