From 9944bb357e4e84ddf5a89c6e5f005a6c8f2e591d Mon Sep 17 00:00:00 2001 From: Patrick Gillespie Date: Wed, 3 Jun 2020 20:11:38 -0400 Subject: [PATCH 1/2] added sortOrder option --- README.md | 5 +- examples/customize-columns/index.js | 5 +- examples/serverside-pagination/index.js | 150 +++++++++++++++++++----- src/MUIDataTable.js | 74 ++++++------ src/components/TableHead.js | 5 +- test/MUIDataTable.test.js | 42 ++++--- 6 files changed, 193 insertions(+), 88 deletions(-) diff --git a/README.md b/README.md index d60b8e6e5..da7cb2d23 100644 --- a/README.md +++ b/README.md @@ -219,6 +219,7 @@ The component accepts the following props: |**`onTableInit`**|function||Callback function that triggers when table state has been initialized. `function(action: string, tableState: object) => void` |**`setRowProps`**|function||Is called for each row and allows you to return custom props for this row based on its data. `function(row: array, dataIndex: number) => object` [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/customize-styling/index.js) |**`setTableProps`**|function||Is called for the table and allows you to return custom props for the table based on its data. `function() => object` [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/customize-styling/index.js) +|**`sortOrder`**|object|{}|Sets the column to sort by and its sort direction. To remove/reset sorting, input in an empty object. Options: `columnName: string, sortDirection: enum('asc', 'desc')` [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/customize-columns/index.js) ## Customize Columns @@ -259,11 +260,10 @@ const columns = [ |**`filterType `**|string|'dropdown'|Choice of filtering view. Takes priority over global filterType option.`enum('checkbox', 'dropdown', 'multiselect', 'textField', 'custom')` Use 'custom' if you are supplying your own rendering via `filterOptions`. |**`sort`**|boolean|true|Enable/disable sorting on column |**`searchable`**|boolean|true|Exclude/include column from search results -|**`sortDirection`**|string||Set default sort order `enum('asc', 'desc', 'none')` **(`null` option has been deprecated, use 'none' instead)** |**`print`**|boolean|true|Display column when printing |**`download`**|boolean|true|Display column in CSV download file |**`hint`**|string||Display hint icon with string as tooltip on hover. -|**`customHeadRender`**|function||Function that returns a string or React component. Used as display for column header. `function(columnMeta, handleToggleColumn) => string`|` React Component` +|**`customHeadRender`**|function||Function that returns a string or React component. Used as display for column header. `function(columnMeta, handleToggleColumn, sortOrder) => string`|` React Component` |**`customBodyRender`**|function||Function that returns a string or React component. Used as display data within all table cells of a given column. `function(value, tableMeta, updateValue) => string`|` React Component` [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/component/index.js) |**`setCellProps`**|function||Is called for each cell and allows to you return custom props for this cell based on its data. `function(cellValue: string, rowIndex: number, columnIndex: number) => object` [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/customize-styling/index.js) |**`setCellHeaderProps`**|function||Is called for each header cell and allows you to return custom props for the header cell based on its data. `function(columnMeta: object) => object` [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/customize-styling/index.js) @@ -276,7 +276,6 @@ function(columnMeta: { display: enum('true', 'false', 'excluded'), filter: boolean, sort: boolean, - sortDirection: boolean, download: boolean, empty: boolean, index: number, diff --git a/examples/customize-columns/index.js b/examples/customize-columns/index.js index 157fd2b50..217f5060f 100644 --- a/examples/customize-columns/index.js +++ b/examples/customize-columns/index.js @@ -19,7 +19,6 @@ class Example extends React.Component { name: "Title", options: { filter: true, - sortDirection: 'asc' } }, { @@ -86,6 +85,10 @@ class Example extends React.Component { filter: true, filterType: 'dropdown', responsive: 'stacked', + sortOrder: { + columnName: 'Title', + sortDirection: 'asc' + } }; return ( diff --git a/examples/serverside-pagination/index.js b/examples/serverside-pagination/index.js index 730fc88e9..c06353b8e 100644 --- a/examples/serverside-pagination/index.js +++ b/examples/serverside-pagination/index.js @@ -8,57 +8,140 @@ class Example extends React.Component { state = { page: 0, count: 1, + rowsPerPage: 5, + sortOrder: {}, data: [["Loading Data..."]], + columns: [ + { + name: "fullName", + label: "Full Name", + options: { + customBodyRender: (value, tableMeta, updateValue) => { + + // Here you can render a more complex display. + // You're given access to tableMeta, which has + // the rowData (as well as the original object data). + // See the console for a detailed look at this object. + + console.log('customBodyRender'); + console.dir(tableMeta); + return value; + } + }, + }, + { + name: "title", + label: "Title", + options: {}, + }, + { + name: "location", + label: "Location", + options: {}, + }, + ], isLoading: false }; componentDidMount() { - this.getData(); + this.getData("", 0); } // get data - getData = () => { + getData = (url, page) => { this.setState({ isLoading: true }); - this.xhrRequest().then(res => { + this.xhrRequest(url, page).then(res => { this.setState({ data: res.data, isLoading: false, count: res.total }); }); } + getSrcData = () => { + return [ + {fullName: "Gabby George", title: "Business Analyst", location: "Minneapolis"}, + {fullName: "Aiden Lloyd", title: "Business Consultant", location: "Dallas"}, + {fullName: "Jaden Collins", title: "Attorney", location: "Santa Ana"}, + {fullName: "Franky Rees", title: "Business Analyst", location: "St. Petersburg"}, + {fullName: "Aaren Rose", title: "Business Analyst", location: "Toledo"}, + + {fullName: "John George", title: "Business Analyst", location: "Washington DC"}, + {fullName: "Pat Lloyd", title: "Computer Programmer", location: "Baltimore"}, + {fullName: "Joe Joe Collins", title: "Attorney", location: "Las Cruces"}, + {fullName: "Franky Hershy", title: "Paper Boy", location: "El Paso"}, + {fullName: "Aaren Smalls", title: "Business Analyst", location: "Tokyo"}, + + {fullName: "Boogie G", title: "Police Officer", location: "Unknown"}, + {fullName: "James Roulf", title: "Business Consultant", location: "Video Game Land"}, + {fullName: "Mike Moocow", title: "Burger King Employee", location: "New York"}, + {fullName: "Mimi Gerock", title: "Business Analyst", location: "McCloud"}, + {fullName: "Jason Evans", title: "Business Analyst", location: "Mt Shasta"}, + + {fullName: "Simple Sam", title: "Business Analyst", location: "Mt Shasta"}, + {fullName: "Marky Mark", title: "Business Consultant", location: "Las Cruces"}, + {fullName: "Jaden Jam", title: "Attorney", location: "El Paso"}, + {fullName: "Holly Jo", title: "Business Analyst", location: "St. Petersburg"}, + {fullName: "Suzie Q", title: "Business Analyst", location: "New York"}, + ]; + } + + sort = (page, sortOrder) => { + + this.setState({ isLoading: true }); + this.xhrRequest("", page, sortOrder).then(res => { + this.setState({ + data: res.data, + page: res.page, + sortOrder, + isLoading: false, + count: res.total, + }); + }); + } + // mock async function - xhrRequest = () => { + xhrRequest = (url, page, sortOrder = {}) => { return new Promise((resolve, reject) => { - const total = 124; // mock record count from server // mock page data - const srcData = [ - ["Gabby George", "Business Analyst", "Minneapolis"], - ["Aiden Lloyd", "Business Consultant", "Dallas"], - ["Jaden Collins", "Attorney", "Santa Ana"], - ["Franky Rees", "Business Analyst", "St. Petersburg"], - ["Aaren Rose", "Business Analyst", "Toledo"] - ]; - const maxRound = Math.floor(Math.random() * 2) + 1; - const data = [...Array(maxRound)].reduce(acc => acc.push(...srcData) && acc, []); - data.sort((a, b) => 0.5 - Math.random()); + let fullData = this.getSrcData(); + const total = fullData.length; // mock record count from server - normally this would be a number attached to the return data + + let sortField = sortOrder.columnName; + let sortDir = sortOrder.sortDirection; + + if (sortField) { + fullData = fullData.sort((a, b) => { + if (a[sortField] < b[sortField]) { + return 1 * (sortDir === 'asc' ? -1 : 1); + } else if (a[sortField] > b[sortField]) { + return -1 * (sortDir === 'asc' ? -1 : 1); + } else { + return 0; + } + }); + } + + const srcData = fullData.slice(page * this.state.rowsPerPage, (page+1) * this.state.rowsPerPage); + let data = srcData; setTimeout(() => { resolve({ - data, total + data, total, page }); - }, 2500); + }, 500); }); } - changePage = (page) => { + changePage = (page, sortOrder) => { this.setState({ isLoading: true, }); - this.xhrRequest(`/myApiServer?page=${page}`).then(res => { + this.xhrRequest(`/myApiServer?page=${page}`, page, sortOrder).then(res => { this.setState({ isLoading: false, - page: page, + page: res.page, + sortOrder, data: res.data, count: res.total, }); @@ -67,40 +150,51 @@ class Example extends React.Component { render() { - const columns = ["Name", "Title", "Location"]; - const { data, page, count, isLoading } = this.state; + const { data, page, count, isLoading, rowsPerPage, sortOrder } = this.state; const options = { filter: true, filterType: 'dropdown', - responsive: 'stacked', + responsive: 'scrollMaxHeight', serverSide: true, count: count, - page: page, + rowsPerPage: rowsPerPage, + rowsPerPageOptions: [], + sortOrder: sortOrder, onTableChange: (action, tableState) => { console.log(action, tableState); + // a developer could react to change on an action basis or // examine the state as a whole and do whatever they want switch (action) { case 'changePage': - this.changePage(tableState.page); + this.changePage(tableState.page, tableState.sortOrder); break; + case 'sort': + this.sort(tableState.page, tableState.sortOrder); + break; + default: + console.log('action not handled.'); } } }; + + console.log('COLUMNS'); + console.dir( JSON.parse(JSON.stringify(this.state.columns)) ); + return (
- + ACME Employee list {isLoading && } - } data={data} columns={columns} options={options} /> + } data={data} columns={this.state.columns} options={options} />
); } } -export default Example; +export default Example; \ No newline at end of file diff --git a/src/MUIDataTable.js b/src/MUIDataTable.js index 01c5b121e..688441248 100644 --- a/src/MUIDataTable.js +++ b/src/MUIDataTable.js @@ -111,7 +111,6 @@ class MUIDataTable extends React.Component { download: PropTypes.bool, viewColumns: PropTypes.bool, filterList: PropTypes.array, - sortDirection: PropTypes.oneOf(['asc', 'desc', 'none']), filterOptions: PropTypes.oneOfType([ PropTypes.array, PropTypes.shape({ @@ -208,6 +207,7 @@ class MUIDataTable extends React.Component { setRowProps: PropTypes.func, setTableProps: PropTypes.func, sort: PropTypes.bool, + sortOrder: PropTypes.object, viewColumns: PropTypes.bool, }), /** Pass and use className to style MUIDataTable as desired */ @@ -255,6 +255,7 @@ class MUIDataTable extends React.Component { lookup: {}, }, showResponsive: false, + sortOrder: {}, }; constructor() { @@ -358,6 +359,7 @@ class MUIDataTable extends React.Component { setTableProps: () => ({}), sort: true, sortFilterList: true, + sortOrder: {}, textLabels: getTextLabels(), viewColumns: true, }); @@ -458,7 +460,6 @@ class MUIDataTable extends React.Component { let columnData = []; let filterData = []; let filterList = []; - let sortDirectionSet = false; newColumns.forEach((column, colIndex) => { let columnOptions = { @@ -470,7 +471,6 @@ class MUIDataTable extends React.Component { searchable: true, download: true, viewColumns: true, - sortDirection: 'none', }; const options = { ...column.options }; @@ -481,24 +481,14 @@ class MUIDataTable extends React.Component { options.display = options.display.toString(); } - if (options.sortDirection === null) { + if (options.sortDirection === null || options.sortDirection) { warnDeprecated( - 'The "null" option for sortDirection is deprecated. sortDirection is an enum, use "asc" | "desc" | "none"', + 'The sortDirection column field has been replaced by the sortOrder option on the options object.', ); - options.sortDirection = 'none'; - } - - if (options.sortDirection !== undefined && options.sortDirection !== 'none') { - if (sortDirectionSet) { - console.error('sortDirection is set for more than one column. Only the first column will be considered.'); - options.sortDirection = 'none'; - } else { - sortDirectionSet = true; - } } } - // remember stored version of display and sortDirection if not overwritten + // remember stored version of display if not overwritten if ( typeof options.display === 'undefined' && prevColumns[colIndex] && @@ -507,14 +497,6 @@ class MUIDataTable extends React.Component { ) { options.display = prevColumns[colIndex].display; } - if ( - typeof options.sortDirection === 'undefined' && - prevColumns[colIndex] && - prevColumns[colIndex].name === column.name && - prevColumns[colIndex].sortDirection - ) { - options.sortDirection = prevColumns[colIndex].sortDirection; - } columnOptions = { name: column.name, @@ -523,13 +505,10 @@ class MUIDataTable extends React.Component { ...options, }; } else { - // remember stored version of display and sortDirection if not overwritten + // remember stored version of display if not overwritten if (prevColumns[colIndex] && prevColumns[colIndex].display) { options.display = prevColumns[colIndex].display; } - if (prevColumns[colIndex] && prevColumns[colIndex].sortDirection) { - options.sortDirection = prevColumns[colIndex].sortDirection; - } columnOptions = { ...columnOptions, ...options, name: column, label: column }; } @@ -582,6 +561,13 @@ class MUIDataTable extends React.Component { let sortDirection = 'none'; let tableMeta; + let sortOrder; + if (this.options.sortOrder && this.options.sortOrder.sortDirection && this.options.sortOrder.columnName) { + sortOrder = Object.assign({}, this.options.sortOrder); + } else { + sortOrder = Object.assign({}, this.state.sortOrder); + } + const data = status === TABLE_LOAD.INITIAL ? this.transformData(columns, props.data) : props.data; let searchText = status === TABLE_LOAD.INITIAL ? this.options.searchText : null; @@ -649,9 +635,9 @@ class MUIDataTable extends React.Component { filterData[colIndex].sort(comparator); } - if (column.sortDirection !== 'none') { + if (column.name === sortOrder.columnName) { + sortDirection = sortOrder.sortDirection; sortIndex = colIndex; - sortDirection = column.sortDirection; } }); @@ -748,6 +734,7 @@ class MUIDataTable extends React.Component { expandedRows: expandedRowsData, count: this.options.count, data: tableData, + sortOrder: sortOrder, displayData: this.getDisplayData(columns, tableData, filterList, searchText, tableMeta), }, callback, @@ -949,23 +936,21 @@ class MUIDataTable extends React.Component { var cb = this.options.onViewColumnsChange || this.options.onColumnViewChange; if (cb) { - cb( - this.state.columns[index].name, - this.state.columns[index].display === 'true' ? 'add' : 'remove', - ); + cb(this.state.columns[index].name, this.state.columns[index].display === 'true' ? 'add' : 'remove'); } if (this.options.onColumnViewChange) { - warnDeprecated( - 'onColumnViewChange has been changed to onViewColumnsChange.', - ); + warnDeprecated('onColumnViewChange has been changed to onViewColumnsChange.'); } }, ); }; getSortDirectionLabel(column) { - return column.sortDirection === 'asc' ? 'ascending' : 'descending'; + if (column.name === this.state.sortOrder.columnName) { + return this.state.sortOrder.sortDirection === 'asc' ? 'ascending' : 'descending'; + } + return null; } getTableProps() { @@ -982,7 +967,14 @@ class MUIDataTable extends React.Component { prevState => { let columns = cloneDeep(prevState.columns); let data = prevState.data; - const newOrder = columns[index].sortDirection !== 'asc' ? 'asc' : 'desc'; + const newOrder = + columns[index].name === this.state.sortOrder.columnName && this.state.sortOrder.sortDirection !== 'desc' + ? 'desc' + : 'asc'; + const newSortOrder = { + columnName: columns[index].name, + sortDirection: newOrder, + }; for (let pos = 0; pos < columns.length; pos++) { if (index !== pos) { @@ -1007,6 +999,7 @@ class MUIDataTable extends React.Component { data: prevState.data, displayData: prevState.displayData, selectedRows: prevState.selectedRows, + sortOrder: newSortOrder, }; } else { const sortedData = this.sortTable(data, index, newOrder); @@ -1016,6 +1009,7 @@ class MUIDataTable extends React.Component { data: sortedData.data, displayData: this.getDisplayData(columns, sortedData.data, prevState.filterList, prevState.searchText), selectedRows: sortedData.selectedRows, + sortOrder: newSortOrder, previousSelectedRow: null, }; } @@ -1480,6 +1474,7 @@ class MUIDataTable extends React.Component { previousSelectedRow, expandedRows, searchText, + sortOrder, serverSideFilterList, } = this.state; @@ -1620,6 +1615,7 @@ class MUIDataTable extends React.Component { areAllRowsExpanded={this.areAllRowsExpanded} toggleAllExpandableRows={this.toggleAllExpandableRows} options={this.options} + sortOrder={sortOrder} components={this.props.components} /> column.display === 'true' && (column.customHeadRender ? ( - column.customHeadRender({ index, ...column }, this.handleToggleColumn) + column.customHeadRender({ index, ...column }, this.handleToggleColumn, sortOrder) ) : ( setCellRef(index + 1, findDOMNode(el))} sort={column.sort} - sortDirection={column.sortDirection} + sortDirection={column.name === sortOrder.columnName ? sortOrder.sortDirection : 'none'} toggleSort={this.handleToggleColumn} hint={column.hint} print={column.print} diff --git a/test/MUIDataTable.test.js b/test/MUIDataTable.test.js index 5145f7039..8724cc074 100644 --- a/test/MUIDataTable.test.js +++ b/test/MUIDataTable.test.js @@ -107,7 +107,6 @@ describe('', function() { label: 'Name', download: true, searchable: true, - sortDirection: 'none', viewColumns: true, customFilterListRender: renderCustomFilterList, // DEPRECATED customFilterListOptions: { render: renderCustomFilterList }, @@ -124,7 +123,6 @@ describe('', function() { download: true, searchable: true, viewColumns: true, - sortDirection: 'none', }, { display: 'true', @@ -138,7 +136,6 @@ describe('', function() { download: true, searchable: true, viewColumns: true, - sortDirection: 'none', customBodyRender: renderCities, }, { @@ -153,7 +150,6 @@ describe('', function() { download: true, searchable: true, viewColumns: true, - sortDirection: 'none', customBodyRender: renderState, customHeadRender: renderHead, }, @@ -169,7 +165,6 @@ describe('', function() { download: true, searchable: true, viewColumns: true, - sortDirection: 'none', }, ]; @@ -214,7 +209,6 @@ describe('', function() { label: 'Test Name', download: true, searchable: true, - sortDirection: 'none', viewColumns: true, customFilterListRender: renderCustomFilterList, // DEPRECATED customFilterListOptions: { render: renderCustomFilterList }, @@ -231,7 +225,6 @@ describe('', function() { download: true, searchable: true, viewColumns: true, - sortDirection: 'none', }, { display: 'true', @@ -245,7 +238,6 @@ describe('', function() { download: true, searchable: true, viewColumns: true, - sortDirection: 'none', customBodyRender: renderCities, }, { @@ -260,7 +252,6 @@ describe('', function() { download: true, searchable: true, viewColumns: true, - sortDirection: 'none', customBodyRender: renderState, customHeadRender: renderHead, }, @@ -276,7 +267,6 @@ describe('', function() { download: true, searchable: true, viewColumns: true, - sortDirection: 'none', }, ]; @@ -393,6 +383,33 @@ describe('', function() { assert.deepEqual(JSON.stringify(state.displayData), displayData); }); + it('should correctly build internal table data and displayData structure with sortOrder set', () => { + const columns = ['Name', 'Company', 'Location']; + + const data = [ + { Name: 'Joe James', Company: 'Test Corp', Location: 'Las Cruces' }, + { Name: 'John Walsh', Company: 'Test Corp', Location: 'El Paso' }, + { Name: 'Bob Herm', Company: 'Test Corp', Location: 'Albuquerque' }, + { Name: 'James Houston', Company: 'Test Corp', Location: 'Santa Fe' }, + ]; + const displayData = JSON.stringify([ + { data: ['Bob Herm', 'Test Corp', 'Albuquerque'], dataIndex: 2 }, + { data: ['John Walsh', 'Test Corp', 'El Paso'], dataIndex: 1 }, + { data: ['Joe James', 'Test Corp', 'Las Cruces'], dataIndex: 0 }, + { data: ['James Houston', 'Test Corp', 'Santa Fe'], dataIndex: 3 }, + ]); + const shallowWrapper = shallow( + , + ); + const state = shallowWrapper.dive().state(); + + assert.deepEqual(JSON.stringify(state.displayData), displayData); + }); + it('should correctly re-build display after xhr with serverSide=true', done => { const fullWrapper = mount(); assert.strictEqual(fullWrapper.find('tbody tr').length, 1); @@ -1104,7 +1121,6 @@ describe('', function() { label: 'Name', download: true, searchable: true, - sortDirection: 'none', customBodyRender: renderName, viewColumns: true, customFilterListRender: renderCustomFilterList, // DEPRECATED @@ -1121,7 +1137,6 @@ describe('', function() { download: true, searchable: true, viewColumns: true, - sortDirection: 'none', }, { name: 'City', @@ -1134,7 +1149,6 @@ describe('', function() { label: 'City Label', download: true, searchable: true, - sortDirection: 'none', customBodyRender: renderCities, viewColumns: true, }, @@ -1150,7 +1164,6 @@ describe('', function() { download: true, searchable: true, viewColumns: true, - sortDirection: 'none', customBodyRender: renderState, customHeadRender: renderHead, }, @@ -1166,7 +1179,6 @@ describe('', function() { download: true, searchable: true, viewColumns: true, - sortDirection: 'none', }, ]; From 2f087d279ff9d3eb92625d42c2ff5f4eaa22cfe6 Mon Sep 17 00:00:00 2001 From: Patrick Gillespie Date: Wed, 3 Jun 2020 21:42:35 -0400 Subject: [PATCH 2/2] refining sortOrder option --- README.md | 2 +- examples/customize-columns/index.js | 4 +-- examples/serverside-pagination/index.js | 4 +-- src/MUIDataTable.js | 43 ++++++++++++------------- src/components/TableHead.js | 2 +- src/components/TableHeadCell.js | 1 + test/MUIDataTable.test.js | 38 +++++++++++++++++++--- 7 files changed, 61 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index da7cb2d23..7f9ffe283 100644 --- a/README.md +++ b/README.md @@ -219,7 +219,7 @@ The component accepts the following props: |**`onTableInit`**|function||Callback function that triggers when table state has been initialized. `function(action: string, tableState: object) => void` |**`setRowProps`**|function||Is called for each row and allows you to return custom props for this row based on its data. `function(row: array, dataIndex: number) => object` [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/customize-styling/index.js) |**`setTableProps`**|function||Is called for the table and allows you to return custom props for the table based on its data. `function() => object` [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/customize-styling/index.js) -|**`sortOrder`**|object|{}|Sets the column to sort by and its sort direction. To remove/reset sorting, input in an empty object. Options: `columnName: string, sortDirection: enum('asc', 'desc')` [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/customize-columns/index.js) +|**`sortOrder`**|object|{}|Sets the column to sort by and its sort direction. To remove/reset sorting, input in an empty object. The object options are the column name and the direction: `name: string, direction: enum('asc', 'desc')` [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/customize-columns/index.js) ## Customize Columns diff --git a/examples/customize-columns/index.js b/examples/customize-columns/index.js index 217f5060f..6b9189499 100644 --- a/examples/customize-columns/index.js +++ b/examples/customize-columns/index.js @@ -86,8 +86,8 @@ class Example extends React.Component { filterType: 'dropdown', responsive: 'stacked', sortOrder: { - columnName: 'Title', - sortDirection: 'asc' + name: 'Title', + direction: 'asc' } }; diff --git a/examples/serverside-pagination/index.js b/examples/serverside-pagination/index.js index c06353b8e..64f46277a 100644 --- a/examples/serverside-pagination/index.js +++ b/examples/serverside-pagination/index.js @@ -105,8 +105,8 @@ class Example extends React.Component { let fullData = this.getSrcData(); const total = fullData.length; // mock record count from server - normally this would be a number attached to the return data - let sortField = sortOrder.columnName; - let sortDir = sortOrder.sortDirection; + let sortField = sortOrder.name; + let sortDir = sortOrder.direction; if (sortField) { fullData = fullData.sort((a, b) => { diff --git a/src/MUIDataTable.js b/src/MUIDataTable.js index 688441248..e3cb413e9 100644 --- a/src/MUIDataTable.js +++ b/src/MUIDataTable.js @@ -483,7 +483,7 @@ class MUIDataTable extends React.Component { if (options.sortDirection === null || options.sortDirection) { warnDeprecated( - 'The sortDirection column field has been replaced by the sortOrder option on the options object.', + 'The sortDirection column field has been deprecated. Please use the sortOrder option on the options object.', ); } } @@ -562,10 +562,20 @@ class MUIDataTable extends React.Component { let tableMeta; let sortOrder; - if (this.options.sortOrder && this.options.sortOrder.sortDirection && this.options.sortOrder.columnName) { + if (this.options.sortOrder && this.options.sortOrder.direction && this.options.sortOrder.name) { sortOrder = Object.assign({}, this.options.sortOrder); } else { sortOrder = Object.assign({}, this.state.sortOrder); + + // if no sortOrder, check and see if there's a sortDirection on one of the columns (deprecation notice for this is given above) + if (!sortOrder.direction) { + props.columns.forEach((column, colIndex) => { + if (column.options && (column.options.sortDirection === 'asc' || column.options.sortDirection === 'desc')) { + sortOrder.name = column.name; + sortOrder.sortDirection = column.sortDirection; + } + }); + } } const data = status === TABLE_LOAD.INITIAL ? this.transformData(columns, props.data) : props.data; @@ -635,8 +645,8 @@ class MUIDataTable extends React.Component { filterData[colIndex].sort(comparator); } - if (column.name === sortOrder.columnName) { - sortDirection = sortOrder.sortDirection; + if (column.name === sortOrder.name) { + sortDirection = sortOrder.direction; sortIndex = colIndex; } }); @@ -946,11 +956,8 @@ class MUIDataTable extends React.Component { ); }; - getSortDirectionLabel(column) { - if (column.name === this.state.sortOrder.columnName) { - return this.state.sortOrder.sortDirection === 'asc' ? 'ascending' : 'descending'; - } - return null; + getSortDirectionLabel(sortOrder) { + return sortOrder.direction === 'asc' ? 'ascending' : 'descending'; } getTableProps() { @@ -968,23 +975,15 @@ class MUIDataTable extends React.Component { let columns = cloneDeep(prevState.columns); let data = prevState.data; const newOrder = - columns[index].name === this.state.sortOrder.columnName && this.state.sortOrder.sortDirection !== 'desc' + columns[index].name === this.state.sortOrder.name && this.state.sortOrder.direction !== 'desc' ? 'desc' : 'asc'; const newSortOrder = { - columnName: columns[index].name, - sortDirection: newOrder, + name: columns[index].name, + direction: newOrder, }; - for (let pos = 0; pos < columns.length; pos++) { - if (index !== pos) { - columns[pos].sortDirection = 'none'; - } else { - columns[pos].sortDirection = newOrder; - } - } - - const orderLabel = this.getSortDirectionLabel(columns[index]); + const orderLabel = this.getSortDirectionLabel(newSortOrder); const announceText = `Table now sorted by ${columns[index].name} : ${orderLabel}`; let newState = { @@ -1019,7 +1018,7 @@ class MUIDataTable extends React.Component { () => { this.setTableAction('sort'); if (this.options.onColumnSortChange) { - this.options.onColumnSortChange(this.state.columns[index].name, this.state.columns[index].sortDirection); + this.options.onColumnSortChange(this.state.sortOrder.name, this.state.sortOrder.direction); } }, ); diff --git a/src/components/TableHead.js b/src/components/TableHead.js index 4d49093a8..dce67b295 100644 --- a/src/components/TableHead.js +++ b/src/components/TableHead.js @@ -104,7 +104,7 @@ class TableHead extends React.Component { type={'cell'} ref={el => setCellRef(index + 1, findDOMNode(el))} sort={column.sort} - sortDirection={column.name === sortOrder.columnName ? sortOrder.sortDirection : 'none'} + sortDirection={column.name === sortOrder.name ? sortOrder.direction : 'none'} toggleSort={this.handleToggleColumn} hint={column.hint} print={column.print} diff --git a/src/components/TableHeadCell.js b/src/components/TableHeadCell.js index 4eb0427c8..c80f2d5c4 100644 --- a/src/components/TableHeadCell.js +++ b/src/components/TableHeadCell.js @@ -125,6 +125,7 @@ class TableHeadCell extends React.Component { onKeyUp={this.handleKeyboardSortinput} onClick={this.handleSortClick} className={classes.toolButton} + data-testid={'headcol-' + this.props.index} tabIndex={0}> ', function() { { data: ['James Houston', 'Test Corp', 'Santa Fe'], dataIndex: 3 }, ]); const shallowWrapper = shallow( - , + , ); const state = shallowWrapper.dive().state(); @@ -481,6 +477,38 @@ describe('', function() { assert.deepEqual(props.options, newOptions); }); + it('should correctly pass the sorted column name and direction to onColumnSortChange', () => { + let sortedCol, sortedDir; + const options = { + rowsPerPage: 1, + rowsPerPageOptions: [1, 2, 4], + page: 1, + onColumnSortChange: (col, dir) => { + sortedCol = col; + sortedDir = dir; + }, + }; + const fullWrapper = mount(); + + // simulate sorting a column + fullWrapper + .find('[data-testid="headcol-1"]') + .at(0) + .simulate('click'); + + assert.strictEqual(sortedCol, 'Company'); + assert.strictEqual(sortedDir, 'asc'); + + // simulate toggling the sort + fullWrapper + .find('[data-testid="headcol-1"]') + .at(0) + .simulate('click'); + + assert.strictEqual(sortedCol, 'Company'); + assert.strictEqual(sortedDir, 'desc'); + }); + it('should correctly re-build internal table data while maintaining pagination after state change', () => { let currentPage; const options = {