diff --git a/integration/specs/Table/table-5.spec.js b/integration/specs/Table/table-5.spec.js index 771426d44..c8e5c7d77 100644 --- a/integration/specs/Table/table-5.spec.js +++ b/integration/specs/Table/table-5.spec.js @@ -14,6 +14,7 @@ describe('Table with selection', () => { }); it('should select and then deselect a row', () => { const table = new PageTable(TABLE); + table.waitUntilDataIsLoaded(); const row5 = table.getRow(4); expect(row5.isRowSelected()).toBe(false); row5.selectRow(); @@ -23,6 +24,7 @@ describe('Table with selection', () => { }); it('should select all rows', () => { const table = new PageTable(TABLE); + table.waitUntilDataIsLoaded(); const row1 = table.getRow(0); const row2 = table.getRow(1); const row3 = table.getRow(2); @@ -48,6 +50,7 @@ describe('Table with selection', () => { }); it('should deselect all rows', () => { const table = new PageTable(TABLE); + table.waitUntilDataIsLoaded(); const row1 = table.getRow(0); const row2 = table.getRow(1); const row3 = table.getRow(2); @@ -67,6 +70,7 @@ describe('Table with selection', () => { }); it('should select the second and fifth row', () => { const table = new PageTable(TABLE); + table.waitUntilDataIsLoaded(); const row1 = table.getRow(0); const row2 = table.getRow(1); const row3 = table.getRow(2); @@ -86,6 +90,7 @@ describe('Table with selection', () => { }); it('should deselect all rows when the second and fifth row are selected and then deselect all rows', () => { const table = new PageTable(TABLE); + table.waitUntilDataIsLoaded(); const row1 = table.getRow(0); const row2 = table.getRow(1); const row3 = table.getRow(2); @@ -106,6 +111,7 @@ describe('Table with selection', () => { }); it('should select all rows between selected rows while press shift key', () => { const table = new PageTable(TABLE); + table.waitUntilDataIsLoaded(); const row1 = table.getRow(0); const row2 = table.getRow(1); const row3 = table.getRow(2); @@ -127,6 +133,7 @@ describe('Table with selection', () => { }); it('should deselect all rows between the two rows deselected while press shift key', () => { const table = new PageTable(TABLE); + table.waitUntilDataIsLoaded(); const row1 = table.getRow(0); const row2 = table.getRow(1); const row3 = table.getRow(2); diff --git a/src/components/Modal/pageObject/index.js b/src/components/Modal/pageObject/index.js index 6f04a7f15..22e038a67 100644 --- a/src/components/Modal/pageObject/index.js +++ b/src/components/Modal/pageObject/index.js @@ -50,7 +50,7 @@ class PageModal { } /** - * Waiting until the open modal transition has finished. + * Wait until the open modal transition has finished. * @method */ waitUntilOpen() { @@ -58,7 +58,7 @@ class PageModal { } /** - * Waiting until the close modal transition has finished. + * Wait until the close modal transition has finished. * @method */ waitUntilClose() { diff --git a/src/components/Table/__test__/table.spec.js b/src/components/Table/__test__/table.spec.js index 14d8f2727..90104a01a 100644 --- a/src/components/Table/__test__/table.spec.js +++ b/src/components/Table/__test__/table.spec.js @@ -853,4 +853,66 @@ describe('', () => { }]); expect(component.instance().lastSelectedRowKey).toBe('1234qwerty'); }); + it('should set the right indexes when data prop changes', () => { + const component = mount( +
+ + +
, + ); + component.setProps({ + data: tableData, + }); + expect(component.instance().indexes).toEqual({ + '1234qwerty': { rowIndex: 0 }, + '5678asdfgh': { rowIndex: 1 }, + '9012zxcvbn': { rowIndex: 2 }, + }); + }); + it('should set the right state when data prop changes', () => { + const component = mount( + + + +
, + ); + component.setProps({ + data: tableData, + }); + const { state } = component.instance(); + expect(state.rows).toEqual([ + { inputType: 'checkbox', isDisabled: false, isSelected: false, key: '1234qwerty' }, + { inputType: 'checkbox', isDisabled: false, isSelected: false, key: '5678asdfgh' }, + { inputType: 'checkbox', isDisabled: false, isSelected: false, key: '9012zxcvbn' }, + ]); + expect(state.bulkSelection).toBe('none'); + }); + it('should set the right state when data prop changes and have selected rows', () => { + const component = mount( + + + +
, + ); + component.setProps({ + data: tableData, + }); + const { state } = component.instance(); + expect(state.rows).toEqual([ + { inputType: 'checkbox', isDisabled: false, isSelected: false, key: '1234qwerty' }, + { inputType: 'checkbox', isDisabled: false, isSelected: true, key: '5678asdfgh' }, + { inputType: 'checkbox', isDisabled: false, isSelected: false, key: '9012zxcvbn' }, + ]); + expect(state.bulkSelection).toBe('some'); + }); }); diff --git a/src/components/Table/body/__test__/body.spec.js b/src/components/Table/body/__test__/body.spec.js index 01e8d0e14..0e61bb9fd 100644 --- a/src/components/Table/body/__test__/body.spec.js +++ b/src/components/Table/body/__test__/body.spec.js @@ -33,14 +33,22 @@ const rows = [ ]; describe('', () => { - it('should return an empty component if data and columns are not passed and is not loading', () => { + it('should return an empty component when data and columns are not passed and is not loading', () => { const component = mount(); expect(component.find('Empty').exists()).toBe(true); }); - it('should return an empty component if data or columns are not arrays and is not loading', () => { - const component = mount(); + it('should return an empty component when there is not data and is not loading', () => { + const component = mount(); expect(component.find('Empty').exists()).toBe(true); }); + it('should return a loading component when there is not data and is loading', () => { + const component = mount(); + expect(component.find('Loading').exists()).toBe(true); + }); + it('should return one row more when there is data and is loading', () => { + const component = mount(); + expect(component.children().length).toBe(3); + }); it('should return an array of Row components', () => { const component = mount(); const rowElements = component.find('Row'); diff --git a/src/components/Table/body/__test__/loadingCells.spec.js b/src/components/Table/body/__test__/loadingCells.spec.js new file mode 100644 index 000000000..cdb55f541 --- /dev/null +++ b/src/components/Table/body/__test__/loadingCells.spec.js @@ -0,0 +1,18 @@ +import React from 'react'; +import { mount } from 'enzyme'; +import LoadingCells from '../loadingCells'; + +describe('', () => { + it('should not render anything when value is 0', () => { + const component = mount( + , + ); + expect(component.children().length).toBe(0); + }); + it('should render the amount of children that match with the value passed', () => { + const component = mount( + , + ); + expect(component.children().length).toBe(3); + }); +}); diff --git a/src/components/Table/body/__test__/row.spec.js b/src/components/Table/body/__test__/row.spec.js index fc142813b..030775bdf 100644 --- a/src/components/Table/body/__test__/row.spec.js +++ b/src/components/Table/body/__test__/row.spec.js @@ -92,4 +92,11 @@ describe('', () => { expect(cell.at(1).prop('isFirst')).toBe(true); expect(cell.at(2).prop('isFirst')).toBe(false); }); + it('should render LoadingCells component when data type is LOADING', () => { + const rowData = { type: 'LOADING' }; + const component = mount( + , + ); + expect(component.find('LoadingCells').exists()).toBe(true); + }); }); diff --git a/src/components/Table/body/index.js b/src/components/Table/body/index.js index 2ab15811e..078acc97e 100644 --- a/src/components/Table/body/index.js +++ b/src/components/Table/body/index.js @@ -32,7 +32,6 @@ export default class Body extends PureComponent { emptyTitle, emptyDescription, } = this.props; - const hasData = Array.isArray(data) && data.length > 0; const isEmpty = data.length === 0; const columnsLength = columns.length; @@ -50,34 +49,31 @@ export default class Body extends PureComponent { ); } - if (hasData && Array.isArray(columns)) { - const newData = getData(data, isLoading); - return newData.map((item, index) => { - const row = rows[index]; - const rowKeyValue = rows[index] && rows[index].key; - const key = rowKeyValue || `row-${index + 1}`; + const newData = getData(data, isLoading); + return newData.map((item, index) => { + const row = rows[index]; + const rowKeyValue = rows[index] && rows[index].key; + const key = rowKeyValue || `row-${index + 1}`; - return ( - onSelectRow( + return ( + onSelectRow( event, isMultiple, rowKeyValue, )} - onDeselectRow={(event, isMultiple) => onDeselectRow( + onDeselectRow={(event, isMultiple) => onDeselectRow( event, isMultiple, rowKeyValue, )} /> - ); - }); - } - return null; + ); + }); } } diff --git a/src/components/Table/body/loadingCells.js b/src/components/Table/body/loadingCells.js index ace59c131..716751083 100644 --- a/src/components/Table/body/loadingCells.js +++ b/src/components/Table/body/loadingCells.js @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; const widths = ['40%', '60%', '70%', '85%', '95%']; @@ -7,18 +8,30 @@ function getRandomWidth() { } export default function LoadingCells({ value }) { - return Array(value).fill().map((item, index) => { - const key = `loading-cell-${index}`; - const styles = { - width: getRandomWidth(), - }; + if (value > 0) { + return Array(value).fill().map((item, index) => { + const key = `loading-cell-${index}`; + const styles = { + width: getRandomWidth(), + }; - return ( - -
-
-
- - ); - }); + return ( + +
+
+
+ + ); + }); + } + return null; } + +LoadingCells.propTypes = { + value: PropTypes.number, +}; + +LoadingCells.defaultProps = { + value: 0, +}; + diff --git a/src/components/Table/helpers/columns/__test__/getColumns.spec.js b/src/components/Table/helpers/columns/__test__/getColumns.spec.js index 95304f775..db390954e 100644 --- a/src/components/Table/helpers/columns/__test__/getColumns.spec.js +++ b/src/components/Table/helpers/columns/__test__/getColumns.spec.js @@ -8,6 +8,11 @@ describe('getColumns', () => { const showCheckboxColumn = false; expect(getColumns(columns, showCheckboxColumn)).toBeNull(); }); + it('should return an empty array when columns is not passed', () => { + const columns = undefined; + const showCheckboxColumn = false; + expect(getColumns(columns, showCheckboxColumn)).toEqual([]); + }); it('should return an empty array when columns is an array with falsy values and showCheckboxColumn is false', () => { const columns = [null, undefined]; const showCheckboxColumn = false; diff --git a/src/components/Table/helpers/columns/getColumns.js b/src/components/Table/helpers/columns/getColumns.js index e6e02a358..f023b8ee0 100644 --- a/src/components/Table/helpers/columns/getColumns.js +++ b/src/components/Table/helpers/columns/getColumns.js @@ -1,7 +1,7 @@ import React from 'react'; import { SELECTABLE_CHECKBOX } from './'; -export default function getColumns(columns, showCheckboxColumn) { +export default function getColumns(columns = [], showCheckboxColumn) { const columnsData = React.Children.map(columns, (column) => { if (column) { return column.props; diff --git a/src/components/Table/helpers/data/__test__/normalizeData.spec.js b/src/components/Table/helpers/data/__test__/normalizeData.spec.js new file mode 100644 index 000000000..ad08b9975 --- /dev/null +++ b/src/components/Table/helpers/data/__test__/normalizeData.spec.js @@ -0,0 +1,14 @@ +import { normalizeData } from '../'; + +describe('normalizeData', () => { + it('should return the same data passed when it is an empty array', () => { + expect(normalizeData([])).toEqual([]); + }); + it('should return the same data passed when it is an array', () => { + expect(normalizeData(['data-1', 'data-2'])).toEqual(['data-1', 'data-2']); + }); + it('should return an empty array when data passed is not an array', () => { + const values = [null, undefined, {}, 'asd', 123, () => {}]; + values.forEach(value => expect(normalizeData(value)).toEqual([])); + }); +}); diff --git a/src/components/Table/helpers/data/index.js b/src/components/Table/helpers/data/index.js new file mode 100644 index 000000000..aa6be6fea --- /dev/null +++ b/src/components/Table/helpers/data/index.js @@ -0,0 +1,8 @@ +/* eslint-disable import/prefer-default-export */ + +export function normalizeData(data) { + if (Array.isArray(data)) { + return data; + } + return []; +} diff --git a/src/components/Table/index.js b/src/components/Table/index.js index 38c467018..f1819a994 100644 --- a/src/components/Table/index.js +++ b/src/components/Table/index.js @@ -16,6 +16,7 @@ import { getRowsWithInitalSelectedRows, isValidMaxRowSelection, } from './helpers/selector'; +import { normalizeData } from './helpers/data'; import ResizeSensor from '../../libs/ResizeSensor'; import debounce from '../../libs/debounce'; import { uniqueId } from '../../libs/utils'; @@ -43,8 +44,8 @@ export default class Table extends Component { tableWidth: undefined, rows: getRows({ keyField, - rows: data, - maxRowSelection: Number(maxRowSelection), + rows: normalizeData(data), + maxRowSelection: maxRowSelection && Number(maxRowSelection), selectedRowsKeys: {}, }), bulkSelection: 'none', @@ -80,12 +81,15 @@ export default class Table extends Component { showCheckboxColumn: prevShowCheckboxColumn, maxRowSelection: prevMaxRowSelection, selectedRows: prevSelectedRows, + data: prevData, } = prevProps; const { children, showCheckboxColumn, maxRowSelection, selectedRows, + data, + keyField, } = this.props; const prevColumns = getColumns(prevChildren, prevShowCheckboxColumn); const currentColumns = getColumns(children, showCheckboxColumn); @@ -95,6 +99,16 @@ export default class Table extends Component { if (prevMaxRowSelection !== maxRowSelection || prevSelectedRows !== selectedRows) { this.updateRows(); } + if (data !== prevData) { + const rows = getRows({ + keyField, + rows: normalizeData(data), + maxRowSelection: this.getMaxRowSelection(), + selectedRowsKeys: {}, + }); + this.indexes = getIndexes(rows); + this.updateRows(rows); + } } componentWillUnmount() { @@ -116,13 +130,12 @@ export default class Table extends Component { getSelectedRows(rows) { const { data } = this.props; - return data.filter((item, index) => rows[index].isSelected); + return normalizeData(data).filter((item, index) => rows[index].isSelected); } getMaxRowSelection() { - const { maxRowSelection } = this.props; - const { rows } = this.state; - const rowsLength = rows.length; + const { maxRowSelection, data } = this.props; + const rowsLength = normalizeData(data).length; const maxRowSelectionNumber = Number(maxRowSelection); if (!isValidMaxRowSelection(maxRowSelection, rowsLength)) { @@ -131,7 +144,7 @@ export default class Table extends Component { return maxRowSelectionNumber; } - updateRows() { + updateRows(updatedRows) { const { keyField, selectedRows, @@ -141,7 +154,7 @@ export default class Table extends Component { this.selectedRowsKeys = {}; const newRows = getRows({ keyField, - rows, + rows: updatedRows || rows, maxRowSelection, selectedRowsKeys: this.selectedRowsKeys, }); @@ -365,7 +378,7 @@ export default class Table extends Component { !$(this.rootElement).$('.rainbow-table_body--loading').isDisplayed() + && $(this.rootElement).$('.rainbow-table_body-row').isDisplayed()); + } } module.exports = PageTable; diff --git a/src/components/Table/readme.md b/src/components/Table/readme.md index ffae7c677..78576224e 100644 --- a/src/components/Table/readme.md +++ b/src/components/Table/readme.md @@ -209,6 +209,8 @@ faEllipsisV, } = require('@fortawesome/free-solid-svg-icons'); + const badgeStyles = { color: '#1de9b6' }; + const StatusBadge = ({ value }) => ; const data = [ { name: 'Leandro Torres', @@ -261,24 +263,44 @@ }, ]; - const badgeStyles = { color: '#1de9b6' }; + class TableExample extends React.Component { + constructor(props) { + super(props); + this.state = { + data: [], + isLoading: true, + }; + } - const StatusBadge = ({ value }) => ; + componentDidMount() { + setTimeout(() => this.setState({ + isLoading: false, + data, + }), 2000); + } -
- - - } /> - } /> - - - console.log(data)}> - - - - -
-
+ render() { + const { data, isLoading } = this.state; + return ( +
+ + + } /> + } /> + + + console.log(data)}> + + + + +
+
+ ); + } + } + + ##### Table with a limited number of Selectable Rows diff --git a/src/components/VerticalSectionOverflow/pageObject/index.js b/src/components/VerticalSectionOverflow/pageObject/index.js index 3f69c2001..002044398 100644 --- a/src/components/VerticalSectionOverflow/pageObject/index.js +++ b/src/components/VerticalSectionOverflow/pageObject/index.js @@ -30,7 +30,7 @@ class PageVerticalSectionOverflow { } /** - * Waiting until the expand transition has finished. + * Wait until the expand transition has finished. * @method */ waitUntilExpand() { @@ -38,7 +38,7 @@ class PageVerticalSectionOverflow { } /** - * Waiting until the contract transition has finished. + * Wait until the contract transition has finished. * @method */ waitUntilCollapse() {