UNPKG

@dgit/react-data-grid-addons

Version:

A set of addons for react-data-grid

1,075 lines (929 loc) 43.4 kB
const React = require('react'); const ReactDOM = require('react-dom'); const Grid = require('react-data-grid'); const { editors: { CheckboxEditor } } = Grid; const TestUtils = require('react-dom/test-utils'); const StubComponent = require('../../../../test/StubComponent'); const mockStateObject = require('./data/MockStateObject'); const { mount } = require('enzyme'); describe('Grid', function() { beforeEach(function() { this.columns = [ { key: 'id', name: 'ID', width: 100, events: { onClick: () => {}} }, { key: 'title', name: 'Title', width: 100 }, { key: 'count', name: 'Count', width: 100 }, { key: 'country', name: 'Country', width: 100, events: { onClick: () => {}, onDoubleClick: () => {}, onDragOver: () => {}}} ]; this._rows = []; this._selectedRows = []; this.rowGetter = (i) => this._rows[i]; for (let i = 0; i < 1000; i++) { this._rows.push({ id: i, title: `Title ${i}`, count: i * 1000, isOdd: !!(i % 2) }); } this.noop = function() {}; this.testProps = { enableCellSelect: true, columns: this.columns, rowGetter: this.rowGetter, rowsCount: this._rows.length, width: 300, onRowUpdated: this.noop, onCellCopyPaste: this.noop, onCellsDragged: this.noop, onGridSort: this.noop, onAddFilter: () => {}, rowKey: 'id' }; this.buildFakeEvent = (addedData) => { return Object.assign({}, { preventDefault: this.noop, stopPropagation: this.noop }, addedData); }; this.buildFakeCellUodate = (addedData) => { return Object.assign({}, { cellKey: 'title', rowIdx: 0, updated: { title: 'some new title' }, key: 'Enter' }, addedData); }; this.getBaseGrid = () => this.component.base; this.getCellMetaData = () => this.getBaseGrid().props.cellMetaData; this.simulateGridKeyDown = (key, ctrlKey) => { let fakeEvent = this.buildFakeEvent({ key: key, keyCode: key, ctrlKey: ctrlKey }); this.getBaseGrid().props.onViewportKeydown(fakeEvent); }; this.simulateGridKeyDownWithKeyCode = (keyCode) => { let fakeEvent = this.buildFakeEvent({ keyCode: keyCode }); this.getBaseGrid().props.onViewportKeydown(fakeEvent); }; let buildProps = (addedProps) => Object.assign({}, this.testProps, addedProps); this.createComponent = (addedProps) => { return mount(<Grid {...buildProps(addedProps)}/>); }; this.componentWrapper = this.createComponent(); this.component = this.componentWrapper.instance(); }); it('should create a new instance of Grid', function() { expect(this.component).toBeDefined(); }); it('should render a BaseGrid stub', function() { expect(this.getBaseGrid()).toBeDefined(); }); it('should be initialized with correct state', function() { let events = [this.columns[0].events, this.columns[1].events, this.columns[2].events, this.columns[3].events]; expect(this.component.state).toEqual(mockStateObject({ selectedRows: this._selectedRows }, events)); }); // Set of tests for the props that defined the height of our rows describe('when defininig heigths on props', function() { describe('for defaults props', function() { beforeEach(function() { const ToolBarStub = new StubComponent('Toolbar'); this.component = this.createComponent({ toolbar: <ToolBarStub /> }).instance(); this.toolbarInstance = TestUtils.findRenderedComponentWithType(this.component, ToolBarStub); this.toolbarInstance.props.onToggleFilter(); this.baseGrid = this.getBaseGrid(); }); it('uses the appropriate default for the grid row height', function() { expect(this.baseGrid.props.rowHeight).toEqual(35); }); it('uses the appropriate default for the header row height', function() { expect(this.baseGrid.props.headerRows[0].height).toEqual(35); }); it('uses the appropriate default for the header filter row height', function() { expect(this.baseGrid.props.headerRows[1].height).toEqual(45); }); }); describe('for a given row height prop', function() { beforeEach(function() { const ToolBarStub = new StubComponent('Toolbar'); this.component = this.createComponent({ toolbar: <ToolBarStub />, rowHeight: 40 }).instance(); this.toolbarInstance = TestUtils.findRenderedComponentWithType(this.component, ToolBarStub); this.toolbarInstance.props.onToggleFilter(); this.baseGrid = this.getBaseGrid(); }); it('passes the correct heigth to the grid rows', function() { expect(this.baseGrid.props.rowHeight).toEqual(40); }); it('passes the grid row heigth to the header row when no height to the specific header row is provided', function() { expect(this.baseGrid.props.headerRows[0].height).toEqual(40); }); it('uses the default prop height for the filter row when none is provided', function() { expect(this.baseGrid.props.headerRows[1].height).toEqual(45); }); }); describe('for given row and header height props', function() { beforeEach(function() { const ToolBarStub = new StubComponent('Toolbar'); this.component = this.createComponent({ toolbar: <ToolBarStub />, rowHeight: 40, headerRowHeight: 50, headerFiltersHeight: 60 }).instance(); this.toolbarInstance = TestUtils.findRenderedComponentWithType(this.component, ToolBarStub); this.toolbarInstance.props.onToggleFilter(); this.baseGrid = this.getBaseGrid(); }); it('passes the correct heigth to the grid rows', function() { expect(this.baseGrid.props.rowHeight).toEqual(40); }); it('passes the correct heigth to the header row', function() { expect(this.baseGrid.props.headerRows[0].height).toEqual(50); }); it('passes the correct heigth to the header filter row', function() { expect(this.baseGrid.props.headerRows[1].height).toEqual(60); }); }); }); describe('if passed in as props to grid', function() { beforeEach(function() { const ToolBarStub = new StubComponent('Toolbar'); this.component = this.createComponent({ toolbar: <ToolBarStub /> }).instance(); this.toolbarInstance = TestUtils.findRenderedComponentWithType(this.component, ToolBarStub); }); it('should render a Toolbar', function() { expect(this.toolbarInstance).toBeDefined(); }); describe('onToggleFilter trigger of Toolbar', function() { beforeEach(function() { this.toolbarInstance.props.onToggleFilter(); this.baseGrid = this.getBaseGrid(); }); it('should set filter state of grid and render a filterable header row', function() { let filterableHeaderRow = this.baseGrid.props.headerRows[1]; expect(this.component.state.canFilter).toBe(true); expect(this.baseGrid.props.headerRows.length).toEqual(2); expect(typeof filterableHeaderRow.ref).toEqual('function'); }); }); }); describe('When cell selection disabled', function() { beforeEach(function() { this.component = this.createComponent({ enableCellSelect: false, columns: this.columns, rowGetter: this.rowGetter, rowsCount: this._rows.length, width: 300 }).instance(); }); it('grid should be initialized with selected state of {rowIdx : -1, idx : -1}', function() { expect(this.component.state.selected).toEqual({ rowIdx: -1, idx: -1 }); }); }); describe('When row selection enabled', function() { beforeEach(function() { this.component = this.createComponent({ enableRowSelect: true}).instance(); this.baseGrid = this.getBaseGrid(); this.selectRowCol = this.baseGrid.props.columnMetrics.columns[0]; }); it('should render an additional Select Row column', function() { expect(this.baseGrid.props.columnMetrics.columns.length).toEqual(this.columns.length + 1); expect(this.selectRowCol.key).toEqual('select-row'); expect(TestUtils.isElementOfType(this.selectRowCol.formatter, CheckboxEditor)).toBe(true); }); describe('checking header checkbox', function() { beforeEach(function() { let checkboxWrapper = document.createElement('div'); checkboxWrapper.innerHTML = '<input type="checkbox" value="value" checked="true" />'; this.checkbox = checkboxWrapper.querySelector('input'); const SelectAll = this.selectRowCol.headerRenderer; this.selectAllWrapper = mount(SelectAll); this.fakeEvent = this.buildFakeEvent({ currentTarget: this.checkbox }); this.selectAllWrapper.props().onChange(this.fakeEvent); }); it('should select all rows', function() { let selectedRows = this.component.state.selectedRows; expect(selectedRows.length).toEqual(this._rows.length); expect(selectedRows.length).toBeGreaterThan(1); selectedRows.forEach((selected) => expect(selected.isSelected).toBe(true)); }); describe('and then unchecking header checkbox', function() { beforeEach(function() { this.checkbox.checked = false; this.selectAllWrapper.props().onChange(this.fakeEvent); }); it('should deselect all rows', function() { let selectedRows = this.component.state.selectedRows; expect(selectedRows.length).toBeGreaterThan(1); selectedRows.forEach((selected) => expect(selected.isSelected).toBe(false)); }); }); }); describe('when selected is false', function() { beforeEach(function() { this.component.setState({selectedRows: [{id: 0, isSelected: false}, {id: 1, isSelected: false}, {id: 2, isSelected: false}, {id: 3, isSelected: false}]}); let selectRowCol = this.baseGrid.props.columnMetrics.columns[0]; selectRowCol.onCellChange(3, 'select-row', this._rows[3], this.buildFakeEvent()); }); it('should be able to select an individual row', function() { expect(this.component.state.selectedRows[3].isSelected).toBe(true); }); }); describe('when selected is null', function() { beforeEach(function() { this.component.setState({selectedRows: [{id: 0, isSelected: null}, {id: 1, isSelected: null}, {id: 2, isSelected: null}, {id: 3, isSelected: null}]}); let selectRowCol = this.baseGrid.props.columnMetrics.columns[0]; selectRowCol.onCellChange(2, 'select-row', this._rows[2], this.buildFakeEvent()); }); it('should be able to select an individual row', function() { expect(this.component.state.selectedRows[2].isSelected).toBe(true); }); }); describe('when selected is true', function() { beforeEach(function() { this.component.setState({selectedRows: [{id: 0, isSelected: null}, {id: 1, isSelected: true}, {id: 2, isSelected: true}, {id: 3, isSelected: true}]}); let selectRowCol = this.baseGrid.props.columnMetrics.columns[0]; selectRowCol.onCellChange(3, 'select-row', this._rows[3], this.buildFakeEvent()); }); it('should be able to unselect an individual row ', function() { expect(this.component.state.selectedRows[3].isSelected).toBe(false); }); }); }); describe('Cell Navigation', function() { describe('when cell navigation is configured to default, none', function() { beforeEach(function() { this.component = this.createComponent({enableCellSelect: true}).instance(); }); describe('when on last cell in a row', function() { beforeEach(function() { this.component.setState({ selected: { idx: 3, rowIdx: 1 } }); }); it('selection should stay on cell', function() { this.simulateGridKeyDown('ArrowRight'); expect(this.component.state.selected).toEqual({ idx: 3, rowIdx: 1 }); }); }); describe('when on first cell in row', function() { beforeEach(function() { this.component.setState({ selected: { idx: 0, rowIdx: 1 } }); }); it('nothing should happen', function() { this.simulateGridKeyDown('ArrowLeft'); expect(this.component.state.selected).toEqual({ idx: 0, rowIdx: 1 }); }); }); describe('when row selection is enabled and positioned on cell before last in row', function() { beforeEach(function() { this.component.setState({ selected: { idx: 2, rowIdx: 1 }, enableRowSelect: true }); }); it('selection should move to last cell', function() { this.simulateGridKeyDown('ArrowRight'); expect(this.component.state.selected).toEqual({ idx: 3, rowIdx: 1 }); }); }); }); describe('when cell navigation is configured to change rows', function() { beforeEach(function() { this.component = this.createComponent({cellNavigationMode: 'changeRow', enableCellSelect: true}).instance(); }); describe('when on last cell in a row that\'s not the last', function() { beforeEach(function() { this.component.setState({ selected: { idx: 3, rowIdx: 1 } }); }); it('selection should move to first cell in next row', function() { this.simulateGridKeyDown('ArrowRight'); expect(this.component.state.selected).toEqual({ idx: 0, rowIdx: 2 }); }); }); describe('when on last cell in last row', function() { beforeEach(function() { this.component.setState({ selected: { idx: 3, rowIdx: 999 } }); }); it('nothing should happen', function() { this.simulateGridKeyDown('ArrowRight'); expect(this.component.state.selected).toEqual({ idx: 3, rowIdx: 999 }); }); }); describe('when on first cell in a row that\'s not the first', function() { beforeEach(function() { this.component.setState({ selected: { idx: 0, rowIdx: 2 } }); }); it('selection should move to last cell in previous row', function() { this.simulateGridKeyDown('ArrowLeft'); expect(this.component.state.selected).toEqual({ idx: 3, rowIdx: 1 }); }); }); describe('when on first cell in the first row', function() { beforeEach(function() { this.component.setState({ selected: { idx: 0, rowIdx: 0 } }); }); it('nothing should happen', function() { this.simulateGridKeyDown('ArrowLeft'); expect(this.component.state.selected).toEqual({ idx: 0, rowIdx: 0 }); }); }); describe('when row selection is enabled and positioned on cell before last in row', function() { beforeEach(function() { this.component.setState({ selected: { idx: 2, rowIdx: 1 }, enableRowSelect: true }); }); it('selection should move to last cell', function() { this.simulateGridKeyDown('ArrowRight'); expect(this.component.state.selected).toEqual({ idx: 3, rowIdx: 1 }); }); }); }); describe('when cell navigation is configured to loop over cells in row', function() { beforeEach(function() { this.component = this.createComponent({cellNavigationMode: 'loopOverRow', enableCellSelect: true}).instance(); }); describe('when on last cell, looping enabled', function() { beforeEach(function() { this.component.setState({ selected: { idx: 3, rowIdx: 1 } }); }); it('selection should move to first cell in same row', function() { this.simulateGridKeyDown('ArrowRight'); expect(this.component.state.selected).toEqual({ idx: 0, rowIdx: 1 }); }); }); describe('when on last cell in last row with looping enabled', function() { beforeEach(function() { this.component.setState({ selected: { idx: 3, rowIdx: 999 } }); }); it('selection should move to first cell in same row', function() { this.simulateGridKeyDown('ArrowRight'); expect(this.component.state.selected).toEqual({ idx: 0, rowIdx: 999 }); }); }); describe('when on first cell with looping enabled', function() { beforeEach(function() { this.component.setState({ selected: { idx: 0, rowIdx: 2 } }); }); it('selection should move to last cell in same row', function() { this.simulateGridKeyDown('ArrowLeft'); expect(this.component.state.selected).toEqual({ idx: 3, rowIdx: 2 }); }); }); describe('when on first cell in first row with looping enabled', function() { beforeEach(function() { this.component.setState({ selected: { idx: 0, rowIdx: 0 } }); }); it('selection should move to last cell in same row', function() { this.simulateGridKeyDown('ArrowLeft'); expect(this.component.state.selected).toEqual({ idx: 3, rowIdx: 0 }); }); }); describe('when row selection enabled and positioned on cell before last in row', function() { beforeEach(function() { this.component.setState({ selected: { idx: 2, rowIdx: 1 }, enableRowSelect: true }); }); it('selection should move to last cell', function() { this.simulateGridKeyDown('ArrowRight'); expect(this.component.state.selected).toEqual({ idx: 3, rowIdx: 1 }); }); }); }); }); describe('Cell Selection/DeSelection handlers', function() { describe('when cell selection/deselection handlers are passed', function() { beforeEach(function() { const extraProps = { onCellSelected: this.noop, onCellDeSelected: this.noop }; spyOn(extraProps, 'onCellSelected'); spyOn(extraProps, 'onCellDeSelected'); this.component = this.createComponent(extraProps).instance(); }); describe('cell is selected', function() { beforeEach(function() { this.component.setState({ selected: { rowIdx: 1, idx: 2 } }); }); it('deselection handler should have been called', function() { this.simulateGridKeyDown('ArrowRight'); expect(this.component.props.onCellDeSelected).toHaveBeenCalled(); expect(this.component.props.onCellDeSelected.calls.mostRecent().args[0]).toEqual({ rowIdx: 1, idx: 2 }); }); it('selection handler should have been called', function() { this.simulateGridKeyDown('ArrowRight'); expect(this.component.props.onCellSelected).toHaveBeenCalled(); expect(this.component.props.onCellSelected.calls.mostRecent().args[0]).toEqual({ rowIdx: 1, idx: 3 }); }); }); }); }); describe('When selection enabled and using rowSelection props', function() { beforeEach(function() { let self = this; this._selectedRows = []; this._deselectedRows = []; this.rows = [{id: '1', isSelected: true}, {id: '2', isSelected: false}, {id: '3', isSelected: false}, {id: '4', isSelected: false}]; let columns = [{name: 'Id', key: 'id'}]; let rowGetter = function(i) { return self.rows[i]; }; this.component = this.createComponent({ rowsCount: this.rows.length, rowGetter: rowGetter, columns: columns, rowSelection: {enableShiftSelect: true, selectBy: {isSelectedKey: 'isSelected'}, onRowsSelected: function(selectedRows) { self._selectedRows = selectedRows; }, onRowsDeselected: function(deselectedRows) { self._deselectedRows = deselectedRows; } }}).instance(); this.baseGrid = this.getBaseGrid(); this.selectRowCol = this.baseGrid.props.columnMetrics.columns[0]; }); it('should call rowSelection.onRowsSelected when row selected', function() { this.selectRowCol.onCellChange(1, '', this.rows[1], this.buildFakeEvent()); expect(this._selectedRows.length).toBe(1); expect(this._selectedRows[0].rowIdx).toBe(1); expect(this._selectedRows[0].row).toBe(this.rows[1]); }); it('should call rowSelection.onRowsDeselected when row de-selected', function() { this.selectRowCol.onCellChange(0, '', this.rows[0], this.buildFakeEvent()); expect(this._deselectedRows.length).toBe(1); expect(this._deselectedRows[0].rowIdx).toBe(0); expect(this._deselectedRows[0].row).toBe(this.rows[0]); }); it('should set lastRowIdxUiSelected state', function() { this.selectRowCol.onCellChange(1, '', this.rows[1], this.buildFakeEvent()); expect(this.component.state.lastRowIdxUiSelected).toEqual(1); }); it('should select range when shift selecting below selected row', function() { this.selectRowCol.onCellChange(1, '', this.rows[1], this.buildFakeEvent()); expect(this._selectedRows.length).toEqual(1); this.simulateGridKeyDownWithKeyCode(16); this.selectRowCol.onCellChange(3, '', this.rows[3], this.buildFakeEvent()); expect(this._selectedRows.length).toEqual(2); }); it('should select range when shift selecting above selected row', function() { this.selectRowCol.onCellChange(3, '', this.rows[3], this.buildFakeEvent()); expect(this._selectedRows.length).toEqual(1); this.simulateGridKeyDownWithKeyCode(16); this.selectRowCol.onCellChange(1, '', this.rows[1], this.buildFakeEvent()); expect(this._selectedRows.length).toEqual(2); }); describe('checking header checkbox', function() { beforeEach(function() { let self = this; this._selectedRows = []; this._deselectedRows = []; this.rows = [{id: '1'}, {id: '2'}]; let columns = [{name: 'Id', key: 'id'}]; let rowGetter = function(i) { return self.rows[i]; }; this.component = this.createComponent({ enableRowSelect: true, rowsCount: this.rows.length, rowGetter: rowGetter, columns: columns, rowSelection: {selectBy: {indexes: []}, onRowsSelected: function(selectedRows) { self._selectedRows = selectedRows; }, onRowsDeselected: function(deselectedRows) { self._deselectedRows = deselectedRows; } }}).instance(); this.baseGrid = this.getBaseGrid(); this.selectRowCol = this.baseGrid.props.columnMetrics.columns[0]; // header checkbox let checkboxWrapper = document.createElement('div'); checkboxWrapper.innerHTML = '<input type="checkbox" value="value" checked="true" />'; this.checkbox = checkboxWrapper.querySelector('input'); this.fakeEvent = this.buildFakeEvent({ currentTarget: this.checkbox }); const SelectAll = this.selectRowCol.headerRenderer; this.selectAllWrapper = mount(SelectAll); this.selectAllWrapper.props().onChange(this.fakeEvent); }); it('should call rowSelection.onRowsSelected with all rows', function() { expect(this._selectedRows.length).toBe(2); }); }); describe('un-checking header checkbox', function() { beforeEach(function() { let self = this; this._selectedRows = []; this._deselectedRows = []; this.rows = [{id: '1'}, {id: '2'}]; let columns = [{name: 'Id', key: 'id'}]; let rowGetter = function(i) { return self.rows[i]; }; this.component = this.createComponent({ enableRowSelect: true, rowsCount: this.rows.length, rowGetter: rowGetter, columns: columns, rowSelection: {selectBy: {indexes: [0, 1]}, onRowsSelected: function(selectedRows) { self._selectedRows = selectedRows; }, onRowsDeselected: function(deselectedRows) { self._deselectedRows = deselectedRows; } }}).instance(); this.baseGrid = this.getBaseGrid(); this.selectRowCol = this.baseGrid.props.columnMetrics.columns[0]; // header checkbox let checkboxWrapper = document.createElement('div'); checkboxWrapper.innerHTML = '<input type="checkbox" value="value" checked="true" />'; this.checkbox = checkboxWrapper.querySelector('input'); const SelectAll = this.selectRowCol.headerRenderer; this.selectAllWrapper = mount(SelectAll); }); it('then unchecking should call rowSelection.onRowsDeselected with all rows', function() { this.checkbox.checked = false; this.fakeEvent = this.buildFakeEvent({ currentTarget: this.checkbox }); this.selectAllWrapper.props().onChange(this.fakeEvent); expect(this._deselectedRows.length).toBe(2); }); }); }); describe('User Interaction', function() { describe('When selected cell is in top corner of grid', function() { beforeEach(function() { this.component.setState({ selected: { idx: 0, rowIdx: 0 } }); }); it('on ArrowUp keyboard event should not change selected index', function() { this.simulateGridKeyDown('ArrowUp'); expect(this.component.state.selected).toEqual({ idx: 0, rowIdx: 0 }); }); it('on ArrowLeft keyboard event should not change selected index', function() { this.simulateGridKeyDown('ArrowLeft'); expect(this.component.state.selected).toEqual({ idx: 0, rowIdx: 0 }); }); }); describe('When selected cell has adjacent cells on all sides', function() { beforeEach(function() { this.component.setState({ selected: { idx: 1, rowIdx: 1 } }); }); it('on ArrowRight keyboard event should increment selected cell index by 1', function() { this.simulateGridKeyDown('ArrowRight'); expect(this.component.state.selected).toEqual({ idx: 2, rowIdx: 1 }); }); it('on ArrowDown keyboard event should increment selected row index by 1', function() { this.simulateGridKeyDown('ArrowDown'); expect(this.component.state.selected).toEqual({ idx: 1, rowIdx: 2 }); }); it('on ArrowLeft keyboard event should decrement selected row index by 1', function() { this.simulateGridKeyDown('ArrowLeft'); expect(this.component.state.selected).toEqual({ idx: 0, rowIdx: 1 }); }); it('on ArrowUp keyboard event should decrement selected row index by 1', function() { this.simulateGridKeyDown('ArrowUp'); expect(this.component.state.selected).toEqual({ idx: 1, rowIdx: 0 }); }); }); describe('When column is editable', function() { beforeEach(function() { const editableColumn = Object.assign({ editable: true }, this.columns[1]); this.columns[1] = editableColumn; this.component = this.createComponent({ columns: this.columns }).instance(); }); describe('copy a cell value', function() { beforeEach(function() { const cCharacterKeyCode = 99; this.component.setState({ selected: { idx: 1, rowIdx: 1 } }); this.simulateGridKeyDown(cCharacterKeyCode, true); }); it('should store the value in grid state', function() { let expectedCellValue = this._rows[1].title; expect(this.component.state.textToCopy).toEqual(expectedCellValue); expect(this.component.state.copied).toEqual({ idx: 1, rowIdx: 1 }); }); }); describe('paste a cell value', function() { beforeEach(function() { let wrapper = this.createComponent(); const vCharacterKeyCode = 118; spyOn(this.testProps, 'onCellCopyPaste'); wrapper.setProps({ onCellCopyPaste: this.testProps.onCellCopyPaste }); this.component = wrapper.instance(); this.component.setState({ textToCopy: 'banana', selected: { idx: 1, rowIdx: 5 }, copied: { idx: 1, rowIdx: 1 } }); this.simulateGridKeyDown(vCharacterKeyCode, true); }); it('should call onCellCopyPaste of component with correct params', function() { expect(this.component.props.onCellCopyPaste).toHaveBeenCalled(); expect(this.component.props.onCellCopyPaste.calls.mostRecent().args[0]).toEqual({ cellKey: 'title', rowIdx: 5, value: 'banana', fromRow: 1, toRow: 5 }); }); }); describe('cell commit cancel', function() { beforeEach(function() { this.component.setState({ selected: { idx: 1, rowIdx: 1, active: true } }); this.getCellMetaData().onCommitCancel(); }); it('should set grid state inactive', function() { expect(this.component.state.selected).toEqual({ idx: 1, rowIdx: 1, active: false }); }); }); describe('pressing escape', function() { beforeEach(function() { this.component.setState({ selected: { idx: 1, rowIdx: 1, active: true } }); this.simulateGridKeyDown('Escape'); }); it('should set grid state inactive', function() { expect(this.component.state.selected).toEqual({ idx: 1, rowIdx: 1, active: false }); }); }); describe('pressing enter', function() { beforeEach(function() { this.component.setState({ selected: { idx: 1, rowIdx: 1, active: false } }); this.simulateGridKeyDown('Enter'); }); it('should set grid state active', function() { expect(this.component.state.selected).toEqual({ idx: 1, rowIdx: 1, active: true, initialKeyCode: 'Enter' }); }); }); describe('pressing delete', function() { beforeEach(function() { this.component.setState({ selected: { idx: 1, rowIdx: 1, active: false } }); this.simulateGridKeyDown('Delete'); }); it('should set grid state active', function() { expect(this.component.state.selected).toEqual({ idx: 1, rowIdx: 1, active: true, initialKeyCode: 'Delete' }); }); }); describe('pressing backspace', function() { beforeEach(function() { this.component.setState({ selected: { idx: 1, rowIdx: 1, active: false } }); this.simulateGridKeyDown('Backspace'); }); it('should set grid state active', function() { expect(this.component.state.selected).toEqual({ idx: 1, rowIdx: 1, active: true, initialKeyCode: 'Backspace' }); }); }); describe('typing a char', function() { beforeEach(function() { const fakeEvent = this.buildFakeEvent({ keyCode: 66, key: 'Unidentified' }); this.component.setState({ selected: { idx: 1, rowIdx: 1, active: false } }); this.getBaseGrid().props.onViewportKeydown(fakeEvent); }); it('should set grid state active and store the typed value', function() { expect(this.component.state.selected).toEqual({ idx: 1, rowIdx: 1, active: true, initialKeyCode: 66 }); }); }); }); describe('Drag events', function() { describe('dragging in grid', function() { beforeEach(function() { this.component.setState({ selected: { idx: 1, rowIdx: 2 } }); const event = { target: { className: 'drag-handle' }}; this.getBaseGrid().props.onViewportDragStart(event); }); it('should store drag rowIdx, idx and value of cell in state', function() { const thirdRowTitle = this._rows[2].title; expect(this.component.state.dragged).toEqual({ idx: 1, rowIdx: 2, value: thirdRowTitle }); }); }); describe('dragging over a row', function() { beforeEach(function() { this.component.setState({ selected: { idx: 1, rowIdx: 2 }, dragged: { idx: 1, rowIdx: 2, value: 'apple', overRowIdx: 6 } }); this.getCellMetaData().handleDragEnterRow(4); }); it('should store the current rowIdx in grid state', function() { expect(this.component.state.dragged).toEqual({ idx: 1, rowIdx: 2, value: 'apple', overRowIdx: 4 }); }); }); describe('finishing drag', function() { beforeEach(function() { let wrapper = this.createComponent(); spyOn(this.testProps, 'onCellsDragged'); wrapper.setProps({ onCellsDragged: this.testProps.onCellsDragged }); this.component = wrapper.instance(); this.component.setState({ selected: { idx: 1, rowIdx: 2 }, dragged: { idx: 1, rowIdx: 2, value: 'apple', overRowIdx: 6 } }); this.getBaseGrid().props.onViewportDragEnd(); }); it('should trigger onCellsDragged event and call it with correct params', function() { expect(this.component.props.onCellsDragged).toHaveBeenCalled(); expect(this.component.props.onCellsDragged.calls.argsFor(0)[0]).toEqual({ cellKey: 'title', fromRow: 2, toRow: 6, value: 'apple' }); }); }); describe('terminating drag', function() { beforeEach(function() { this.component.setState({ dragged: { idx: 1, rowIdx: 2, value: 'apple', overRowIdx: 6 } }); this.getCellMetaData().handleTerminateDrag(); }); it('should clear drag state', function() { expect(this.component.state.dragged).toBe(null); }); }); }); describe('Adding a new column', function() { beforeEach(function() { let wrapper = this.createComponent(); const newColumn = { key: 'isodd', name: 'Is Odd', width: 100 }; const newColumns = Object.assign([], this.columns); newColumns.splice(2, 0, newColumn); wrapper.setProps({ columns: newColumns }); this.component = wrapper.instance(); this.columns = this.component.state.columnMetrics.columns; }); it('should add column', function() { expect(this.columns.length).toEqual(5); }); it('should calculate column metrics for added column', function() { expect(this.columns[2]).toEqual(jasmine.objectContaining({ key: 'isodd', name: 'Is Odd', width: 100 })); }); }); describe('Remove a column', function() { beforeEach(function() { let wrapper = this.createComponent(); const newColumns = Object.assign([], this.columns); newColumns.splice(1, 1); wrapper.setProps({ columns: newColumns }); this.component = wrapper.instance(); this.columns = this.component.state.columnMetrics.columns; }); it('should remove column', function() { expect(this.columns.length).toEqual(3); }); it('should no longer include metrics for removed column', function() { expect(this.columns[0]).toEqual(jasmine.objectContaining({ key: 'id', name: 'ID', width: 100 })); expect(this.columns[1]).toEqual(jasmine.objectContaining({ key: 'count', name: 'Count', width: 100 })); }); }); describe('outside row/cell', function() { beforeEach(function() { this.component.setState({ selected: { idx: 1, rowIdx: 1 } }); }); it('should deselect currently selected cell on click', function() { this.getBaseGrid().props.onViewportClick(); expect(this.component.state.selected).toEqual(jasmine.objectContaining({ idx: -1, rowIdx: -1 })); }); it('should deselect currently selected cell on double-click', function() { this.getBaseGrid().props.onViewportDoubleClick(); expect(this.component.state.selected).toEqual(jasmine.objectContaining({ idx: -1, rowIdx: -1 })); }); }); }); describe('Cell Meta Data', function() { it('should create a cellMetaData object and pass to baseGrid as props', function() { let meta = this.getCellMetaData(); expect(meta).toEqual(jasmine.objectContaining({ selected: { rowIdx: 0, idx: 0 }, dragged: null, copied: null })); expect(meta.onCellClick).toEqual(jasmine.any(Function)); expect(meta.onCommit).toEqual(jasmine.any(Function)); expect(meta.onCommitCancel).toEqual(jasmine.any(Function)); expect(meta.handleDragEnterRow).toEqual(jasmine.any(Function)); expect(meta.handleTerminateDrag).toEqual(jasmine.any(Function)); }); describe('Changing Grid state', function() { beforeEach(function() { let newState = { selected: { idx: 2, rowIdx: 2 }, dragged: { idx: 2, rowIdx: 2 } }; this.component.setState(newState); }); it(' should update cellMetaData', function() { expect(this.getCellMetaData()).toEqual(jasmine.objectContaining({ selected: { idx: 2, rowIdx: 2 }, dragged: { idx: 2, rowIdx: 2 } })); }); }); describe('cell commit', function() { beforeEach(function() { let wrapper = this.createComponent(); spyOn(this.testProps, 'onRowUpdated'); wrapper.setProps({ onRowUpdated: this.testProps.onRowUpdated }); this.component = wrapper.instance(); this.component.setState({ selected: { idx: 3, rowIdx: 3, active: true } }); this.getCellMetaData().onCommit(this.buildFakeCellUodate()); }); it('should trigger onRowUpdated with correct params', function() { const onRowUpdated = this.component.props.onRowUpdated; expect(onRowUpdated.calls.mostRecent().args[0]).toEqual(this.buildFakeCellUodate()); }); it('should deactivate selected cell', function() { expect(this.component.state.selected).toEqual(jasmine.objectContaining({ idx: 3, rowIdx: 3, active: false })); }); }); describe('Cell click', function() { beforeEach(function() { this.getCellMetaData().onCellClick({ idx: 2, rowIdx: 2 }); }); it('should set selected state of grid', function() { expect(this.component.state.selected).toEqual({ idx: 2, rowIdx: 2 }); }); }); describe('Column events', function() { let columnWithEvent; const eventColumnIdx = 3; const eventColumnRowIdx = 2; const eventColumnRowId = 2; beforeEach(function() { columnWithEvent = this.component.state.columnMetrics.columns[3]; let events = this.testProps.columns[3].events; spyOn(events, 'onClick'); spyOn(events, 'onDoubleClick'); spyOn(events, 'onDragOver'); }); it('should call an event when there is one', function() { this.getCellMetaData().onColumnEvent({}, {idx: eventColumnIdx, rowIdx: eventColumnRowIdx, name: 'onClick'}); expect(columnWithEvent.events.onClick).toHaveBeenCalled(); }); it('should call the correct event', function() { this.getCellMetaData().onColumnEvent({}, {idx: eventColumnIdx, rowIdx: eventColumnRowIdx, name: 'onClick'}); expect(columnWithEvent.events.onClick).toHaveBeenCalled(); expect(columnWithEvent.events.onDoubleClick).not.toHaveBeenCalled(); }); it('should call double click event on double click', function() { this.getCellMetaData().onColumnEvent({}, {idx: eventColumnIdx, rowIdx: eventColumnRowIdx, name: 'onDoubleClick'}); expect(columnWithEvent.events.onDoubleClick).toHaveBeenCalled(); }); it('should call drag over event on drag over click', function() { this.getCellMetaData().onColumnEvent({}, {idx: eventColumnIdx, rowIdx: eventColumnRowIdx, name: 'onDragOver'}); expect(columnWithEvent.events.onDragOver).toHaveBeenCalled(); }); it('should call the event when there is one with the correct args', function() { this.getCellMetaData().onColumnEvent({}, {idx: eventColumnIdx, rowIdx: eventColumnRowIdx, rowId: eventColumnRowId, name: 'onClick'}); expect(columnWithEvent.events.onClick.calls.mostRecent().args).toEqual([{}, {column: columnWithEvent, idx: eventColumnIdx, rowIdx: eventColumnRowIdx, rowId: eventColumnRowId }]); }); it('events should work for the first column', function() { const firstColumnIdx = 0; let firstColumn = this.component.state.columnMetrics.columns[firstColumnIdx]; let firstColumnEvents = this.testProps.columns[firstColumnIdx].events; spyOn(firstColumnEvents, 'onClick'); this.getCellMetaData().onColumnEvent({}, {idx: firstColumnIdx, rowIdx: eventColumnRowIdx, rowId: eventColumnRowId, name: 'onClick'}); expect(firstColumn.events.onClick).toHaveBeenCalled(); }); }); }); describe('changes to non metric column data', function() { beforeEach(function() { let wrapper = this.createComponent(); this.originalMetrics = Object.assign({}, this.component.state.columnMetrics); const editableColumn = Object.assign({ editable: true }, this.columns[0]); this.columns[0] = editableColumn; wrapper.setProps({ columns: this.columns }); this.component = wrapper.instance(); }); it('should keep original metric information', function() { let columnMetrics = this.component.state.columnMetrics; expect(columnMetrics.columns.length).toBeGreaterThan(1); columnMetrics.columns.forEach((column, index) => { expect(column.width).toEqual(this.originalMetrics.columns[index].width); expect(column.left).toEqual(this.originalMetrics.columns[index].left); }); }); }); describe('Table width', function() { let wrapper; beforeEach(function() { wrapper = this.createComponent(); this.component = wrapper.instance(); this.tableElement = ReactDOM.findDOMNode(this.component); }); it('should generate the width based on the container size', function() { expect(this.tableElement.style.width).toEqual('100%'); }); describe('providing table width as prop', function() { beforeEach(function() { wrapper.setProps({ minWidth: 900 }); this.component = wrapper.instance(); }); it('should set the width of the table', function() { expect(this.tableElement.style.width).toEqual('900px'); }); }); }); describe('onRowClick handler', function() { beforeEach(function() { let self = this; this.rows = [{id: '1', isSelected: true}, {id: '2', isSelected: false}]; let columns = [{name: 'Id', key: 'id'}, {name: 'Title', key: 'title', width: 100 }]; let rowGetter = function(i) { return self.rows[i]; }; this.rowClicked = {}; this.rowClicks = 0; this.component = this.createComponent({rowsCount: this.rows.length, rowGetter: rowGetter, columns: columns, onRowClick: function(rowIdx, row, column) { self.rowClicked = {row, column}; self.rowClicks++; }}).instance(); }); it('calls handler when row (cell) clicked', function() { this.getCellMetaData().onCellClick({ idx: 1, rowIdx: 1}); expect(this.rowClicks).toBe(1); const { row, column } = this.rowClicked; expect(row).toEqual(jasmine.objectContaining(this.rows[1])); expect(column).toEqual(jasmine.objectContaining(this.columns[1])); }); }); });