import _defineProperty from 'babel-runtime/helpers/defineProperty';
import _slicedToArray from 'babel-runtime/helpers/slicedToArray';
import _Object$entries from 'babel-runtime/core-js/object/entries';
import _Number$parseInt from 'babel-runtime/core-js/number/parse-int';
import _Array$from from 'babel-runtime/core-js/array/from';
import _extends from 'babel-runtime/helpers/extends';
import _objectWithoutProperties from 'babel-runtime/helpers/objectWithoutProperties';
import React from 'react';
import { findDOMNode } from 'react-dom';
import { render } from '../TestUtils';
import { Simulate } from 'react-dom/test-utils';
import Immutable from 'immutable';
import Column from './Column';
import Table from './Table';
import SortDirection from './SortDirection';

describe('Table', function () {
  var array = [];
  for (var i = 0; i < 100; i++) {
    array.push({
      id: i,
      name: 'Name ' + i,
      email: 'user-' + i + '@treasure-data.com'
    });
  }
  var list = Immutable.fromJS(array);

  // Works with an Immutable List of Maps
  function immutableRowGetter(_ref) {
    var index = _ref.index;

    return list.get(index);
  }

  // Works with an Array of Objects
  function vanillaRowGetter(_ref2) {
    var index = _ref2.index;

    return array[index];
  }

  // Override default behavior of overscanning by at least 1 (for accessibility)
  // Because it makes for simple tests below
  function overscanIndicesGetter(_ref3) {
    var direction = _ref3.direction,
        cellCount = _ref3.cellCount,
        overscanCellsCount = _ref3.overscanCellsCount,
        scrollDirection = _ref3.scrollDirection,
        startIndex = _ref3.startIndex,
        stopIndex = _ref3.stopIndex;

    return {
      overscanStartIndex: startIndex,
      overscanStopIndex: stopIndex
    };
  }

  function getMarkup() {
    var _ref4 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
        cellDataGetter = _ref4.cellDataGetter,
        cellRenderer = _ref4.cellRenderer,
        _ref4$columnData = _ref4.columnData,
        columnData = _ref4$columnData === undefined ? { data: 123 } : _ref4$columnData,
        columnID = _ref4.columnID,
        columnStyle = _ref4.columnStyle,
        _ref4$disableSort = _ref4.disableSort,
        disableSort = _ref4$disableSort === undefined ? false : _ref4$disableSort,
        headerRenderer = _ref4.headerRenderer,
        maxWidth = _ref4.maxWidth,
        minWidth = _ref4.minWidth,
        flexTableProps = _objectWithoutProperties(_ref4, ['cellDataGetter', 'cellRenderer', 'columnData', 'columnID', 'columnStyle', 'disableSort', 'headerRenderer', 'maxWidth', 'minWidth']);

    return React.createElement(
      Table,
      _extends({
        headerHeight: 20,
        height: 100,
        overscanRowCount: 0,
        overscanIndicesGetter: overscanIndicesGetter,
        rowCount: list.size,
        rowGetter: immutableRowGetter,
        rowHeight: 10,
        width: 100
      }, flexTableProps),
      React.createElement(Column, {
        label: 'Name',
        dataKey: 'name',
        columnData: columnData,
        width: 50,
        cellRenderer: cellRenderer,
        cellDataGetter: cellDataGetter,
        headerRenderer: headerRenderer,
        disableSort: disableSort,
        style: columnStyle,
        id: columnID
      }),
      React.createElement(Column, {
        label: 'Email',
        dataKey: 'email',
        maxWidth: maxWidth,
        minWidth: minWidth,
        width: 50
      }),
      false,
      true,
      null,
      undefined
    );
  }

  beforeEach(function () {
    return jest.resetModules();
  });

  describe('children', function () {
    it('should accept Column children', function () {
      var children = [React.createElement(Column, {
        dataKey: 'foo',
        width: 100
      })];
      var result = Table.propTypes.children({ children: children }, 'children', 'Table');
      expect(result instanceof Error).toEqual(false);
    });

    it('should not accept non-Column children', function () {
      var children = [React.createElement('div', null)];
      var result = Table.propTypes.children({ children: children }, 'children', 'Table');
      expect(result instanceof Error).toEqual(true);
    });

    it('should accept falsy children to allow easier dynamic showing/hiding of columns', function () {
      var children = [false, React.createElement(Column, {
        dataKey: 'foo',
        width: 100
      }), null];
      var result = Table.propTypes.children({ children: children }, 'children', 'Table');
      expect(result instanceof Error).toEqual(false);
    });
  });

  describe('height', function () {
    it('should subtract header row height from the inner Grid height if headers are enabled', function () {
      var rendered = findDOMNode(render(getMarkup({
        headerHeight: 10,
        overscanRowCount: 0,
        rowHeight: 20,
        height: 50
      })));
      var rows = rendered.querySelectorAll('.ReactVirtualized__Table__row');
      expect(rows.length).toEqual(2);
    });

    it('should not subtract header row height from the inner Grid height if headers are disabled', function () {
      var rendered = findDOMNode(render(getMarkup({
        disableHeader: true,
        headerHeight: 10,
        overscanRowCount: 0,
        rowHeight: 20,
        height: 50
      })));
      var rows = rendered.querySelectorAll('.ReactVirtualized__Table__row');
      expect(rows.length).toEqual(3);
    });
  });

  describe('initial rendering', function () {
    // Ensure that both Immutable Lists of Maps and Arrays of Objects are supported
    var useImmutable = [true, false];
    useImmutable.forEach(function (useImmutable) {
      it('should render the correct number of rows', function () {
        var rendered = findDOMNode(render(getMarkup({
          rowGetter: useImmutable ? immutableRowGetter : vanillaRowGetter
        })));
        // 100px height should fit 1 header (20px) and 8 rows (10px each) -
        expect(rendered.querySelectorAll('.ReactVirtualized__Table__headerRow').length).toEqual(1);
        expect(rendered.querySelectorAll('.ReactVirtualized__Table__row').length).toEqual(8);
      });

      it('should render the expected headers', function () {
        var rendered = findDOMNode(render(getMarkup({
          rowGetter: useImmutable ? immutableRowGetter : vanillaRowGetter
        })));
        var columns = rendered.querySelectorAll('.ReactVirtualized__Table__headerColumn');
        expect(columns.length).toEqual(2);
        expect(columns[0].textContent).toEqual('Name');
        expect(columns[1].textContent).toEqual('Email');
      });

      it('should render the expected rows and columns', function () {
        var rendered = findDOMNode(render(getMarkup({
          rowGetter: useImmutable ? immutableRowGetter : vanillaRowGetter,
          headerHeight: 10,
          rowHeight: 20,
          height: 50
        })));
        var rows = rendered.querySelectorAll('.ReactVirtualized__Table__row');
        expect(rows.length).toEqual(2);
        _Array$from(rows).forEach(function (row, index) {
          var rowData = list.get(index);
          var columns = row.querySelectorAll('.ReactVirtualized__Table__rowColumn');
          expect(columns.length).toEqual(2);
          expect(columns[0].textContent).toEqual(rowData.get('name'));
          expect(columns[1].textContent).toEqual(rowData.get('email'));
        });
      });
    });

    it('should support a :rowHeight function', function () {
      var rowHeight = function rowHeight(_ref5) {
        var index = _ref5.index;
        return 10 + index * 10;
      };
      var rendered = findDOMNode(render(getMarkup({
        rowHeight: rowHeight,
        rowCount: 3
      })));
      var rows = rendered.querySelectorAll('.ReactVirtualized__Table__row');
      _Array$from(rows).forEach(function (row, index) {
        expect(_Number$parseInt(row.style.height, 10)).toEqual(rowHeight({ index: index }));
      });
    });

    it('should support :minWidth and :maxWidth values for a column', function () {
      var rendered = findDOMNode(render(getMarkup({
        maxWidth: 75,
        minWidth: 25,
        rowCount: 1
      })));
      var columns = rendered.querySelectorAll('.ReactVirtualized__Table__rowColumn');
      var emailColumn = columns[1];
      expect(_Number$parseInt(emailColumn.style.maxWidth, 10)).toEqual(75);
      expect(_Number$parseInt(emailColumn.style.minWidth, 10)).toEqual(25);
    });
  });

  describe('measureAllRows', function () {
    it('should measure any unmeasured rows', function () {
      var rendered = render(getMarkup({
        estimatedRowSize: 15,
        height: 0,
        rowCount: 10,
        rowHeight: function rowHeight() {
          return 20;
        },
        width: 0
      }));
      expect(rendered.Grid._rowSizeAndPositionManager.getTotalSize()).toEqual(150);
      rendered.measureAllRows();
      expect(rendered.Grid._rowSizeAndPositionManager.getTotalSize()).toEqual(200);
    });
  });

  describe('recomputeRowHeights', function () {
    it('should recompute row heights and other values when called', function () {
      var indices = [];
      var rowHeight = function rowHeight(_ref6) {
        var index = _ref6.index;

        indices.push(index);
        return 10;
      };
      var component = render(getMarkup({
        rowHeight: rowHeight,
        rowCount: 50
      }));

      indices.splice(0);
      component.recomputeRowHeights();

      // Only the rows required to fill the current viewport will be rendered
      expect(indices[0]).toEqual(0);
      expect(indices[indices.length - 1]).toEqual(7);

      indices.splice(0);
      component.recomputeRowHeights(4);

      expect(indices[0]).toEqual(4);
      expect(indices[indices.length - 1]).toEqual(7);
    });
  });

  describe('forceUpdateGrid', function () {
    it('should refresh inner Grid content when called', function () {
      var marker = 'a';
      function cellRenderer(_ref7) {
        var rowIndex = _ref7.rowIndex;

        return '' + rowIndex + marker;
      }
      var component = render(getMarkup({ cellRenderer: cellRenderer }));
      var node = findDOMNode(component);
      expect(node.textContent).toContain('1a');
      marker = 'b';
      component.forceUpdateGrid();
      expect(node.textContent).toContain('1b');
    });
  });

  describe('custom getter functions', function () {
    it('should use a custom cellDataGetter if specified', function () {
      var rendered = findDOMNode(render(getMarkup({
        cellDataGetter: function cellDataGetter(_ref8) {
          var columnData = _ref8.columnData,
              dataKey = _ref8.dataKey,
              rowData = _ref8.rowData;
          return 'Custom ' + dataKey + ' for row ' + rowData.get('id');
        }
      })));
      var nameColumns = rendered.querySelectorAll('.ReactVirtualized__Table__rowColumn:first-of-type');
      _Array$from(nameColumns).forEach(function (nameColumn, index) {
        expect(nameColumn.textContent).toEqual('Custom name for row ' + index);
      });
    });

    it('should use a custom cellRenderer if specified', function () {
      var rendered = findDOMNode(render(getMarkup({
        cellRenderer: function cellRenderer(_ref9) {
          var cellData = _ref9.cellData;
          return 'Custom ' + cellData;
        }
      })));
      var nameColumns = rendered.querySelectorAll('.ReactVirtualized__Table__rowColumn:first-of-type');
      _Array$from(nameColumns).forEach(function (nameColumn, index) {
        var rowData = list.get(index);
        expect(nameColumn.textContent).toEqual('Custom ' + rowData.get('name'));
      });
    });

    it('should set the rendered cell content as the cell :title if it is a string', function () {
      var rendered = findDOMNode(render(getMarkup({
        cellRenderer: function cellRenderer() {
          return 'Custom';
        }
      })));
      var nameColumn = rendered.querySelector('.ReactVirtualized__Table__rowColumn:first-of-type');
      expect(nameColumn.getAttribute('title')).toContain('Custom');
    });

    it('should not set a cell :title if the rendered cell content is not a string', function () {
      var rendered = findDOMNode(render(getMarkup({
        cellRenderer: function cellRenderer() {
          return React.createElement(
            'div',
            null,
            'Custom'
          );
        }
      })));
      var nameColumn = rendered.querySelector('.ReactVirtualized__Table__rowColumn:first-of-type');
      expect(nameColumn.getAttribute('title')).toEqual(null);
    });
  });

  describe('sorting', function () {
    it('should not render sort indicators if no sort function is provided', function () {
      var rendered = findDOMNode(render(getMarkup()));
      var nameColumn = rendered.querySelectorAll('.ReactVirtualized__Table__headerColumn:first-of-type');

      expect(nameColumn.className || '').not.toContain('ReactVirtualized__Table__sortableHeaderColumn');
    });

    it('should not render sort indicators for non-sortable columns', function () {
      var rendered = findDOMNode(render(getMarkup({
        disableSort: true,
        sort: function sort() {}
      })));
      var nameColumn = rendered.querySelectorAll('.ReactVirtualized__Table__headerColumn:first-of-type');

      expect(nameColumn.className || '').not.toContain('ReactVirtualized__Table__sortableHeaderColumn');
      expect(rendered.querySelectorAll('.ReactVirtualized__Table__sortableHeaderColumn').length).toEqual(1); // Email only
    });

    it('should render sortable column headers as sortable', function () {
      var rendered = findDOMNode(render(getMarkup({
        sort: function sort() {}
      })));
      var nameColumn = rendered.querySelector('.ReactVirtualized__Table__headerColumn:first-of-type');

      expect(nameColumn.className).toContain('ReactVirtualized__Table__sortableHeaderColumn');
      expect(rendered.querySelectorAll('.ReactVirtualized__Table__sortableHeaderColumn').length).toEqual(2); // Email and Name
    });

    it('should render the correct sort indicator by the current sort-by column', function () {
      var sortDirections = [SortDirection.ASC, SortDirection.DESC];
      sortDirections.forEach(function (sortDirection) {
        var rendered = findDOMNode(render(getMarkup({
          sort: function sort() {},
          sortBy: 'name',
          sortDirection: sortDirection
        })));
        var nameColumn = rendered.querySelector('.ReactVirtualized__Table__headerColumn:first-of-type');

        expect(nameColumn.querySelector('.ReactVirtualized__Table__sortableHeaderIcon')).not.toEqual(null);
        expect(nameColumn.querySelector('.ReactVirtualized__Table__sortableHeaderIcon--' + sortDirection)).not.toEqual(null);
      });
    });

    it('should call sort with the correct arguments when the current sort-by column header is clicked', function () {
      var sortDirections = [SortDirection.ASC, SortDirection.DESC];
      sortDirections.forEach(function (sortDirection) {
        var sortCalls = [];
        var rendered = findDOMNode(render(getMarkup({
          sort: function sort(_ref10) {
            var sortBy = _ref10.sortBy,
                sortDirection = _ref10.sortDirection;
            return sortCalls.push({ sortBy: sortBy, sortDirection: sortDirection });
          },
          sortBy: 'name',
          sortDirection: sortDirection
        })));
        var nameColumn = rendered.querySelector('.ReactVirtualized__Table__headerColumn:first-of-type');

        Simulate.click(nameColumn);
        expect(sortCalls.length).toEqual(1);

        var _sortCalls$ = sortCalls[0],
            sortBy = _sortCalls$.sortBy,
            newSortDirection = _sortCalls$.sortDirection;

        var expectedSortDirection = sortDirection === SortDirection.ASC ? SortDirection.DESC : SortDirection.ASC;
        expect(sortBy).toEqual('name');
        expect(newSortDirection).toEqual(expectedSortDirection);
      });
    });

    it('should call sort with the correct arguments when a new sort-by column header is clicked', function () {
      var sortCalls = [];
      var rendered = findDOMNode(render(getMarkup({
        sort: function sort(_ref11) {
          var sortBy = _ref11.sortBy,
              sortDirection = _ref11.sortDirection;
          return sortCalls.push({ sortBy: sortBy, sortDirection: sortDirection });
        },
        sortBy: 'email',
        sortDirection: SortDirection.ASC
      })));
      var nameColumn = rendered.querySelector('.ReactVirtualized__Table__headerColumn:first-of-type');

      Simulate.click(nameColumn);
      expect(sortCalls.length).toEqual(1);

      var _sortCalls$2 = sortCalls[0],
          sortBy = _sortCalls$2.sortBy,
          sortDirection = _sortCalls$2.sortDirection;

      expect(sortBy).toEqual('name');
      expect(sortDirection).toEqual(SortDirection.ASC);
    });

    it('should call sort when a column header is activated via ENTER or SPACE key', function () {
      var sortCalls = [];
      var rendered = findDOMNode(render(getMarkup({
        sort: function sort(_ref12) {
          var sortBy = _ref12.sortBy,
              sortDirection = _ref12.sortDirection;
          return sortCalls.push({ sortBy: sortBy, sortDirection: sortDirection });
        },
        sortBy: 'name'
      })));
      var nameColumn = rendered.querySelector('.ReactVirtualized__Table__headerColumn:first-of-type');
      expect(sortCalls.length).toEqual(0);
      Simulate.keyDown(nameColumn, { key: ' ' });
      expect(sortCalls.length).toEqual(1);
      Simulate.keyDown(nameColumn, { key: 'Enter' });
      expect(sortCalls.length).toEqual(2);
      Simulate.keyDown(nameColumn, { key: 'F' });
      expect(sortCalls.length).toEqual(2);
    });
  });

  describe('headerRowRenderer', function () {
    it('should render a custom header row if one is provided', function () {
      var headerRowRenderer = jest.fn().mockReturnValue(React.createElement(
        'div',
        null,
        'foo bar'
      ));
      var rendered = findDOMNode(render(getMarkup({
        headerHeight: 33,
        headerRowRenderer: headerRowRenderer,
        rowClassName: 'someRowClass'
      })));
      expect(rendered.textContent).toContain('foo bar');
      expect(headerRowRenderer).toHaveBeenCalled();
      var params = headerRowRenderer.mock.calls[0][0];
      expect(params.className).toContain('someRowClass');
      expect(params.columns).toHaveLength(2);
      expect(params.style.height).toBe(33);
    });
  });

  describe('headerRenderer', function () {
    it('should render a custom header if one is provided', function () {
      var columnData = { foo: 'foo', bar: 'bar' };
      var headerRendererCalls = [];
      var rendered = findDOMNode(render(getMarkup({
        columnData: columnData,
        headerRenderer: function headerRenderer(params) {
          headerRendererCalls.push(params);
          return 'custom header';
        },
        sortBy: 'name',
        sortDirection: SortDirection.ASC
      })));
      var nameColumn = rendered.querySelector('.ReactVirtualized__Table__headerColumn:first-of-type');

      expect(nameColumn.textContent).toContain('custom header');
      expect(headerRendererCalls.length).toBeTruthy();

      var headerRendererCall = headerRendererCalls[0];
      expect(headerRendererCall.columnData).toEqual(columnData);
      expect(headerRendererCall.dataKey).toEqual('name');
      expect(headerRendererCall.disableSort).toEqual(false);
      expect(headerRendererCall.label).toEqual('Name');
      expect(headerRendererCall.sortBy).toEqual('name');
      expect(headerRendererCall.sortDirection).toEqual(SortDirection.ASC);
    });

    it('should honor sort for custom headers', function () {
      var sortCalls = [];
      var rendered = findDOMNode(render(getMarkup({
        headerRenderer: function headerRenderer(params) {
          return 'custom header';
        },
        sort: function sort(_ref13) {
          var sortBy = _ref13.sortBy,
              sortDirection = _ref13.sortDirection;
          return sortCalls.push([sortBy, sortDirection]);
        },
        sortBy: 'name',
        sortDirection: SortDirection.ASC
      })));
      var nameColumn = rendered.querySelector('.ReactVirtualized__Table__headerColumn:first-of-type');

      Simulate.click(nameColumn);

      expect(sortCalls.length).toEqual(1);
      var sortCall = sortCalls[0];
      expect(sortCall[0]).toEqual('name');
      expect(sortCall[1]).toEqual(SortDirection.DESC);
    });

    it('should honor :onHeaderClick for custom header', function () {
      var columnData = { foo: 'foo', bar: 'bar' };
      var onHeaderClick = jest.fn();
      var rendered = findDOMNode(render(getMarkup({
        columnData: columnData,
        headerRenderer: function headerRenderer(params) {
          return 'custom header';
        },
        onHeaderClick: onHeaderClick
      })));
      var nameColumn = rendered.querySelector('.ReactVirtualized__Table__headerColumn:first-of-type');

      Simulate.click(nameColumn);

      expect(onHeaderClick).toHaveBeenCalledTimes(1);
      var params = onHeaderClick.mock.calls[0][0];
      expect(params.dataKey).toEqual('name');
      expect(params.columnData).toEqual(columnData);
      expect(params.event.type).toEqual('click');
    });
  });

  describe('noRowsRenderer', function () {
    it('should call :noRowsRenderer if :rowCount is 0', function () {
      var rendered = render(getMarkup({
        noRowsRenderer: function noRowsRenderer() {
          return React.createElement(
            'div',
            null,
            'No rows!'
          );
        },
        rowCount: 0
      }));
      var bodyDOMNode = findDOMNode(rendered.Grid);
      expect(bodyDOMNode.textContent).toEqual('No rows!');
    });

    it('should render an empty body if :rowCount is 0 and there is no :noRowsRenderer', function () {
      var rendered = render(getMarkup({
        rowCount: 0
      }));
      var bodyDOMNode = findDOMNode(rendered.Grid);
      expect(bodyDOMNode.textContent).toEqual('');
    });
  });

  describe('onHeaderClick', function () {
    it('should call :onHeaderClick with the correct arguments when a column header is clicked and sorting is disabled', function () {
      var onHeaderClick = jest.fn();
      var rendered = findDOMNode(render(getMarkup({
        disableSort: true,
        onHeaderClick: onHeaderClick
      })));
      var nameColumn = rendered.querySelector('.ReactVirtualized__Table__headerColumn:first-of-type');

      Simulate.click(nameColumn);

      expect(onHeaderClick).toHaveBeenCalledTimes(1);
      var params = onHeaderClick.mock.calls[0][0];
      expect(params.dataKey).toEqual('name');
      expect(params.columnData.data).toEqual(123);
      expect(params.event.type).toEqual('click');
    });

    it('should call :onHeaderClick with the correct arguments when a column header is clicked and sorting is enabled', function () {
      var onHeaderClick = jest.fn();
      var rendered = findDOMNode(render(getMarkup({
        disableSort: false,
        onHeaderClick: onHeaderClick
      })));
      var nameColumn = rendered.querySelector('.ReactVirtualized__Table__headerColumn:first-of-type');

      Simulate.click(nameColumn);

      expect(onHeaderClick).toHaveBeenCalledTimes(1);
      var params = onHeaderClick.mock.calls[0][0];
      expect(params.dataKey).toEqual('name');
      expect(params.columnData.data).toEqual(123);
      expect(params.event.type).toEqual('click');
    });
  });

  describe('onRowClick', function () {
    it('should call :onRowClick with the correct :rowIndex when a row is clicked', function () {
      var onRowClick = jest.fn();
      var rendered = findDOMNode(render(getMarkup({
        onRowClick: onRowClick
      })));
      var rows = rendered.querySelectorAll('.ReactVirtualized__Table__row');
      Simulate.click(rows[0]);
      Simulate.click(rows[3]);
      expect(onRowClick).toHaveBeenCalledTimes(2);
      expect(onRowClick.mock.calls.map(function (call) {
        return call[0].index;
      })).toEqual([0, 3]);
    });
  });

  describe('onRowDoubleClick', function () {
    it('should call :onRowDoubleClick with the correct :rowIndex when a row is clicked', function () {
      var onRowDoubleClick = jest.fn();
      var rendered = findDOMNode(render(getMarkup({
        onRowDoubleClick: onRowDoubleClick
      })));
      var rows = rendered.querySelectorAll('.ReactVirtualized__Table__row');
      Simulate.doubleClick(rows[0]);
      Simulate.doubleClick(rows[3]);
      expect(onRowDoubleClick).toHaveBeenCalledTimes(2);
      expect(onRowDoubleClick.mock.calls.map(function (call) {
        return call[0].index;
      })).toEqual([0, 3]);
    });
  });

  describe('onRowMouseOver/Out', function () {
    it('should call :onRowMouseOver and :onRowMouseOut with the correct :rowIndex when the mouse is moved over rows', function () {
      var onRowMouseOver = jest.fn();
      var onRowMouseOut = jest.fn();
      var rendered = findDOMNode(render(getMarkup({
        onRowMouseOver: onRowMouseOver,
        onRowMouseOut: onRowMouseOut
      })));

      var simulateMouseOver = function simulateMouseOver(from, to) {
        Simulate.mouseOut(from, { relatedTarget: to });
        Simulate.mouseOver(to, { relatedTarget: from });
      };

      var rows = rendered.querySelectorAll('.ReactVirtualized__Table__row');

      simulateMouseOver(rows[0], rows[1]);
      simulateMouseOver(rows[1], rows[2]);
      simulateMouseOver(rows[2], rows[3]);

      expect(onRowMouseOver).toHaveBeenCalled();
      expect(onRowMouseOut).toHaveBeenCalled();
      expect(onRowMouseOver.mock.calls.map(function (call) {
        return call[0].index;
      })).toEqual([1, 2, 3]);
      expect(onRowMouseOut.mock.calls.map(function (call) {
        return call[0].index;
      })).toEqual([0, 1, 2]);
    });
  });

  describe('rowClassName', function () {
    it('should render a static classname given :rowClassName as a string', function () {
      var staticClassName = 'staticClass';
      var rendered = findDOMNode(render(getMarkup({
        rowClassName: staticClassName
      })));
      var rows = rendered.querySelectorAll('.ReactVirtualized__Table__row');
      _Array$from(rows).forEach(function (row, index) {
        expect(row.className).toContain(staticClassName);
      });
    });

    it('should render dynamic classname given :rowClassName as a function', function () {
      var rendered = findDOMNode(render(getMarkup({
        rowClassName: function rowClassName(_ref14) {
          var index = _ref14.index;
          return index % 2 === 0 ? 'even' : 'odd';
        }
      })));
      var rows = rendered.querySelectorAll('.ReactVirtualized__Table__row');
      _Array$from(rows).forEach(function (row, index) {
        if (index % 2 === 0) {
          expect(row.className).toContain('even');
          expect(row.className).not.toContain('odd');
        } else {
          expect(row.className).toContain('odd');
          expect(row.className).not.toContain('even');
        }
      });
    });
  });

  describe('onRowsRendered', function () {
    it('should call :onRowsRendered at least one row is rendered', function () {
      var startIndex = void 0,
          stopIndex = void 0;
      render(getMarkup({
        onRowsRendered: function onRowsRendered(params) {
          var _params;

          return _params = params, startIndex = _params.startIndex, stopIndex = _params.stopIndex, _params;
        }
      }));
      expect(startIndex).toEqual(0);
      expect(stopIndex).toEqual(7);
    });

    it('should not call :onRowsRendered unless the start or stop indices have changed', function () {
      var numCalls = 0;
      var startIndex = void 0;
      var stopIndex = void 0;
      var onRowsRendered = function onRowsRendered(params) {
        startIndex = params.startIndex;
        stopIndex = params.stopIndex;
        numCalls++;
      };
      render(getMarkup({ onRowsRendered: onRowsRendered }));
      expect(numCalls).toEqual(1);
      expect(startIndex).toEqual(0);
      expect(stopIndex).toEqual(7);
      render(getMarkup({ onRowsRendered: onRowsRendered }));
      expect(numCalls).toEqual(1);
      expect(startIndex).toEqual(0);
      expect(stopIndex).toEqual(7);
    });

    it('should call :onRowsRendered if the start or stop indices have changed', function () {
      var numCalls = 0;
      var startIndex = void 0;
      var stopIndex = void 0;
      var onRowsRendered = function onRowsRendered(params) {
        startIndex = params.startIndex;
        stopIndex = params.stopIndex;
        numCalls++;
      };
      render(getMarkup({ onRowsRendered: onRowsRendered }));
      expect(numCalls).toEqual(1);
      expect(startIndex).toEqual(0);
      expect(stopIndex).toEqual(7);
      render(getMarkup({
        height: 50,
        onRowsRendered: onRowsRendered
      }));
      expect(numCalls).toEqual(2);
      expect(startIndex).toEqual(0);
      expect(stopIndex).toEqual(2);
    });

    it('should not call :onRowsRendered if no rows are rendered', function () {
      var startIndex = void 0,
          stopIndex = void 0;
      render(getMarkup({
        height: 0,
        onRowsRendered: function onRowsRendered(params) {
          var _params2;

          return _params2 = params, startIndex = _params2.startIndex, stopIndex = _params2.stopIndex, _params2;
        }
      }));
      expect(startIndex).toEqual(undefined);
      expect(stopIndex).toEqual(undefined);
    });
  });

  describe(':scrollTop property', function () {
    it('should render correctly when an initial :scrollTop property is specified', function () {
      var startIndex = void 0,
          stopIndex = void 0;
      render(getMarkup({
        onRowsRendered: function onRowsRendered(params) {
          var _params3;

          return _params3 = params, startIndex = _params3.startIndex, stopIndex = _params3.stopIndex, _params3;
        },
        scrollTop: 80
      }));
      expect(startIndex).toEqual(8);
      expect(stopIndex).toEqual(15);
    });

    it('should render correctly when :scrollTop property is updated', function () {
      var startIndex = void 0,
          stopIndex = void 0;

      render(getMarkup({
        onRowsRendered: function onRowsRendered(params) {
          var _params4;

          return _params4 = params, startIndex = _params4.startIndex, stopIndex = _params4.stopIndex, _params4;
        }
      }));
      expect(startIndex).toEqual(0);
      expect(stopIndex).toEqual(7);

      render(getMarkup({
        onRowsRendered: function onRowsRendered(params) {
          var _params5;

          return _params5 = params, startIndex = _params5.startIndex, stopIndex = _params5.stopIndex, _params5;
        },
        scrollTop: 80
      }));
      expect(startIndex).toEqual(8);
      expect(stopIndex).toEqual(15);
    });
  });

  describe('styles, classNames, and ids', function () {
    it('should use the expected global CSS classNames', function () {
      var node = findDOMNode(render(getMarkup({
        sort: function sort() {},
        sortBy: 'name',
        sortDirection: SortDirection.ASC
      })));
      expect(node.className).toEqual('ReactVirtualized__Table');
      expect(node.querySelector('.ReactVirtualized__Table__headerRow')).toBeTruthy();
      expect(node.querySelector('.ReactVirtualized__Table__rowColumn')).toBeTruthy();
      expect(node.querySelector('.ReactVirtualized__Table__headerColumn')).toBeTruthy();
      expect(node.querySelector('.ReactVirtualized__Table__row')).toBeTruthy();
      expect(node.querySelector('.ReactVirtualized__Table__sortableHeaderColumn')).toBeTruthy();
      expect(node.querySelector('.ReactVirtualized__Table__sortableHeaderIcon')).toBeTruthy();
    });

    it('should use a custom :className if specified', function () {
      var node = findDOMNode(render(getMarkup({
        className: 'foo',
        headerClassName: 'bar',
        rowClassName: 'baz'
      })));
      expect(node.className).toContain('foo');
      expect(node.querySelectorAll('.bar').length).toEqual(2);
      expect(node.querySelectorAll('.baz').length).toEqual(9);
    });

    it('should use a custom :id if specified', function () {
      var node = findDOMNode(render(getMarkup({ id: 'bar' })));
      expect(node.getAttribute('id')).toEqual('bar');
    });

    it('should not set :id on the inner Grid', function () {
      var node = findDOMNode(render(getMarkup({ id: 'bar' })));
      var grid = node.querySelector('.ReactVirtualized__Grid');
      expect(grid.getAttribute('id')).not.toEqual('bar');
    });

    it('should use custom :styles if specified', function () {
      var columnStyle = { backgroundColor: 'red' };
      var headerStyle = { backgroundColor: 'blue' };
      var rowStyle = { backgroundColor: 'green' };
      var style = { backgroundColor: 'orange' };
      var node = findDOMNode(render(getMarkup({
        columnStyle: columnStyle,
        headerStyle: headerStyle,
        rowStyle: rowStyle,
        style: style
      })));
      expect(node.querySelector('.ReactVirtualized__Table__rowColumn').style.backgroundColor).toEqual('red');
      expect(node.querySelector('.ReactVirtualized__Table__headerColumn').style.backgroundColor).toEqual('blue');
      expect(node.querySelector('.ReactVirtualized__Table__row').style.backgroundColor).toEqual('green');
      expect(node.style.backgroundColor).toEqual('orange');
    });

    it('should render dynamic style given :rowStyle as a function', function () {
      var rendered = findDOMNode(render(getMarkup({
        rowStyle: function rowStyle(_ref15) {
          var index = _ref15.index;
          return index % 2 === 0 ? { backgroundColor: 'red' } : { backgroundColor: 'green' };
        }
      })));
      var rows = rendered.querySelectorAll('.ReactVirtualized__Table__row');
      _Array$from(rows).forEach(function (row, index) {
        if (index % 2 === 0) {
          expect(row.style.backgroundColor).toEqual('red');
        } else {
          expect(row.style.backgroundColor).toEqual('green');
        }
      });
    });

    it('should pass :gridClassName and :gridStyle to the inner Grid', function () {
      var rendered = findDOMNode(render(getMarkup({
        gridClassName: 'foo',
        gridStyle: { backgroundColor: 'red' }
      })));
      var grid = rendered.querySelector('.ReactVirtualized__Grid');
      expect(grid.className).toContain('foo');
      expect(grid.style.backgroundColor).toEqual('red');
    });
  });

  describe('overscanRowCount', function () {
    it('should not overscan by default', function () {
      var mock = jest.fn();
      mock.mockImplementation(overscanIndicesGetter);

      render(getMarkup({
        overscanIndicesGetter: mock
      }));
      expect(mock.mock.calls[0][0].overscanCellsCount).toEqual(0);
      expect(mock.mock.calls[1][0].overscanCellsCount).toEqual(0);
    });

    it('should overscan the specified amount', function () {
      var mock = jest.fn();
      mock.mockImplementation(overscanIndicesGetter);

      render(getMarkup({
        overscanIndicesGetter: mock,
        overscanRowCount: 10
      }));
      expect(mock.mock.calls[0][0].overscanCellsCount).toEqual(0);
      expect(mock.mock.calls[1][0].overscanCellsCount).toEqual(10);
    });
  });

  describe('onScroll', function () {
    it('should trigger callback when component initially mounts', function () {
      var onScrollCalls = [];
      render(getMarkup({
        onScroll: function onScroll(params) {
          return onScrollCalls.push(params);
        }
      }));
      expect(onScrollCalls).toEqual([{
        clientHeight: 80,
        scrollHeight: 1000,
        scrollTop: 0
      }]);
    });

    it('should trigger callback when component scrolls', function () {
      var onScrollCalls = [];
      var rendered = render(getMarkup({
        onScroll: function onScroll(params) {
          return onScrollCalls.push(params);
        }
      }));
      var target = {
        scrollLeft: 0,
        scrollTop: 100
      };
      rendered.Grid._scrollingContainer = target; // HACK to work around _onScroll target check
      Simulate.scroll(findDOMNode(rendered.Grid), { target: target });
      expect(onScrollCalls.length).toEqual(2);
      expect(onScrollCalls[1]).toEqual({
        clientHeight: 80,
        scrollHeight: 1000,
        scrollTop: 100
      });
    });
  });

  describe('a11y properties', function () {
    it('should set aria role on the table', function () {
      var node = findDOMNode(render(getMarkup()));
      expect(node.getAttribute('role')).toEqual('grid');
    });

    it('should set aria role on the header row', function () {
      var rendered = findDOMNode(render(getMarkup()));
      var row = rendered.querySelector('.ReactVirtualized__Table__headerRow');
      expect(row.getAttribute('role')).toEqual('row');
    });

    it('should set appropriate aria role on the grid', function () {
      var rendered = findDOMNode(render(getMarkup()));
      var grid = rendered.querySelector('.ReactVirtualized__Table__Grid');
      expect(grid.getAttribute('role')).toEqual('rowgroup');
    });

    it('should set aria role on a row', function () {
      var rendered = findDOMNode(render(getMarkup()));
      var row = rendered.querySelector('.ReactVirtualized__Table__row');
      expect(row.getAttribute('role')).toEqual('row');
    });

    it('should set aria role on a cell', function () {
      var rendered = findDOMNode(render(getMarkup()));
      var cell = rendered.querySelector('.ReactVirtualized__Table__rowColumn');
      expect(cell.getAttribute('role')).toEqual('gridcell');
    });

    it('should set aria-describedby on a cell when the column has an id', function () {
      var columnID = 'column-header-test';
      var rendered = findDOMNode(render(getMarkup({
        columnID: columnID
      })));
      var cell = rendered.querySelector('.ReactVirtualized__Table__rowColumn');
      expect(cell.getAttribute('aria-describedby')).toEqual(columnID);
    });

    it('should attach a11y properties to a row if :onRowClick is specified', function () {
      var rendered = findDOMNode(render(getMarkup({
        onRowClick: function onRowClick() {}
      })));
      var row = rendered.querySelector('.ReactVirtualized__Table__row');
      expect(row.getAttribute('aria-label')).toEqual('row');
      expect(row.tabIndex).toEqual(0);
    });

    it('should not attach a11y properties to a row if no :onRowClick is specified', function () {
      var rendered = findDOMNode(render(getMarkup({
        onRowClick: null
      })));
      var row = rendered.querySelector('.ReactVirtualized__Table__row');
      expect(row.getAttribute('aria-label')).toEqual(null);
      expect(row.tabIndex).toEqual(-1);
    });

    it('should set aria role on a header column', function () {
      var rendered = findDOMNode(render(getMarkup()));
      var header = rendered.querySelector('.ReactVirtualized__Table__headerColumn');
      expect(header.getAttribute('role')).toEqual('columnheader');
    });

    it('should set aria-sort ascending on a header column if the column is sorted ascending', function () {
      var rendered = findDOMNode(render(getMarkup({
        sortBy: 'name',
        sortDirection: SortDirection.ASC
      })));
      var header = rendered.querySelector('.ReactVirtualized__Table__headerColumn');
      expect(header.getAttribute('aria-sort')).toEqual('ascending');
    });

    it('should set aria-sort descending on a header column if the column is sorted descending', function () {
      var rendered = findDOMNode(render(getMarkup({
        sortBy: 'name',
        sortDirection: SortDirection.DESC
      })));
      var header = rendered.querySelector('.ReactVirtualized__Table__headerColumn');
      expect(header.getAttribute('aria-sort')).toEqual('descending');
    });

    it('should set id on a header column when the column has an id', function () {
      var columnID = 'column-header-test';
      var rendered = findDOMNode(render(getMarkup({
        columnID: columnID
      })));
      var header = rendered.querySelector('.ReactVirtualized__Table__headerColumn');
      expect(header.getAttribute('id')).toEqual(columnID);
    });

    it('should attach a11y properties to a header column if sort is enabled', function () {
      var rendered = findDOMNode(render(getMarkup({
        disableSort: false,
        sort: function sort() {}
      })));
      var row = rendered.querySelector('.ReactVirtualized__Table__headerColumn');
      expect(row.getAttribute('aria-label')).toEqual('Name');
      expect(row.tabIndex).toEqual(0);
    });

    it('should not attach a11y properties to a header column if sort is not enabled', function () {
      var rendered = findDOMNode(render(getMarkup({
        disableSort: true
      })));
      var row = rendered.querySelector('.ReactVirtualized__Table__headerColumn');
      expect(row.getAttribute('aria-label')).toEqual(null);
      expect(row.tabIndex).toEqual(-1);
    });
  });

  describe('tabIndex', function () {
    it('should be focusable by default', function () {
      var rendered = findDOMNode(render(getMarkup()));
      expect(rendered.querySelector('.ReactVirtualized__Grid').tabIndex).toEqual(0);
    });

    it('should allow tabIndex to be overridden', function () {
      var rendered = findDOMNode(render(getMarkup({
        tabIndex: -1
      })));
      expect(rendered.querySelector('.ReactVirtualized__Grid').tabIndex).toEqual(-1);
    });
  });

  describe('pure', function () {
    it('should not re-render unless props have changed', function () {
      var headerRendererCalled = false;
      var cellRendererCalled = false;
      function headerRenderer() {
        headerRendererCalled = true;
        return 'foo';
      }
      function cellRenderer() {
        cellRendererCalled = true;
        return 'foo';
      }
      var markup = getMarkup({
        headerRenderer: headerRenderer,
        cellRenderer: cellRenderer
      });
      render(markup);
      expect(headerRendererCalled).toEqual(true);
      expect(cellRendererCalled).toEqual(true);
      headerRendererCalled = false;
      cellRendererCalled = false;
      render(markup);
      expect(headerRendererCalled).toEqual(false);
      expect(cellRendererCalled).toEqual(false);
    });

    it('should re-render both the Table and the inner Grid whenever an external property changes', function () {
      var headerRendererCalled = false;
      var cellRendererCalled = false;
      function headerRenderer() {
        headerRendererCalled = true;
        return 'foo';
      }
      function cellRenderer() {
        cellRendererCalled = true;
        return 'foo';
      }
      var initialProperties = {
        autoHeight: false,
        cellRenderer: cellRenderer,
        estimatedRowSize: 15,
        headerRenderer: headerRenderer,
        overscanRowCount: 1,
        rowHeight: 15,
        rowCount: 20,
        scrollToAlignment: 'auto',
        scrollTop: 0,
        sortBy: 'name',
        sortDirection: SortDirection.ASC,
        tabIndex: null
      };
      var changedProperties = {
        autoHeight: true,
        estimatedRowSize: 10,
        overscanRowCount: 0,
        rowHeight: 10,
        rowCount: 10,
        scrollToAlignment: 'center',
        scrollTop: 1,
        sortBy: 'email',
        sortDirection: SortDirection.DESC,
        tabIndex: 1
      };
      _Object$entries(changedProperties).forEach(function (_ref16) {
        var _ref17 = _slicedToArray(_ref16, 2),
            key = _ref17[0],
            value = _ref17[1];

        render.unmount(); // Reset
        render(getMarkup(initialProperties));
        headerRendererCalled = true;
        cellRendererCalled = false;
        render(getMarkup(_extends({}, initialProperties, _defineProperty({}, key, value))));
        expect(headerRendererCalled).toEqual(true);
        expect(cellRendererCalled).toEqual(true);
      });
    });
  });

  it('should set the width of the single-column inner Grid to auto', function () {
    var rendered = findDOMNode(render(getMarkup()));
    expect(rendered.querySelector('.ReactVirtualized__Grid__innerScrollContainer').style.width).toEqual('auto');
  });

  it('should relay the Grid :parent param to the Column :cellRenderer', function () {
    var cellRenderer = jest.fn().mockReturnValue(null);
    findDOMNode(render(getMarkup({ cellRenderer: cellRenderer })));
    expect(cellRenderer.mock.calls[0][0].parent).not.toBeUndefined();
  });
});