@emsipl/react-data-grid-addons
Version:
A set of addons for react-data-grid
494 lines (418 loc) • 19.5 kB
JavaScript
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) => {
const fakeEvent = this.buildFakeEvent({
key: key,
keyCode: key,
ctrlKey: ctrlKey
});
this.getBaseGrid().props.onViewportKeydown(fakeEvent);
};
this.simulateGridKeyDownWithKeyCode = (keyCode) => {
const fakeEvent = this.buildFakeEvent({
keyCode: keyCode
});
this.getBaseGrid().props.onViewportKeydown(fakeEvent);
};
const 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() {
const 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() {
expect(this.component.state.canFilter).toBe(true);
expect(this.baseGrid.props.headerRows.length).toEqual(2);
});
});
});
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() {
const 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() {
const 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() {
const 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 }] });
const 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 }] });
const 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 }] });
const 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('When selection enabled and using rowSelection props', function() {
beforeEach(function() {
const self = this;
this._selectedRows = [];
this._deselectedRows = [];
this.rows = [{ id: '1', isSelected: true }, { id: '2', isSelected: false }, { id: '3', isSelected: false }, { id: '4', isSelected: false }];
const columns = [{ name: 'Id', key: 'id' }];
const 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() {
const self = this;
this._selectedRows = [];
this._deselectedRows = [];
this.rows = [{ id: '1' }, { id: '2' }];
const columns = [{ name: 'Id', key: 'id' }];
const 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
const 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() {
const self = this;
this._selectedRows = [];
this._deselectedRows = [];
this.rows = [{ id: '1' }, { id: '2' }];
const columns = [{ name: 'Id', key: 'id' }];
const 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
const 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('changes to non metric column data', function() {
beforeEach(function() {
const 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() {
const 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() {
const self = this;
this.rows = [{ id: '1', isSelected: true }, { id: '2', isSelected: false }];
const columns = [{ name: 'Id', key: 'id' }, { name: 'Title', key: 'title', width: 100 }];
const 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]));
});
});
});