From d3299c11ba5b3f034a5a77f683e307cab13729f1 Mon Sep 17 00:00:00 2001 From: Diego Alvarez Date: Tue, 16 Aug 2022 16:32:12 -0500 Subject: [PATCH 1/9] added multicolumn sort for normal table --- __tests__/demo/demo-components/index.js | 97 +++++++++++++++++++++ __tests__/demo/demo.js | 6 +- package-lock.json | 110 ++++++++---------------- package.json | 2 +- src/components/MTableHeader/index.js | 49 ++++++----- src/defaults/props.options.js | 2 + src/material-table.js | 46 +++++++++- src/prop-types.js | 9 ++ src/utils/data-manager.js | 97 ++++++++++++++++++++- 9 files changed, 320 insertions(+), 98 deletions(-) diff --git a/__tests__/demo/demo-components/index.js b/__tests__/demo/demo-components/index.js index f8ade78a..1bee8384 100644 --- a/__tests__/demo/demo-components/index.js +++ b/__tests__/demo/demo-components/index.js @@ -1253,3 +1253,100 @@ export function FixedColumnWithEdit() { /> ); } + +export function TableMultiSorting(props) { + const [data, setData] = useState(rawData); + // const [selection, setSelection] = useState([]); + + const global_cols = [ + { + title: 'number', + field: 'number', + minWidth: 140, + maxWidth: 400 /* , sorting: false */ + }, + { + title: 'title', + field: 'title', + minWidth: 140, + maxWidth: 400, + sorting: true + }, + { + title: 'name', + field: 'name', + minWidth: 140, + maxWidth: 400, + sorting: true + }, + { + title: 'lastName', + field: 'lastName', + minWidth: 140, + maxWidth: 400, + sorting: true + } + ]; + + const global_data1 = [ + { + number: '1', + title: 'Developer', + name: 'Mehmet', + lastName: 'Baran', + id: 1 + }, + { + number: '2', + title: 'Developer', + name: 'Pratik', + lastName: 'N', + id: 2 + }, + { + number: '2', + title: 'Human Resources', + name: 'Juan', + lastName: 'Lopez', + id: 3 + }, + { + number: '2', + title: 'Consultant', + name: 'Edgar', + lastName: 'Martinez', + id: 4 + } + ]; + + return ( + ( + + )} */ + options={{ + // selection: true + // sorting: true, + keepSortDirectionOnColumnSwitch: false, + maxColumnSort: 3 + }} + /> + ); + /* return ( + ( + // + // )} + options={{ + // selection: true + sorting: false + }} + /> + ); */ +} diff --git a/__tests__/demo/demo.js b/__tests__/demo/demo.js index 08b408c6..e8157461 100644 --- a/__tests__/demo/demo.js +++ b/__tests__/demo/demo.js @@ -45,7 +45,8 @@ import { TreeData, TableWithSummary, TableWithNumberOfPagesAround, - FixedColumnWithEdit + FixedColumnWithEdit, + TableMultiSorting } from './demo-components'; import { I1353, I1941, I122 } from './demo-components/RemoteData'; import { Table, TableCell, TableRow, Paper } from '@material-ui/core'; @@ -60,6 +61,9 @@ render(

DetailPanelRemounting

+

Multi Sorting

+ +

Switcher

diff --git a/package-lock.json b/package-lock.json index b9807a03..10d65cba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1224,9 +1224,9 @@ } }, "@discoveryjs/json-ext": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.6.tgz", - "integrity": "sha512-ws57AidsDvREKrZKYffXddNkyaF14iHNHm8VQnZH6t99E8gczjNN0GpvcGny0imC80yQ0tHz1xVUKk/KFQSUyA==", + "version": "0.5.7", + "resolved": "https://jdasoftware.jfrog.io/jdasoftware/api/npm/npm/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", "dev": true }, "@emotion/hash": { @@ -2913,15 +2913,15 @@ } }, "@webpack-cli/configtest": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.1.1.tgz", - "integrity": "sha512-1FBc1f9G4P/AxMqIgfZgeOTuRnwZMten8E7zap5zgpPInnCrP8D4Q81+4CWIch8i/Nf7nXjP0v6CjjbHOrXhKg==", + "version": "1.2.0", + "resolved": "https://jdasoftware.jfrog.io/jdasoftware/api/npm/npm/@webpack-cli/configtest/-/configtest-1.2.0.tgz", + "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", "dev": true }, "@webpack-cli/info": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.4.1.tgz", - "integrity": "sha512-PKVGmazEq3oAo46Q63tpMr4HipI3OPfP7LiNOEJg963RMgT0rqheag28NCML0o3GIzA3DmxP1ZIAv9oTX1CUIA==", + "version": "1.5.0", + "resolved": "https://jdasoftware.jfrog.io/jdasoftware/api/npm/npm/@webpack-cli/info/-/info-1.5.0.tgz", + "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", "dev": true, "requires": { "envinfo": "^7.7.3" @@ -4288,7 +4288,7 @@ }, "clone-deep": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "resolved": "https://jdasoftware.jfrog.io/jdasoftware/api/npm/npm/clone-deep/-/clone-deep-4.0.1.tgz", "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, "requires": { @@ -4340,9 +4340,9 @@ "dev": true }, "colorette": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", - "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "version": "2.0.19", + "resolved": "https://jdasoftware.jfrog.io/jdasoftware/api/npm/npm/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", "dev": true }, "combined-stream": { @@ -5095,7 +5095,7 @@ }, "envinfo": { "version": "7.8.1", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "resolved": "https://jdasoftware.jfrog.io/jdasoftware/api/npm/npm/envinfo/-/envinfo-7.8.1.tgz", "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", "dev": true }, @@ -6157,9 +6157,9 @@ "dev": true }, "fastest-levenshtein": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", - "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "version": "1.0.16", + "resolved": "https://jdasoftware.jfrog.io/jdasoftware/api/npm/npm/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", + "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", "dev": true }, "fastq": { @@ -6895,12 +6895,6 @@ } } }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true - }, "husky": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/husky/-/husky-1.2.0.tgz", @@ -7103,7 +7097,7 @@ }, "interpret": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "resolved": "https://jdasoftware.jfrog.io/jdasoftware/api/npm/npm/interpret/-/interpret-2.2.0.tgz", "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", "dev": true }, @@ -10451,7 +10445,7 @@ }, "rechoir": { "version": "0.7.1", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "resolved": "https://jdasoftware.jfrog.io/jdasoftware/api/npm/npm/rechoir/-/rechoir-0.7.1.tgz", "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", "dev": true, "requires": { @@ -11029,7 +11023,7 @@ }, "shallow-clone": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "resolved": "https://jdasoftware.jfrog.io/jdasoftware/api/npm/npm/shallow-clone/-/shallow-clone-3.0.1.tgz", "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, "requires": { @@ -12546,18 +12540,18 @@ } }, "webpack-cli": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.9.2.tgz", - "integrity": "sha512-m3/AACnBBzK/kMTcxWHcZFPrw/eQuY4Df1TxvIWfWM2x7mRqBQCqKEd96oCUa9jkapLBaFfRce33eGDb4Pr7YQ==", + "version": "4.10.0", + "resolved": "https://jdasoftware.jfrog.io/jdasoftware/api/npm/npm/webpack-cli/-/webpack-cli-4.10.0.tgz", + "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", "dev": true, "requires": { "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^1.1.1", - "@webpack-cli/info": "^1.4.1", - "@webpack-cli/serve": "^1.6.1", + "@webpack-cli/configtest": "^1.2.0", + "@webpack-cli/info": "^1.5.0", + "@webpack-cli/serve": "^1.7.0", "colorette": "^2.0.14", "commander": "^7.0.0", - "execa": "^5.0.0", + "cross-spawn": "^7.0.3", "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", "interpret": "^2.2.0", @@ -12565,49 +12559,17 @@ "webpack-merge": "^5.7.3" }, "dependencies": { + "@webpack-cli/serve": { + "version": "1.7.0", + "resolved": "https://jdasoftware.jfrog.io/jdasoftware/api/npm/npm/@webpack-cli/serve/-/serve-1.7.0.tgz", + "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "dev": true + }, "commander": { "version": "7.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "resolved": "https://jdasoftware.jfrog.io/jdasoftware/api/npm/npm/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", "dev": true - }, - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true - }, - "is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } } } }, @@ -12859,7 +12821,7 @@ }, "webpack-merge": { "version": "5.8.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "resolved": "https://jdasoftware.jfrog.io/jdasoftware/api/npm/npm/webpack-merge/-/webpack-merge-5.8.0.tgz", "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", "dev": true, "requires": { @@ -12943,7 +12905,7 @@ }, "wildcard": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "resolved": "https://jdasoftware.jfrog.io/jdasoftware/api/npm/npm/wildcard/-/wildcard-2.0.0.tgz", "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", "dev": true }, diff --git a/package.json b/package.json index 30022f26..19cb007a 100644 --- a/package.json +++ b/package.json @@ -102,7 +102,7 @@ "typescript": "^4.1.3", "webpack": "^5.11.0", "webpack-bundle-analyzer": "^4.3.0", - "webpack-cli": "^4.9.2", + "webpack-cli": "^4.10.0", "webpack-dev-server": "^3.11.0" }, "dependencies": { diff --git a/src/components/MTableHeader/index.js b/src/components/MTableHeader/index.js index 437db7fb..9627872b 100644 --- a/src/components/MTableHeader/index.js +++ b/src/components/MTableHeader/index.js @@ -25,6 +25,8 @@ export function MTableHeader({ onColumnResized, columns, ...props }) { [columns] ); + // console.log('multipleSort ===>', props.multipleSort) + const handleMouseDown = (e, columnDef, colIndex) => { const startX = e.clientX; const th = e.target.closest('th'); @@ -239,14 +241,14 @@ export function MTableHeader({ onColumnResized, columns, ...props }) { {columnDef.sorting !== false && options.sorting ? ( {columnDef.title} @@ -261,14 +263,14 @@ export function MTableHeader({ onColumnResized, columns, ...props }) { content = ( {columnDef.title} @@ -431,6 +433,8 @@ export function MTableHeader({ onColumnResized, columns, ...props }) { ); } +// TODO: Case when returning back to prev clicker column and keepSortDirectionOnColumnSwitch, should start form where user left +// Move this to a utils const computeNewOrderDirection = ( orderBy, orderDirection, @@ -460,48 +464,55 @@ const computeNewOrderDirection = ( function RenderSortButton({ columnDef, - orderBy, keepSortDirectionOnColumnSwitch, - orderDirection, icon, thirdSortClick, onOrderChange, - children + children, + columnIndex, + orderByCollection }) { - const active = orderBy === columnDef.tableData.id; + // console.log('orderByCollection', orderByCollection) + const activeColumn = orderByCollection.find( + ({ orderBy }) => orderBy === columnDef.tableData.id + ); + // If current sorted column or prop asked to // maintain sort order when switching sorted column, // follow computed order direction if defined // else default direction is asc const direction = - active || keepSortDirectionOnColumnSwitch ? orderDirection || 'asc' : 'asc'; - let ariaSort = 'none'; + activeColumn || keepSortDirectionOnColumnSwitch + ? (activeColumn && activeColumn.orderDirection) || 'asc' + : 'asc'; - if (active && direction === 'asc') { + let ariaSort = 'none'; + if (activeColumn && direction === 'asc') { ariaSort = columnDef.ariaSortAsc ? columnDef.ariaSortAsc : 'Ascendant'; - } - if (active && direction === 'desc') { + } else if (activeColumn && direction === 'desc') { ariaSort = columnDef.ariaSortDesc ? columnDef.ariaSortDesc : 'Descendant'; } + const orderBy = activeColumn && activeColumn.orderBy; + return ( { const newOrderDirection = computeNewOrderDirection( orderBy, - orderDirection, + direction, columnDef, thirdSortClick, keepSortDirectionOnColumnSwitch ); - onOrderChange(columnDef.tableData.id, newOrderDirection); + onOrderChange(columnDef.tableData.id, newOrderDirection, columnIndex); }} > {children} @@ -512,8 +523,7 @@ function RenderSortButton({ MTableHeader.defaultProps = { dataCount: 0, selectedCount: 0, - orderBy: undefined, - orderDirection: 'asc' + orderByCollection: [] }; MTableHeader.propTypes = { @@ -523,9 +533,8 @@ MTableHeader.propTypes = { selectedCount: PropTypes.number, onAllSelected: PropTypes.func, onOrderChange: PropTypes.func, - orderBy: PropTypes.number, - orderDirection: PropTypes.string, showActionsColumn: PropTypes.bool, + orderByCollection: PropTypes.array, tooltip: PropTypes.string }; diff --git a/src/defaults/props.options.js b/src/defaults/props.options.js index 15ea08e8..23f067cd 100644 --- a/src/defaults/props.options.js +++ b/src/defaults/props.options.js @@ -43,6 +43,8 @@ export default { selection: false, selectionProps: {}, sorting: true, + maxColumnSort: 1, + initialOrderByCollection: [], keepSortDirectionOnColumnSwitch: true, toolbar: true, defaultExpanded: false, diff --git a/src/material-table.js b/src/material-table.js index a3991ddd..675161c6 100644 --- a/src/material-table.js +++ b/src/material-table.js @@ -16,9 +16,21 @@ import { MTableScrollbar } from '@components'; +/* TODO: + Initi data based on maxColumnSort + do a initSort if predefaultsort + + On click add columns to be sorted + add an sort order id + on click sort the elements + + On arrow button show only the arrow on buttons that are on the column sort array +*/ + export default class MaterialTable extends React.Component { dataManager = new DataManager(); checkedForFunctions = false; + constructor(props) { super(props); @@ -111,6 +123,7 @@ export default class MaterialTable extends React.Component { this.dataManager.setTableWidth(props.options.tableWidth ?? 'full'); this.dataManager.setColumns(props.columns, prevColumns, savedColumns); this.dataManager.setDefaultExpanded(props.options.defaultExpanded); + this.dataManager.setMaxColumnSort(props.options.maxColumnSort); this.dataManager.changeRowEditing(); if (this.isRemoteData(props)) { @@ -165,6 +178,12 @@ export default class MaterialTable extends React.Component { defaultSortColumnIndex, defaultSortDirection ); + shouldReorder && + props.multipleSort && + this.dataManager.changeColumnOrder( + defaultSortColumnIndex, + defaultSortDirection + ); isInit && this.dataManager.changeSearchText(props.options.searchText || ''); isInit && this.dataManager.changeSearchDebounce(props.options.searchDebounceDelay); @@ -441,9 +460,16 @@ export default class MaterialTable extends React.Component { this.setState(this.dataManager.getRenderState()); }; - onChangeOrder = (orderBy, orderDirection) => { + onChangeOrder = (orderBy, orderDirection, columnIndex) => { const newOrderBy = orderDirection === '' ? -1 : orderBy; this.dataManager.changeOrder(newOrderBy, orderDirection); + /* this.props.multipleSort && */ this.dataManager.changeColumnOrder( + newOrderBy, + orderDirection, + columnIndex + ); + + // console.log('onChangeOrder ===>', orderBy, orderDirection) if (this.isRemoteData()) { const query = { ...this.state.query }; @@ -457,6 +483,7 @@ export default class MaterialTable extends React.Component { this.props.onOrderChange(newOrderBy, orderDirection); }); } else { + // console.log('orderByCollection ===>', this.dataManager.getRenderState().orderByCollection) this.setState(this.dataManager.getRenderState(), () => { this.props.onOrderChange && this.props.onOrderChange(newOrderBy, orderDirection); @@ -464,6 +491,20 @@ export default class MaterialTable extends React.Component { } }; + onChangeColumnOrder = (orderBy, orderDirection) => { + const newOrderBy = orderDirection === '' ? -1 : orderBy; + /* this.props.multipleSort && */ this.dataManager.changeColumnOrder( + newOrderBy, + orderDirection + ); + + // console.log('orderByCollection ===>', this.dataManager.getRenderState().orderByCollection) + this.setState(this.dataManager.getRenderState(), () => { + this.props.onOrderChange && + this.props.onOrderChange(newOrderBy, orderDirection); + }); + }; + onPageChange = (event, page) => { if (this.isRemoteData()) { const query = { ...this.state.query }; @@ -944,6 +985,7 @@ export default class MaterialTable extends React.Component { ); } } + renderTable = (props) => ( this.props.options.exportAll ? this.state.data : this.state.renderData; diff --git a/src/prop-types.js b/src/prop-types.js index 188e32c5..783437df 100644 --- a/src/prop-types.js +++ b/src/prop-types.js @@ -371,7 +371,16 @@ export const propTypes = { showSelectGroupCheckbox: PropTypes.bool, showTitle: PropTypes.bool, showTextRowsSelected: PropTypes.bool, + // TODO Remove Sorting sorting: PropTypes.bool, + initialOrderByCollection: PropTypes.arrayOf( + PropTypes.shape({ + orderBy: PropTypes.string, + oderDirection: PropTypes.string, + orderIndex: PropTypes.string + }) + ), + maxColumnSort: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), keepSortDirectionOnColumnSwitch: PropTypes.bool, toolbar: PropTypes.bool, thirdSortClick: PropTypes.bool, diff --git a/src/utils/data-manager.js b/src/utils/data-manager.js index 1d26eaa3..8ba3595a 100644 --- a/src/utils/data-manager.js +++ b/src/utils/data-manager.js @@ -12,6 +12,8 @@ export default class DataManager { detailPanelType = 'multiple'; lastDetailPanelRow = undefined; lastEditingRow = undefined; + maxColumnSort = 1; + orderByCollection = []; orderBy = -1; orderDirection = 'desc'; pageSize = 5; @@ -183,6 +185,10 @@ export default class DataManager { this.defaultExpanded = expanded; } + setMaxColumnSort(maxColumnSort) { + this.maxColumnSort = maxColumnSort; + } + changeApplySearch(applySearch) { this.applySearch = applySearch; this.searched = false; @@ -369,6 +375,7 @@ export default class DataManager { setCheck([currentGroup]); }; + // TODO: remove this function changeOrder(orderBy, orderDirection) { this.orderBy = orderBy; this.orderDirection = orderDirection; @@ -377,6 +384,26 @@ export default class DataManager { this.sorted = false; } + changeColumnOrder(orderBy, orderDirection, columnIndex) { + const prevColumns = this.orderByCollection.filter( + ({ orderBy }) => orderBy !== columnIndex + ); + + if (prevColumns.length == this.maxColumnSort) { + prevColumns.shift(); + } + + console.log('prevColumns', prevColumns, orderBy, columnIndex); + if (orderBy > -1) { + prevColumns.push({ orderBy, orderDirection, columnIndex }); + } + this.orderByCollection = [...prevColumns]; + + console.log('changeColumnOrder', this.orderByCollection); + this.currentPage = 0; + this.sorted = false; + } + changeGroupOrder(columnId) { const column = this.columns.find((c) => c.tableData.id === columnId); @@ -707,7 +734,72 @@ export default class DataManager { } } + sortBy(arr, columnsDefs, orderCollection) { + const sort = this.sort; + const getFieldValue = this.getFieldValue; + return arr.sort(function sortData( + a, + b, + columns = columnsDefs, + collection = orderCollection + ) { + const { orderBy, orderDirection } = collection[0]; + + const columnDef = columns.get(orderBy); + + let compareValue = 0; + if (columnDef.customSort) { + if (orderDirection === 'desc') { + compareValue = columnDef.customSort(b, a, 'row', orderDirection); + } else { + compareValue = columnDef.customSort(a, b, 'row', orderDirection); + } + } else { + // Calculate compare value and modify based on order + compareValue = sort( + getFieldValue(a, columnDef), + getFieldValue(b, columnDef), + columnDef.type + ); + + compareValue = + orderDirection.toLowerCase() === 'desc' + ? compareValue * -1 + : compareValue; + } + + // See if the next key needs to be considered + const checkNextKey = compareValue === 0 && collection.length !== 1; + + return checkNextKey + ? sortData(a, b, columns, collection.slice(1)) + : compareValue; + }); + } + sortList(list) { + let sortedList = [...list]; + // TODO: order the collection based on the order + console.log('sortList ===>', this.orderByCollection); + + const collectionIds = this.orderByCollection.map( + (collection) => collection.orderBy + ); + const columnsDefs = new Map(); + this.columns.forEach((column) => { + const columnId = column.tableData.id; + if (collectionIds.includes(columnId)) { + columnsDefs.set(columnId, column); + } + }); + + return (sortedList = this.sortBy( + sortedList, + columnsDefs, + this.orderByCollection + )); + console.log('sortedList ===>', sortedList); + let columnDef = this.columns.find((_) => _.tableData.id === this.orderBy); if (!columnDef) { columnDef = this.columns[0]; @@ -773,6 +865,7 @@ export default class DataManager { currentPage: this.currentPage, data: this.sortedData, lastEditingRow: this.lastEditingRow, + orderByCollection: this.orderByCollection, orderBy: this.orderBy, orderDirection: this.orderDirection, originalData: [...this.data], @@ -1125,6 +1218,7 @@ export default class DataManager { sortData() { this.paged = false; + console.log('getRenderState', this.sorted); if (this.isDataType('group')) { this.sortedData = [...this.groupedData]; @@ -1227,7 +1321,8 @@ export default class DataManager { } } else if (this.isDataType('normal')) { this.sortedData = [...this.searchedData]; - if (this.orderBy != -1 && this.applySort) { + // if (this.orderBy != -1 && this.applySort) { + if (this.orderByCollection.length && this.applySort) { this.sortedData = this.sortList(this.sortedData); } } From 2eeaae4e5b8bc7af4435a344d5e3c288fa978034 Mon Sep 17 00:00:00 2001 From: Diiegoav Date: Tue, 23 Aug 2022 15:24:24 -0500 Subject: [PATCH 2/9] added multicolumn sort for top data --- __tests__/demo/demo-components/index.js | 147 +++++++++++++++++---- src/components/MTableHeader/index.js | 2 - src/defaults/props.options.js | 2 +- src/material-table.js | 164 +++++++++++++----------- src/prop-types.js | 13 +- src/utils/data-manager.js | 99 ++++++-------- 6 files changed, 258 insertions(+), 169 deletions(-) diff --git a/__tests__/demo/demo-components/index.js b/__tests__/demo/demo-components/index.js index 1bee8384..398414e2 100644 --- a/__tests__/demo/demo-components/index.js +++ b/__tests__/demo/demo-components/index.js @@ -987,6 +987,20 @@ export function DefaultOrderIssue(props) { birthYear: 2017, birthCity: 34, id: 1 + }, + { + name: 'Mehmet', + surname: 'Terot', + birthYear: 1997, + birthCity: 63, + id: 3 + }, + { + name: 'Mehmet', + surname: 'Terot', + birthYear: 2000, + birthCity: 34, + id: 4 } ]} options={{ @@ -1255,70 +1269,155 @@ export function FixedColumnWithEdit() { } export function TableMultiSorting(props) { - const [data, setData] = useState(rawData); - // const [selection, setSelection] = useState([]); - - const global_cols = [ + /* const global_cols = [ { - title: 'number', + title: 'Number', field: 'number', minWidth: 140, - maxWidth: 400 /* , sorting: false */ + maxWidth: 400, + // type: 'numeric' + // , sorting: false }, { - title: 'title', + title: 'Title', field: 'title', minWidth: 140, maxWidth: 400, - sorting: true + sorting: true, + defaultSort: 'desc' }, { - title: 'name', + title: 'Name', field: 'name', minWidth: 140, maxWidth: 400, - sorting: true + sorting: true, + defaultSort: 'desc' }, { - title: 'lastName', + title: 'Last Name', field: 'lastName', minWidth: 140, maxWidth: 400, - sorting: true + sorting: true, + defaultSort: 'asc' + } + ]; */ + + const global_cols = [ + { title: 'Adl', field: 'name' }, + { title: 'Soyadl', field: 'surname' }, + { title: 'Cinsiyet', field: 'sex' }, + { title: 'Tipi', field: 'type', removable: false }, + { title: 'Doğum Yili', field: 'birthYear', type: 'numeric' }, + { + title: 'Doğum Yeri', + field: 'birthCity', + lookup: { 34: 'İstanbul', 63: 'Şanliurfa' } } ]; - const global_data1 = [ + /* const global_data1 = [ { - number: '1', + number: 1, title: 'Developer', name: 'Mehmet', lastName: 'Baran', - id: 1 + id: '1231' }, { - number: '2', + number: 22, title: 'Developer', name: 'Pratik', lastName: 'N', - id: 2 + id: '1234' }, { - number: '2', + number: 25, title: 'Human Resources', name: 'Juan', lastName: 'Lopez', - id: 3 + id: '1235' }, { - number: '2', + number: 3, title: 'Consultant', name: 'Edgar', lastName: 'Martinez', - id: 4 + id: '1236' } + ]; */ + + const global_data1 = [ + { + id: 1, + name: 'a', + surname: 'Baran', + birthYear: 1987, + birthCity: 63, + sex: 'Male', + type: 'adult' + }, + { + id: 2, + name: 'b', + surname: 'Baran', + birthYear: 1987, + birthCity: 34, + sex: 'Female', + type: 'adult', + parentId: 1 + }, + { + id: 3, + name: 'c', + surname: 'Baran', + birthYear: 1987, + birthCity: 34, + sex: 'Female', + type: 'child', + parentId: 1 + }, + { + id: 4, + name: 'd', + surname: 'Baran', + birthYear: 1987, + birthCity: 34, + sex: 'Female', + type: 'child', + parentId: 3 + }, + { + id: 5, + name: 'e', + surname: 'Baran', + birthYear: 1987, + birthCity: 34, + sex: 'Female', + type: 'child' + }, + { + id: 6, + name: 'f', + surname: 'Baran', + birthYear: 1987, + birthCity: 34, + sex: 'Female', + type: 'child', + parentId: 5 + } + ]; + + const orderCollection = [ + { orderBy: 1, orderDirection: 'asc', columnIndex: 1 }, + { orderBy: 2, orderDirection: 'desc', columnIndex: 2 } ]; + const onOrderCollectionChange = (orderByCollection) => { + console.log('onOrderCollectionChange ===>', orderByCollection); + }; + return ( ( )} */ + parentChildData={(row, rows) => rows.find((a) => a.id === row.parentId)} options={{ + selection: true, // selection: true // sorting: true, keepSortDirectionOnColumnSwitch: false, - maxColumnSort: 3 + maxColumnSort: 3, + defaultOrderByCollection: orderCollection }} + onOrderCollectionChange={onOrderCollectionChange} /> ); /* return ( diff --git a/src/components/MTableHeader/index.js b/src/components/MTableHeader/index.js index 9627872b..39ba2553 100644 --- a/src/components/MTableHeader/index.js +++ b/src/components/MTableHeader/index.js @@ -25,8 +25,6 @@ export function MTableHeader({ onColumnResized, columns, ...props }) { [columns] ); - // console.log('multipleSort ===>', props.multipleSort) - const handleMouseDown = (e, columnDef, colIndex) => { const startX = e.clientX; const th = e.target.closest('th'); diff --git a/src/defaults/props.options.js b/src/defaults/props.options.js index 23f067cd..4944b9ed 100644 --- a/src/defaults/props.options.js +++ b/src/defaults/props.options.js @@ -44,7 +44,7 @@ export default { selectionProps: {}, sorting: true, maxColumnSort: 1, - initialOrderByCollection: [], + defaultOrderByCollection: [], keepSortDirectionOnColumnSwitch: true, toolbar: true, defaultExpanded: false, diff --git a/src/material-table.js b/src/material-table.js index 675161c6..26f26d78 100644 --- a/src/material-table.js +++ b/src/material-table.js @@ -16,17 +16,6 @@ import { MTableScrollbar } from '@components'; -/* TODO: - Initi data based on maxColumnSort - do a initSort if predefaultsort - - On click add columns to be sorted - add an sort order id - on click sort the elements - - On arrow button show only the arrow on buttons that are on the column sort array -*/ - export default class MaterialTable extends React.Component { dataManager = new DataManager(); checkedForFunctions = false; @@ -54,10 +43,10 @@ export default class MaterialTable extends React.Component { (a) => a.tableData.id === renderState.orderBy ), orderDirection: renderState.orderDirection, + orderByCollection: renderState.orderByCollection, page: 0, pageSize: calculatedProps.options.pageSize, search: renderState.searchText, - totalCount: 0 }, showAddRow: false, @@ -84,6 +73,7 @@ export default class MaterialTable extends React.Component { page: this.props.options.initialPage || 0 }); } + /** * THIS WILL NEED TO BE REMOVED EVENTUALLY. * Warn consumer of renamed prop. @@ -93,6 +83,16 @@ export default class MaterialTable extends React.Component { 'Property `onDoubleRowClick` has been renamed to `onRowDoubleClick`' ); } + + /** + * THIS WILL NEED TO BE REMOVED EVENTUALLY. + * Warn consumer of deprecated prop. + */ + if (this.props.sorting !== undefined) { + console.error( + 'Property `sorting` has been deprecated, please start using `maxColumnSort` instead' + ); + } } ); } @@ -123,9 +123,11 @@ export default class MaterialTable extends React.Component { this.dataManager.setTableWidth(props.options.tableWidth ?? 'full'); this.dataManager.setColumns(props.columns, prevColumns, savedColumns); this.dataManager.setDefaultExpanded(props.options.defaultExpanded); - this.dataManager.setMaxColumnSort(props.options.maxColumnSort); this.dataManager.changeRowEditing(); + const { grouping, maxColumnSort } = props.options; + this.dataManager.setMaxColumnSort(grouping ? 1 : maxColumnSort); + if (this.isRemoteData(props)) { this.dataManager.changeApplySearch(false); this.dataManager.changeApplyFilters(false); @@ -137,53 +139,47 @@ export default class MaterialTable extends React.Component { this.dataManager.setData(props.data, props.options.idSynonym); } - let defaultSortColumnIndex = -1; - let defaultSortDirection = ''; - let prevSortColumnIndex = -1; - let prevSortDirection = ''; - if (props && props.options.sorting !== false) { - defaultSortColumnIndex = props.columns.findIndex( - (a) => a.defaultSort && a.sorting !== false - ); - defaultSortDirection = - defaultSortColumnIndex > -1 - ? props.columns[defaultSortColumnIndex].defaultSort - : ''; - } - if (prevColumns) { - prevSortColumnIndex = prevColumns.findIndex( - (a) => a.defaultSort && a.sorting !== false + const { defaultOrderByCollection } = props.options; + let defaultCollectionSort = []; + let prevCollectionSort = []; + + if (defaultOrderByCollection && defaultOrderByCollection.length) { + defaultCollectionSort = [...defaultOrderByCollection]; + } else { + const defaultSorts = getDefaultCollectionSort( + props.columns, + prevColumns, + this.dataManager.maxColumnSort ); - prevSortDirection = - prevSortColumnIndex > -1 && props.columns[prevSortColumnIndex] - ? props.columns[prevSortColumnIndex].defaultSort - : ''; + defaultCollectionSort = [...defaultSorts[0]]; + prevCollectionSort = [...defaultSorts[1]]; } + const defaultSort = JSON.stringify(defaultCollectionSort); + const prevSort = JSON.stringify(prevCollectionSort); + const currentSort = JSON.stringify(this.dataManager.orderByCollection); // If the default sorting changed and differs from the current default sorting, it will trigger a new sorting const shouldReorder = isInit || (!this.isRemoteData() && // Only if a defaultSortingDirection is passed, it will evaluate for changes - defaultSortDirection && + defaultCollectionSort.length && // Default sorting has changed - (defaultSortColumnIndex !== prevSortColumnIndex || - defaultSortDirection !== prevSortDirection) && + defaultSort !== prevSort && // Default sorting differs from current sorting - (defaultSortColumnIndex !== this.dataManager.orderBy || - defaultSortDirection !== this.dataManager.orderDirection)); - - shouldReorder && - this.dataManager.changeOrder( - defaultSortColumnIndex, - defaultSortDirection - ); - shouldReorder && - props.multipleSort && - this.dataManager.changeColumnOrder( - defaultSortColumnIndex, - defaultSortDirection + defaultSort !== currentSort); + + if (shouldReorder) { + defaultCollectionSort.forEach( + ({ orderBy, orderDirection, columnIndex }) => + this.dataManager.changeColumnOrder( + orderBy, + orderDirection, + columnIndex + ) ); + } + isInit && this.dataManager.changeSearchText(props.options.searchText || ''); isInit && this.dataManager.changeSearchDebounce(props.options.searchDebounceDelay); @@ -461,15 +457,7 @@ export default class MaterialTable extends React.Component { }; onChangeOrder = (orderBy, orderDirection, columnIndex) => { - const newOrderBy = orderDirection === '' ? -1 : orderBy; - this.dataManager.changeOrder(newOrderBy, orderDirection); - /* this.props.multipleSort && */ this.dataManager.changeColumnOrder( - newOrderBy, - orderDirection, - columnIndex - ); - - // console.log('onChangeOrder ===>', orderBy, orderDirection) + this.dataManager.changeColumnOrder(orderBy, orderDirection, columnIndex); if (this.isRemoteData()) { const query = { ...this.state.query }; @@ -478,33 +466,30 @@ export default class MaterialTable extends React.Component { (a) => a.tableData.id === newOrderBy ); query.orderDirection = orderDirection; + console.warn( + 'Properties orderBy and orderDirection had been deprecated when remote data, please start using orderByCollection instead' + ); + query.orderByCollection = this.dataManager.orderByCollection; this.onQueryChange(query, () => { this.props.onOrderChange && this.props.onOrderChange(newOrderBy, orderDirection); + this.props.onOrderCollectionChange && + this.props.onOrderCollectionChange( + this.dataManager.orderByCollection + ); }); } else { - // console.log('orderByCollection ===>', this.dataManager.getRenderState().orderByCollection) this.setState(this.dataManager.getRenderState(), () => { this.props.onOrderChange && this.props.onOrderChange(newOrderBy, orderDirection); + this.props.onOrderCollectionChange && + this.props.onOrderCollectionChange( + this.dataManager.orderByCollection + ); }); } }; - onChangeColumnOrder = (orderBy, orderDirection) => { - const newOrderBy = orderDirection === '' ? -1 : orderBy; - /* this.props.multipleSort && */ this.dataManager.changeColumnOrder( - newOrderBy, - orderDirection - ); - - // console.log('orderByCollection ===>', this.dataManager.getRenderState().orderByCollection) - this.setState(this.dataManager.getRenderState(), () => { - this.props.onOrderChange && - this.props.onOrderChange(newOrderBy, orderDirection); - }); - }; - onPageChange = (event, page) => { if (this.isRemoteData()) { const query = { ...this.state.query }; @@ -1019,8 +1004,6 @@ export default class MaterialTable extends React.Component { (a) => a.position === 'row' || typeof a === 'function' ) } - orderBy={this.state.orderBy} - orderDirection={this.state.orderDirection} onAllSelected={this.onAllSelected} onOrderChange={this.onChangeOrder} orderByCollection={this.state.orderByCollection} @@ -1322,3 +1305,34 @@ function functionlessColumns(columns) { }, {}) ); } + +function getDefaultCollectionSort(currentColumns, prevColumns, maxColumnSort) { + let defaultCollectionSort = []; + let prevCollectionSort = []; + + if (maxColumnSort > 0) { + defaultCollectionSort = reduceByDefaultSort(currentColumns); + } + + if (prevColumns) { + prevCollectionSort = reduceByDefaultSort(prevColumns); + } + + return [ + defaultCollectionSort.slice(0, maxColumnSort), + prevCollectionSort.slice(0, maxColumnSort) + ]; +} + +function reduceByDefaultSort(list) { + return list.reduce((acc, column, index) => { + if (column.defaultSort && column.sorting !== false) { + acc.push({ + orderBy: index, + orderDirection: column.defaultSort, + columnIndex: index + }); + } + return acc; + }, []); +} diff --git a/src/prop-types.js b/src/prop-types.js index 783437df..b338e71a 100644 --- a/src/prop-types.js +++ b/src/prop-types.js @@ -371,16 +371,16 @@ export const propTypes = { showSelectGroupCheckbox: PropTypes.bool, showTitle: PropTypes.bool, showTextRowsSelected: PropTypes.bool, - // TODO Remove Sorting - sorting: PropTypes.bool, - initialOrderByCollection: PropTypes.arrayOf( + sorting: PropTypes.bool, // TODO: This will be removed eventually + defaultOrderByCollection: PropTypes.arrayOf( PropTypes.shape({ - orderBy: PropTypes.string, + orderBy: PropTypes.number, oderDirection: PropTypes.string, - orderIndex: PropTypes.string + orderIndex: PropTypes.number }) ), - maxColumnSort: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), + maxColumnSort: PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 'many']), + showColumnSortOrder: PropTypes.bool, keepSortDirectionOnColumnSwitch: PropTypes.bool, toolbar: PropTypes.bool, thirdSortClick: PropTypes.bool, @@ -407,6 +407,7 @@ export const propTypes = { onPageChange: PropTypes.func, onChangeColumnHidden: PropTypes.func, onOrderChange: PropTypes.func, + onOrderCollectionChange: PropTypes.func, onRowClick: PropTypes.func, onRowDoubleClick: PropTypes.func, onTreeExpandChange: PropTypes.func, diff --git a/src/utils/data-manager.js b/src/utils/data-manager.js index 8ba3595a..91dc8e93 100644 --- a/src/utils/data-manager.js +++ b/src/utils/data-manager.js @@ -3,6 +3,8 @@ import uuid from 'uuid'; import { selectFromObject } from './'; import { widthToNumber } from './common-values'; +const MANY = 'many'; + export default class DataManager { checkForId = false; applyFilters = false; @@ -14,8 +16,6 @@ export default class DataManager { lastEditingRow = undefined; maxColumnSort = 1; orderByCollection = []; - orderBy = -1; - orderDirection = 'desc'; pageSize = 5; paging = true; parentFunc = null; @@ -186,7 +186,25 @@ export default class DataManager { } setMaxColumnSort(maxColumnSort) { - this.maxColumnSort = maxColumnSort; + const notAvailableColumns = this.columns.filter( + (column) => column.sorting === false + ); + const availableColumnsLength = + this.columns.length - notAvailableColumns.length; + + console.log('set maxColumnSort', maxColumnSort, availableColumnsLength); + if (maxColumnSort === MANY) { + this.maxColumnSort = availableColumnsLength.length; + } else { + this.maxColumnSort = + maxColumnSort > availableColumnsLength + ? availableColumnsLength + : maxColumnSort; + } + } + + setOrderByCollection(orderByCollection) { + this.orderByCollection = orderByCollection; } changeApplySearch(applySearch) { @@ -375,15 +393,6 @@ export default class DataManager { setCheck([currentGroup]); }; - // TODO: remove this function - changeOrder(orderBy, orderDirection) { - this.orderBy = orderBy; - this.orderDirection = orderDirection; - this.currentPage = 0; - - this.sorted = false; - } - changeColumnOrder(orderBy, orderDirection, columnIndex) { const prevColumns = this.orderByCollection.filter( ({ orderBy }) => orderBy !== columnIndex @@ -394,7 +403,7 @@ export default class DataManager { } console.log('prevColumns', prevColumns, orderBy, columnIndex); - if (orderBy > -1) { + if (orderDirection !== '') { prevColumns.push({ orderBy, orderDirection, columnIndex }); } this.orderByCollection = [...prevColumns]; @@ -734,10 +743,11 @@ export default class DataManager { } } - sortBy(arr, columnsDefs, orderCollection) { + sortBy(list, columnsDefs, orderCollection) { const sort = this.sort; const getFieldValue = this.getFieldValue; - return arr.sort(function sortData( + + return list.sort(function sortData( a, b, columns = columnsDefs, @@ -755,6 +765,7 @@ export default class DataManager { compareValue = columnDef.customSort(a, b, 'row', orderDirection); } } else { + console.log('getFieldValue ===>', getFieldValue(a, columnDef)); // Calculate compare value and modify based on order compareValue = sort( getFieldValue(a, columnDef), @@ -778,7 +789,6 @@ export default class DataManager { } sortList(list) { - let sortedList = [...list]; // TODO: order the collection based on the order console.log('sortList ===>', this.orderByCollection); @@ -793,46 +803,7 @@ export default class DataManager { } }); - return (sortedList = this.sortBy( - sortedList, - columnsDefs, - this.orderByCollection - )); - console.log('sortedList ===>', sortedList); - - let columnDef = this.columns.find((_) => _.tableData.id === this.orderBy); - if (!columnDef) { - columnDef = this.columns[0]; - } - let result = list; - - if (columnDef.customSort) { - if (this.orderDirection === 'desc') { - result = list.sort((a, b) => columnDef.customSort(b, a, 'row', 'desc')); - } else { - result = list.sort((a, b) => - columnDef.customSort(a, b, 'row', this.orderDirection) - ); - } - } else { - result = list.sort( - this.orderDirection === 'desc' - ? (a, b) => - this.sort( - this.getFieldValue(b, columnDef), - this.getFieldValue(a, columnDef), - columnDef.type - ) - : (a, b) => - this.sort( - this.getFieldValue(a, columnDef), - this.getFieldValue(b, columnDef), - columnDef.type - ) - ); - } - - return result; + return this.sortBy(list, columnsDefs, this.orderByCollection); } getRenderState = () => { @@ -866,8 +837,7 @@ export default class DataManager { data: this.sortedData, lastEditingRow: this.lastEditingRow, orderByCollection: this.orderByCollection, - orderBy: this.orderBy, - orderDirection: this.orderDirection, + maxColumnSort: this.maxColumnSort, originalData: [...this.data], pageSize: this.pageSize, renderData: this.pagedData, @@ -1282,9 +1252,9 @@ export default class DataManager { element.groupsIndex = getGroupsIndex(element.groups); sortGroupData(element.groups, level + 1); } else { - if (this.orderBy >= 0 && this.orderDirection) { + if (this.maxColumnSort > 0 && this.orderByCollection.length) { element.data = this.sortList(element.data); - } else if (this.orderDirection === '') { + } else if (!this.orderByCollection.length) { element.data = element.data.sort((a, b) => { return ( this.data.findIndex( @@ -1303,7 +1273,7 @@ export default class DataManager { sortGroupData(this.sortedData, 1); } else if (this.isDataType('tree')) { this.sortedData = [...this.treefiedData]; - if (this.orderBy != -1) { + if (this.maxColumnSort > 0 && this.orderByCollection.length) { this.sortedData = this.sortList(this.sortedData); const sortTree = (list) => { @@ -1321,8 +1291,11 @@ export default class DataManager { } } else if (this.isDataType('normal')) { this.sortedData = [...this.searchedData]; - // if (this.orderBy != -1 && this.applySort) { - if (this.orderByCollection.length && this.applySort) { + if ( + this.maxColumnSort > 0 && + this.orderByCollection.length && + this.applySort + ) { this.sortedData = this.sortList(this.sortedData); } } From 6d8245cbe2ab93a20bf08409bfb8c0aa515d3a87 Mon Sep 17 00:00:00 2001 From: Diiegoav Date: Wed, 24 Aug 2022 12:02:31 -0500 Subject: [PATCH 3/9] removed logs --- src/components/MTableHeader/index.js | 1 - src/utils/data-manager.js | 8 -------- 2 files changed, 9 deletions(-) diff --git a/src/components/MTableHeader/index.js b/src/components/MTableHeader/index.js index 39ba2553..915c2c79 100644 --- a/src/components/MTableHeader/index.js +++ b/src/components/MTableHeader/index.js @@ -470,7 +470,6 @@ function RenderSortButton({ columnIndex, orderByCollection }) { - // console.log('orderByCollection', orderByCollection) const activeColumn = orderByCollection.find( ({ orderBy }) => orderBy === columnDef.tableData.id ); diff --git a/src/utils/data-manager.js b/src/utils/data-manager.js index 91dc8e93..d1212ffc 100644 --- a/src/utils/data-manager.js +++ b/src/utils/data-manager.js @@ -192,7 +192,6 @@ export default class DataManager { const availableColumnsLength = this.columns.length - notAvailableColumns.length; - console.log('set maxColumnSort', maxColumnSort, availableColumnsLength); if (maxColumnSort === MANY) { this.maxColumnSort = availableColumnsLength.length; } else { @@ -402,13 +401,11 @@ export default class DataManager { prevColumns.shift(); } - console.log('prevColumns', prevColumns, orderBy, columnIndex); if (orderDirection !== '') { prevColumns.push({ orderBy, orderDirection, columnIndex }); } this.orderByCollection = [...prevColumns]; - console.log('changeColumnOrder', this.orderByCollection); this.currentPage = 0; this.sorted = false; } @@ -765,7 +762,6 @@ export default class DataManager { compareValue = columnDef.customSort(a, b, 'row', orderDirection); } } else { - console.log('getFieldValue ===>', getFieldValue(a, columnDef)); // Calculate compare value and modify based on order compareValue = sort( getFieldValue(a, columnDef), @@ -789,9 +785,6 @@ export default class DataManager { } sortList(list) { - // TODO: order the collection based on the order - console.log('sortList ===>', this.orderByCollection); - const collectionIds = this.orderByCollection.map( (collection) => collection.orderBy ); @@ -1188,7 +1181,6 @@ export default class DataManager { sortData() { this.paged = false; - console.log('getRenderState', this.sorted); if (this.isDataType('group')) { this.sortedData = [...this.groupedData]; From 951725ab007c38dfeaded705e4bba5a37e28d0c3 Mon Sep 17 00:00:00 2001 From: Diiegoav Date: Wed, 24 Aug 2022 14:43:16 -0500 Subject: [PATCH 4/9] addded testing and demo clean up --- __tests__/demo/demo-components/index.js | 110 +------------- __tests__/multiColumnSort.test.js | 193 ++++++++++++++++++++++++ 2 files changed, 200 insertions(+), 103 deletions(-) create mode 100644 __tests__/multiColumnSort.test.js diff --git a/__tests__/demo/demo-components/index.js b/__tests__/demo/demo-components/index.js index 398414e2..19691f4e 100644 --- a/__tests__/demo/demo-components/index.js +++ b/__tests__/demo/demo-components/index.js @@ -1269,14 +1269,12 @@ export function FixedColumnWithEdit() { } export function TableMultiSorting(props) { - /* const global_cols = [ + const global_cols = [ { title: 'Number', field: 'number', minWidth: 140, - maxWidth: 400, - // type: 'numeric' - // , sorting: false + maxWidth: 400 }, { title: 'Title', @@ -1302,22 +1300,9 @@ export function TableMultiSorting(props) { sorting: true, defaultSort: 'asc' } - ]; */ - - const global_cols = [ - { title: 'Adl', field: 'name' }, - { title: 'Soyadl', field: 'surname' }, - { title: 'Cinsiyet', field: 'sex' }, - { title: 'Tipi', field: 'type', removable: false }, - { title: 'Doğum Yili', field: 'birthYear', type: 'numeric' }, - { - title: 'Doğum Yeri', - field: 'birthCity', - lookup: { 34: 'İstanbul', 63: 'Şanliurfa' } - } ]; - /* const global_data1 = [ + const global_data1 = [ { number: 1, title: 'Developer', @@ -1342,71 +1327,10 @@ export function TableMultiSorting(props) { { number: 3, title: 'Consultant', - name: 'Edgar', - lastName: 'Martinez', + name: 'Raul', + lastName: 'Barak', id: '1236' } - ]; */ - - const global_data1 = [ - { - id: 1, - name: 'a', - surname: 'Baran', - birthYear: 1987, - birthCity: 63, - sex: 'Male', - type: 'adult' - }, - { - id: 2, - name: 'b', - surname: 'Baran', - birthYear: 1987, - birthCity: 34, - sex: 'Female', - type: 'adult', - parentId: 1 - }, - { - id: 3, - name: 'c', - surname: 'Baran', - birthYear: 1987, - birthCity: 34, - sex: 'Female', - type: 'child', - parentId: 1 - }, - { - id: 4, - name: 'd', - surname: 'Baran', - birthYear: 1987, - birthCity: 34, - sex: 'Female', - type: 'child', - parentId: 3 - }, - { - id: 5, - name: 'e', - surname: 'Baran', - birthYear: 1987, - birthCity: 34, - sex: 'Female', - type: 'child' - }, - { - id: 6, - name: 'f', - surname: 'Baran', - birthYear: 1987, - birthCity: 34, - sex: 'Female', - type: 'child', - parentId: 5 - } ]; const orderCollection = [ @@ -1422,15 +1346,9 @@ export function TableMultiSorting(props) { ( - - )} */ - parentChildData={(row, rows) => rows.find((a) => a.id === row.parentId)} + title="Multi Column Sort" options={{ - selection: true, - // selection: true - // sorting: true, + selection: false, keepSortDirectionOnColumnSwitch: false, maxColumnSort: 3, defaultOrderByCollection: orderCollection @@ -1438,18 +1356,4 @@ export function TableMultiSorting(props) { onOrderCollectionChange={onOrderCollectionChange} /> ); - /* return ( - ( - // - // )} - options={{ - // selection: true - sorting: false - }} - /> - ); */ } diff --git a/__tests__/multiColumnSort.test.js b/__tests__/multiColumnSort.test.js new file mode 100644 index 00000000..53fba123 --- /dev/null +++ b/__tests__/multiColumnSort.test.js @@ -0,0 +1,193 @@ +import '@testing-library/jest-dom'; +import { fireEvent, render } from '@testing-library/react'; +import * as React from 'react'; +import MaterialTable from '../src'; + +const columns = [ + { + title: 'Number', + field: 'number', + minWidth: 140, + maxWidth: 400 + }, + { + title: 'Title', + field: 'title', + minWidth: 140, + maxWidth: 400, + sorting: true + }, + { + title: 'Name', + field: 'name', + minWidth: 140, + maxWidth: 400, + sorting: true + }, + { + title: 'Last Name', + field: 'lastName', + minWidth: 140, + maxWidth: 400, + sorting: true + } +]; + +const data = [ + { + number: 1, + title: 'Developer', + name: 'Mehmet', + lastName: 'Baran', + id: '1231' + }, + { + number: 22, + title: 'Developer', + name: 'Pratik', + lastName: 'N', + id: '1234' + }, + { + number: 25, + title: 'Human Resources', + name: 'Juan', + lastName: 'Lopez', + id: '1235' + }, + { + number: 3, + title: 'Consultant', + name: 'Raul', + lastName: 'Barak', + id: '1236' + } +]; + +describe('Multi Column Sort', () => { + let initialOrderCollection = []; + let onOrderCollectionChangeSpy; + + beforeEach(() => { + jest.clearAllMocks(); + onOrderCollectionChangeSpy = jest.fn(); + initialOrderCollection = [ + { + orderBy: 1, + orderDirection: 'asc', + columnIndex: 1 + }, + { + orderBy: 2, + orderDirection: 'desc', + columnIndex: 2 + } + ]; + }); + + test('should update table by multi column', () => { + const { queryAllByTestId } = render( + + ); + + const numberColumn = queryAllByTestId('mtableheader-sortlabel')[0]; + fireEvent.click(numberColumn); + + expect(onOrderCollectionChangeSpy).toHaveBeenCalledWith([ + { columnIndex: 0, orderBy: 0, orderDirection: 'asc' } + ]); + + const titleColumn = queryAllByTestId('mtableheader-sortlabel')[1]; + fireEvent.click(titleColumn); + + expect(onOrderCollectionChangeSpy).toHaveBeenCalledWith([ + { columnIndex: 0, orderBy: 0, orderDirection: 'asc' }, + { columnIndex: 1, orderBy: 1, orderDirection: 'asc' } + ]); + }); + + test('should update table by multi column and replace first if reach the maximum order columns', () => { + const { queryAllByTestId } = render( + + ); + + const numberColumn = queryAllByTestId('mtableheader-sortlabel')[0]; + fireEvent.click(numberColumn); + + expect(onOrderCollectionChangeSpy).toHaveBeenCalledWith([ + { columnIndex: 0, orderBy: 0, orderDirection: 'asc' } + ]); + + fireEvent.click(queryAllByTestId('mtableheader-sortlabel')[1]); + fireEvent.click(queryAllByTestId('mtableheader-sortlabel')[2]); + fireEvent.click(queryAllByTestId('mtableheader-sortlabel')[3]); + + expect(onOrderCollectionChangeSpy).toHaveBeenCalledWith([ + { columnIndex: 1, orderBy: 1, orderDirection: 'asc' }, + { columnIndex: 2, orderBy: 2, orderDirection: 'asc' }, + { columnIndex: 3, orderBy: 3, orderDirection: 'asc' } + ]); + }); + + test('should order desc when secon click', () => { + const { queryAllByTestId } = render( + + ); + + const numberColumn = queryAllByTestId('mtableheader-sortlabel')[0]; + fireEvent.click(numberColumn); + fireEvent.click(numberColumn); + + expect(onOrderCollectionChangeSpy).toHaveBeenCalledWith([ + { columnIndex: 0, orderBy: 0, orderDirection: 'desc' } + ]); + }); + + test('should have being initialized by defaultOrderByCollection', () => { + const { queryAllByTestId } = render( + + ); + + const numberColumn = queryAllByTestId('mtableheader-sortlabel')[0]; + fireEvent.click(numberColumn); + + expect(onOrderCollectionChangeSpy).toHaveBeenCalledWith([ + { columnIndex: 1, orderBy: 1, orderDirection: 'asc' }, + { columnIndex: 2, orderBy: 2, orderDirection: 'desc' }, + { columnIndex: 0, orderBy: 0, orderDirection: 'asc' } + ]); + }); +}); From b08d40d28ffd6d30e88b561c18933469a30f6b45 Mon Sep 17 00:00:00 2001 From: Diiegoav Date: Thu, 25 Aug 2022 14:59:20 -0500 Subject: [PATCH 5/9] updated on change column order --- __tests__/demo/demo-components/index.js | 5 +- __tests__/multiColumnSort.test.js | 26 +++++----- src/components/MTableHeader/index.js | 63 +++++++++++++------------ src/defaults/props.options.js | 1 + src/material-table.js | 28 +++++------ src/utils/data-manager.js | 56 ++++++++++++++++++---- 6 files changed, 110 insertions(+), 69 deletions(-) diff --git a/__tests__/demo/demo-components/index.js b/__tests__/demo/demo-components/index.js index 19691f4e..50f73154 100644 --- a/__tests__/demo/demo-components/index.js +++ b/__tests__/demo/demo-components/index.js @@ -1334,8 +1334,8 @@ export function TableMultiSorting(props) { ]; const orderCollection = [ - { orderBy: 1, orderDirection: 'asc', columnIndex: 1 }, - { orderBy: 2, orderDirection: 'desc', columnIndex: 2 } + { orderBy: 1, orderDirection: 'asc', sortOrder: 1 }, + { orderBy: 2, orderDirection: 'desc', sortOrder: 2 } ]; const onOrderCollectionChange = (orderByCollection) => { @@ -1349,7 +1349,6 @@ export function TableMultiSorting(props) { title="Multi Column Sort" options={{ selection: false, - keepSortDirectionOnColumnSwitch: false, maxColumnSort: 3, defaultOrderByCollection: orderCollection }} diff --git a/__tests__/multiColumnSort.test.js b/__tests__/multiColumnSort.test.js index 53fba123..b8e4e626 100644 --- a/__tests__/multiColumnSort.test.js +++ b/__tests__/multiColumnSort.test.js @@ -75,12 +75,12 @@ describe('Multi Column Sort', () => { { orderBy: 1, orderDirection: 'asc', - columnIndex: 1 + sortOrder: 1 }, { orderBy: 2, orderDirection: 'desc', - columnIndex: 2 + sortOrder: 2 } ]; }); @@ -102,15 +102,15 @@ describe('Multi Column Sort', () => { fireEvent.click(numberColumn); expect(onOrderCollectionChangeSpy).toHaveBeenCalledWith([ - { columnIndex: 0, orderBy: 0, orderDirection: 'asc' } + { sortOrder: 0, orderBy: 0, orderDirection: 'asc' } ]); const titleColumn = queryAllByTestId('mtableheader-sortlabel')[1]; fireEvent.click(titleColumn); expect(onOrderCollectionChangeSpy).toHaveBeenCalledWith([ - { columnIndex: 0, orderBy: 0, orderDirection: 'asc' }, - { columnIndex: 1, orderBy: 1, orderDirection: 'asc' } + { sortOrder: 0, orderBy: 0, orderDirection: 'asc' }, + { sortOrder: 1, orderBy: 1, orderDirection: 'asc' } ]); }); @@ -131,7 +131,7 @@ describe('Multi Column Sort', () => { fireEvent.click(numberColumn); expect(onOrderCollectionChangeSpy).toHaveBeenCalledWith([ - { columnIndex: 0, orderBy: 0, orderDirection: 'asc' } + { sortOrder: 0, orderBy: 0, orderDirection: 'asc' } ]); fireEvent.click(queryAllByTestId('mtableheader-sortlabel')[1]); @@ -139,9 +139,9 @@ describe('Multi Column Sort', () => { fireEvent.click(queryAllByTestId('mtableheader-sortlabel')[3]); expect(onOrderCollectionChangeSpy).toHaveBeenCalledWith([ - { columnIndex: 1, orderBy: 1, orderDirection: 'asc' }, - { columnIndex: 2, orderBy: 2, orderDirection: 'asc' }, - { columnIndex: 3, orderBy: 3, orderDirection: 'asc' } + { sortOrder: 1, orderBy: 1, orderDirection: 'asc' }, + { sortOrder: 2, orderBy: 2, orderDirection: 'asc' }, + { sortOrder: 3, orderBy: 3, orderDirection: 'asc' } ]); }); @@ -163,7 +163,7 @@ describe('Multi Column Sort', () => { fireEvent.click(numberColumn); expect(onOrderCollectionChangeSpy).toHaveBeenCalledWith([ - { columnIndex: 0, orderBy: 0, orderDirection: 'desc' } + { sortOrder: 0, orderBy: 0, orderDirection: 'desc' } ]); }); @@ -185,9 +185,9 @@ describe('Multi Column Sort', () => { fireEvent.click(numberColumn); expect(onOrderCollectionChangeSpy).toHaveBeenCalledWith([ - { columnIndex: 1, orderBy: 1, orderDirection: 'asc' }, - { columnIndex: 2, orderBy: 2, orderDirection: 'desc' }, - { columnIndex: 0, orderBy: 0, orderDirection: 'asc' } + { sortOrder: 1, orderBy: 1, orderDirection: 'asc' }, + { sortOrder: 2, orderBy: 2, orderDirection: 'desc' }, + { sortOrder: 0, orderBy: 0, orderDirection: 'asc' } ]); }); }); diff --git a/src/components/MTableHeader/index.js b/src/components/MTableHeader/index.js index 915c2c79..a9524f22 100644 --- a/src/components/MTableHeader/index.js +++ b/src/components/MTableHeader/index.js @@ -245,8 +245,8 @@ export function MTableHeader({ onColumnResized, columns, ...props }) { icon={icons.SortArrow} thirdSortClick={options.thirdSortClick} onOrderChange={props.onOrderChange} - columnIndex={index} orderByCollection={props.orderByCollection} + showColumnSortOrder={props.showColumnSortOrder} > {columnDef.title} @@ -267,8 +267,8 @@ export function MTableHeader({ onColumnResized, columns, ...props }) { icon={icons.SortArrow} thirdSortClick={options.thirdSortClick} onOrderChange={props.onOrderChange} - columnIndex={index} orderByCollection={props.orderByCollection} + showColumnSortOrder={props.showColumnSortOrder} > {columnDef.title} @@ -431,8 +431,6 @@ export function MTableHeader({ onColumnResized, columns, ...props }) { ); } -// TODO: Case when returning back to prev clicker column and keepSortDirectionOnColumnSwitch, should start form where user left -// Move this to a utils const computeNewOrderDirection = ( orderBy, orderDirection, @@ -467,12 +465,11 @@ function RenderSortButton({ thirdSortClick, onOrderChange, children, - columnIndex, orderByCollection }) { - const activeColumn = orderByCollection.find( - ({ orderBy }) => orderBy === columnDef.tableData.id - ); + const activeColumn = orderByCollection + .filter((collection) => collection.sortOrder) + .find(({ orderBy }) => orderBy === columnDef.tableData.id); // If current sorted column or prop asked to // maintain sort order when switching sorted column, @@ -493,27 +490,34 @@ function RenderSortButton({ const orderBy = activeColumn && activeColumn.orderBy; return ( - { - const newOrderDirection = computeNewOrderDirection( - orderBy, - direction, - columnDef, - thirdSortClick, - keepSortDirectionOnColumnSwitch - ); - onOrderChange(columnDef.tableData.id, newOrderDirection, columnIndex); - }} - > - {children} - + <> + { + const newOrderDirection = computeNewOrderDirection( + orderBy, + direction, + columnDef, + thirdSortClick, + keepSortDirectionOnColumnSwitch + ); + onOrderChange( + columnDef.tableData.id, + newOrderDirection, + activeColumn && activeColumn.sortOrder + ); + }} + > + {children} + + {activeColumn && activeColumn.sortOrder} + ); } @@ -532,6 +536,7 @@ MTableHeader.propTypes = { onOrderChange: PropTypes.func, showActionsColumn: PropTypes.bool, orderByCollection: PropTypes.array, + showColumnSortOrder: PropTypes.bool, tooltip: PropTypes.string }; diff --git a/src/defaults/props.options.js b/src/defaults/props.options.js index 4944b9ed..7dc3e919 100644 --- a/src/defaults/props.options.js +++ b/src/defaults/props.options.js @@ -45,6 +45,7 @@ export default { sorting: true, maxColumnSort: 1, defaultOrderByCollection: [], + showColumnSortOrder: false, keepSortDirectionOnColumnSwitch: true, toolbar: true, defaultExpanded: false, diff --git a/src/material-table.js b/src/material-table.js index c4e47a1e..af166518 100644 --- a/src/material-table.js +++ b/src/material-table.js @@ -127,6 +127,7 @@ export default class MaterialTable extends React.Component { const { grouping, maxColumnSort } = props.options; this.dataManager.setMaxColumnSort(grouping ? 1 : maxColumnSort); + this.dataManager.setOrderByCollection(); if (this.isRemoteData(props)) { this.dataManager.changeApplySearch(false); @@ -170,13 +171,8 @@ export default class MaterialTable extends React.Component { defaultSort !== currentSort); if (shouldReorder) { - defaultCollectionSort.forEach( - ({ orderBy, orderDirection, columnIndex }) => - this.dataManager.changeColumnOrder( - orderBy, - orderDirection, - columnIndex - ) + defaultCollectionSort.forEach(({ orderBy, orderDirection, sortOrder }) => + this.dataManager.changeColumnOrder(orderBy, orderDirection, sortOrder) ); } @@ -456,14 +452,14 @@ export default class MaterialTable extends React.Component { this.setState(this.dataManager.getRenderState()); }; - onChangeOrder = (orderBy, orderDirection, columnIndex) => { - this.dataManager.changeColumnOrder(orderBy, orderDirection, columnIndex); + onChangeOrder = (orderBy, orderDirection, sortOrder) => { + this.dataManager.changeColumnOrder(orderBy, orderDirection, sortOrder); if (this.isRemoteData()) { const query = { ...this.state.query }; query.page = 0; query.orderBy = this.state.columns.find( - (a) => a.tableData.id === newOrderBy + (a) => a.tableData.id === orderBy ); query.orderDirection = orderDirection; console.warn( @@ -472,7 +468,7 @@ export default class MaterialTable extends React.Component { query.orderByCollection = this.dataManager.orderByCollection; this.onQueryChange(query, () => { this.props.onOrderChange && - this.props.onOrderChange(newOrderBy, orderDirection); + this.props.onOrderChange(orderBy, orderDirection); this.props.onOrderCollectionChange && this.props.onOrderCollectionChange( this.dataManager.orderByCollection @@ -481,7 +477,7 @@ export default class MaterialTable extends React.Component { } else { this.setState(this.dataManager.getRenderState(), () => { this.props.onOrderChange && - this.props.onOrderChange(newOrderBy, orderDirection); + this.props.onOrderChange(orderBy, orderDirection); this.props.onOrderCollectionChange && this.props.onOrderCollectionChange( this.dataManager.orderByCollection @@ -1011,7 +1007,11 @@ export default class MaterialTable extends React.Component { } onAllSelected={this.onAllSelected} onOrderChange={this.onChangeOrder} - orderByCollection={this.state.orderByCollection} + orderByCollection={this.state.orderByCollection.slice( + 0, + this.state.maxColumnSort + )} + showColumnSortOrder={this.props.showColumnSortOrder} isTreeData={this.props.parentChildData !== undefined} treeDataMaxLevel={this.state.treeDataMaxLevel} onColumnResized={this.onColumnResized} @@ -1335,7 +1335,7 @@ function reduceByDefaultSort(list) { acc.push({ orderBy: index, orderDirection: column.defaultSort, - columnIndex: index + sortOrder: index }); } return acc; diff --git a/src/utils/data-manager.js b/src/utils/data-manager.js index d1212ffc..91b8d2f8 100644 --- a/src/utils/data-manager.js +++ b/src/utils/data-manager.js @@ -202,8 +202,12 @@ export default class DataManager { } } - setOrderByCollection(orderByCollection) { - this.orderByCollection = orderByCollection; + setOrderByCollection() { + this.orderByCollection = this.columns.map((columnDef) => ({ + orderBy: columnDef.tableData.id, + sortOrder: undefined, + orderDirection: '' + })); } changeApplySearch(applySearch) { @@ -392,18 +396,50 @@ export default class DataManager { setCheck([currentGroup]); }; - changeColumnOrder(orderBy, orderDirection, columnIndex) { - const prevColumns = this.orderByCollection.filter( - ({ orderBy }) => orderBy !== columnIndex + sortOrderCollection = (list) => { + return list.sort((a, b) => { + if (!a.sortOrder) return 1; + if (!b.sortOrder) return -1; + return a.sortOrder < b.sortOrder ? -1 : a.sortOrder > b.sortOrder ? 1 : 0; + }); + }; + + changeColumnOrder(orderBy, orderDirection, sortOrder) { + let prevColumns = []; + + const sortColumns = this.orderByCollection.filter( + (collection) => collection.sortOrder ); + if (sortColumns.length === this.maxColumnSort && !sortOrder) { + this.orderByCollection[0].orderDirection = ''; + this.orderByCollection[0].sortOrder = undefined; + + prevColumns = this.orderByCollection.map((collection) => { + if (collection.sortOrder) { + collection.sortOrder -= 1; + } else if (collection.orderBy === orderBy && orderDirection) { + collection.sortOrder = sortColumns.length; + collection.orderDirection = orderDirection; + } - if (prevColumns.length == this.maxColumnSort) { - prevColumns.shift(); + return collection; + }); + } else { + prevColumns = this.orderByCollection.map((collection) => { + if (collection.orderBy === orderBy && orderDirection) { + collection.orderDirection = orderDirection; + collection.sortOrder = sortOrder || sortColumns.length + 1; + } else if (!orderDirection && collection.orderBy === orderBy) { + collection.orderDirection = orderDirection; + collection.sortOrder = undefined; + } else if (!orderDirection && sortOrder < collection.sortOrder) { + collection.sortOrder -= 1; + } + return collection; + }); } - if (orderDirection !== '') { - prevColumns.push({ orderBy, orderDirection, columnIndex }); - } + prevColumns = this.sortOrderCollection(prevColumns); this.orderByCollection = [...prevColumns]; this.currentPage = 0; From 2cb39180cfdf3d5ee5ee77c01bae3a9def8c0a8a Mon Sep 17 00:00:00 2001 From: Diiegoav Date: Thu, 25 Aug 2022 16:31:54 -0500 Subject: [PATCH 6/9] added style, and show order indicator flag --- __tests__/demo/demo-components/index.js | 5 +++-- src/components/MTableHeader/index.js | 25 ++++++++++++++++++------- src/material-table.js | 12 ++++-------- src/prop-types.js | 1 + src/utils/data-manager.js | 8 +++++--- 5 files changed, 31 insertions(+), 20 deletions(-) diff --git a/__tests__/demo/demo-components/index.js b/__tests__/demo/demo-components/index.js index 50f73154..de42d905 100644 --- a/__tests__/demo/demo-components/index.js +++ b/__tests__/demo/demo-components/index.js @@ -1349,8 +1349,9 @@ export function TableMultiSorting(props) { title="Multi Column Sort" options={{ selection: false, - maxColumnSort: 3, - defaultOrderByCollection: orderCollection + maxColumnSort: 2, + defaultOrderByCollection: orderCollection, + showColumnSortOrder: true }} onOrderCollectionChange={onOrderCollectionChange} /> diff --git a/src/components/MTableHeader/index.js b/src/components/MTableHeader/index.js index a9524f22..643b9dc2 100644 --- a/src/components/MTableHeader/index.js +++ b/src/components/MTableHeader/index.js @@ -246,7 +246,8 @@ export function MTableHeader({ onColumnResized, columns, ...props }) { thirdSortClick={options.thirdSortClick} onOrderChange={props.onOrderChange} orderByCollection={props.orderByCollection} - showColumnSortOrder={props.showColumnSortOrder} + showColumnSortOrder={options.showColumnSortOrder} + sortOrderIndicatorStyle={options.sortOrderIndicatorStyle} > {columnDef.title} @@ -268,7 +269,8 @@ export function MTableHeader({ onColumnResized, columns, ...props }) { thirdSortClick={options.thirdSortClick} onOrderChange={props.onOrderChange} orderByCollection={props.orderByCollection} - showColumnSortOrder={props.showColumnSortOrder} + showColumnSortOrder={options.showColumnSortOrder} + sortOrderIndicatorStyle={options.sortOrderIndicatorStyle} > {columnDef.title} @@ -465,11 +467,13 @@ function RenderSortButton({ thirdSortClick, onOrderChange, children, - orderByCollection + orderByCollection, + showColumnSortOrder, + sortOrderIndicatorStyle }) { - const activeColumn = orderByCollection - .filter((collection) => collection.sortOrder) - .find(({ orderBy }) => orderBy === columnDef.tableData.id); + const activeColumn = orderByCollection.find( + ({ orderBy }) => orderBy === columnDef.tableData.id + ); // If current sorted column or prop asked to // maintain sort order when switching sorted column, @@ -516,7 +520,14 @@ function RenderSortButton({ > {children} - {activeColumn && activeColumn.sortOrder} + {showColumnSortOrder && activeColumn && ( + + {activeColumn.sortOrder} + + )} ); } diff --git a/src/material-table.js b/src/material-table.js index af166518..f516a562 100644 --- a/src/material-table.js +++ b/src/material-table.js @@ -465,13 +465,13 @@ export default class MaterialTable extends React.Component { console.warn( 'Properties orderBy and orderDirection had been deprecated when remote data, please start using orderByCollection instead' ); - query.orderByCollection = this.dataManager.orderByCollection; + query.orderByCollection = this.dataManager.getOrderByCollection(); this.onQueryChange(query, () => { this.props.onOrderChange && this.props.onOrderChange(orderBy, orderDirection); this.props.onOrderCollectionChange && this.props.onOrderCollectionChange( - this.dataManager.orderByCollection + this.dataManager.getOrderByCollection() ); }); } else { @@ -480,7 +480,7 @@ export default class MaterialTable extends React.Component { this.props.onOrderChange(orderBy, orderDirection); this.props.onOrderCollectionChange && this.props.onOrderCollectionChange( - this.dataManager.orderByCollection + this.dataManager.getOrderByCollection() ); }); } @@ -1007,11 +1007,7 @@ export default class MaterialTable extends React.Component { } onAllSelected={this.onAllSelected} onOrderChange={this.onChangeOrder} - orderByCollection={this.state.orderByCollection.slice( - 0, - this.state.maxColumnSort - )} - showColumnSortOrder={this.props.showColumnSortOrder} + orderByCollection={this.dataManager.getOrderByCollection()} isTreeData={this.props.parentChildData !== undefined} treeDataMaxLevel={this.state.treeDataMaxLevel} onColumnResized={this.onColumnResized} diff --git a/src/prop-types.js b/src/prop-types.js index b338e71a..dbae0de3 100644 --- a/src/prop-types.js +++ b/src/prop-types.js @@ -381,6 +381,7 @@ export const propTypes = { ), maxColumnSort: PropTypes.oneOf([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 'many']), showColumnSortOrder: PropTypes.bool, + sortOrderIndicatorStyle: PropTypes.object, keepSortDirectionOnColumnSwitch: PropTypes.bool, toolbar: PropTypes.bool, thirdSortClick: PropTypes.bool, diff --git a/src/utils/data-manager.js b/src/utils/data-manager.js index 91b8d2f8..5d3da647 100644 --- a/src/utils/data-manager.js +++ b/src/utils/data-manager.js @@ -396,6 +396,10 @@ export default class DataManager { setCheck([currentGroup]); }; + getOrderByCollection = () => { + return this.orderByCollection.filter((collection) => collection.sortOrder); + }; + sortOrderCollection = (list) => { return list.sort((a, b) => { if (!a.sortOrder) return 1; @@ -406,10 +410,8 @@ export default class DataManager { changeColumnOrder(orderBy, orderDirection, sortOrder) { let prevColumns = []; + const sortColumns = this.getOrderByCollection(); - const sortColumns = this.orderByCollection.filter( - (collection) => collection.sortOrder - ); if (sortColumns.length === this.maxColumnSort && !sortOrder) { this.orderByCollection[0].orderDirection = ''; this.orderByCollection[0].sortOrder = undefined; From b7a90cacf79f0aa7eb35d6100dd23450d8a3d230 Mon Sep 17 00:00:00 2001 From: Diiegoav Date: Thu, 25 Aug 2022 16:42:20 -0500 Subject: [PATCH 7/9] corrected test --- __tests__/multiColumnSort.test.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/__tests__/multiColumnSort.test.js b/__tests__/multiColumnSort.test.js index b8e4e626..fbb3745e 100644 --- a/__tests__/multiColumnSort.test.js +++ b/__tests__/multiColumnSort.test.js @@ -102,15 +102,15 @@ describe('Multi Column Sort', () => { fireEvent.click(numberColumn); expect(onOrderCollectionChangeSpy).toHaveBeenCalledWith([ - { sortOrder: 0, orderBy: 0, orderDirection: 'asc' } + { sortOrder: 1, orderBy: 0, orderDirection: 'asc' } ]); const titleColumn = queryAllByTestId('mtableheader-sortlabel')[1]; fireEvent.click(titleColumn); expect(onOrderCollectionChangeSpy).toHaveBeenCalledWith([ - { sortOrder: 0, orderBy: 0, orderDirection: 'asc' }, - { sortOrder: 1, orderBy: 1, orderDirection: 'asc' } + { sortOrder: 1, orderBy: 0, orderDirection: 'asc' }, + { sortOrder: 2, orderBy: 1, orderDirection: 'asc' } ]); }); @@ -131,7 +131,7 @@ describe('Multi Column Sort', () => { fireEvent.click(numberColumn); expect(onOrderCollectionChangeSpy).toHaveBeenCalledWith([ - { sortOrder: 0, orderBy: 0, orderDirection: 'asc' } + { sortOrder: 1, orderBy: 0, orderDirection: 'asc' } ]); fireEvent.click(queryAllByTestId('mtableheader-sortlabel')[1]); @@ -163,7 +163,7 @@ describe('Multi Column Sort', () => { fireEvent.click(numberColumn); expect(onOrderCollectionChangeSpy).toHaveBeenCalledWith([ - { sortOrder: 0, orderBy: 0, orderDirection: 'desc' } + { sortOrder: 1, orderBy: 0, orderDirection: 'desc' } ]); }); @@ -187,7 +187,7 @@ describe('Multi Column Sort', () => { expect(onOrderCollectionChangeSpy).toHaveBeenCalledWith([ { sortOrder: 1, orderBy: 1, orderDirection: 'asc' }, { sortOrder: 2, orderBy: 2, orderDirection: 'desc' }, - { sortOrder: 0, orderBy: 0, orderDirection: 'asc' } + { sortOrder: 3, orderBy: 0, orderDirection: 'asc' } ]); }); }); From c7f96e69f4d414b06159acb0665844e03bf01892 Mon Sep 17 00:00:00 2001 From: Diiegoav Date: Mon, 29 Aug 2022 10:44:05 -0500 Subject: [PATCH 8/9] corrected on sort logic --- __tests__/pre.build.test.js | 3 +++ src/material-table.js | 2 +- src/utils/data-manager.js | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/__tests__/pre.build.test.js b/__tests__/pre.build.test.js index 78da0e2b..3c625374 100644 --- a/__tests__/pre.build.test.js +++ b/__tests__/pre.build.test.js @@ -39,6 +39,7 @@ describe('Render Table : Pre Build', () => { expect(screen.getAllByRole('table')).toHaveLength(2); }); }); + // Render table with data describe('when attempting to render a table with data', () => { it('renders without crashing', () => { @@ -78,6 +79,7 @@ describe('Render Table : Pre Build', () => { name: /5 rows First Page Previous Page 1-5 of 99 Next Page Last Page/i }); }); + it('navigates between the pages', () => { const data = makeData(); render(); @@ -125,6 +127,7 @@ describe('Render Table : Pre Build', () => { }); expect(screen.getAllByRole('row')).toHaveLength(8); }); + it('filters data by search input', async () => { const data = makeData(); render(); diff --git a/src/material-table.js b/src/material-table.js index f516a562..92b311b7 100644 --- a/src/material-table.js +++ b/src/material-table.js @@ -170,7 +170,7 @@ export default class MaterialTable extends React.Component { // Default sorting differs from current sorting defaultSort !== currentSort); - if (shouldReorder) { + if (shouldReorder && defaultCollectionSort.length) { defaultCollectionSort.forEach(({ orderBy, orderDirection, sortOrder }) => this.dataManager.changeColumnOrder(orderBy, orderDirection, sortOrder) ); diff --git a/src/utils/data-manager.js b/src/utils/data-manager.js index 5d3da647..fe05ecfa 100644 --- a/src/utils/data-manager.js +++ b/src/utils/data-manager.js @@ -1323,7 +1323,7 @@ export default class DataManager { this.sortedData = [...this.searchedData]; if ( this.maxColumnSort > 0 && - this.orderByCollection.length && + this.getOrderByCollection().length && this.applySort ) { this.sortedData = this.sortList(this.sortedData); From 6989d57729b57563329bf3ee9f0fb34f10557f53 Mon Sep 17 00:00:00 2001 From: Diiegoav Date: Thu, 1 Sep 2022 16:32:45 -0500 Subject: [PATCH 9/9] code clean up and feedback --- src/components/MTableHeader/index.js | 16 +++++-- src/index.js | 2 + src/material-table.js | 14 ++++-- src/prop-types.js | 16 ++++++- src/utils/constants.js | 1 + src/utils/data-manager.js | 64 +++++++++++++--------------- 6 files changed, 70 insertions(+), 43 deletions(-) create mode 100644 src/utils/constants.js diff --git a/src/components/MTableHeader/index.js b/src/components/MTableHeader/index.js index 643b9dc2..0c21f1ea 100644 --- a/src/components/MTableHeader/index.js +++ b/src/components/MTableHeader/index.js @@ -236,7 +236,9 @@ export function MTableHeader({ onColumnResized, columns, ...props }) { } } > - {columnDef.sorting !== false && options.sorting ? ( + {columnDef.sorting !== false && + options.sorting && + props.allowSorting ? ( ); - } else if (columnDef.sorting !== false && options.sorting) { + } else if ( + columnDef.sorting !== false && + options.sorting && + !props.allowSorting + ) { content = ( ({ diff --git a/src/index.js b/src/index.js index cb82f92c..f8698261 100644 --- a/src/index.js +++ b/src/index.js @@ -56,3 +56,5 @@ export { MTableSteppedPagination, MTableToolbar } from './components'; + +export { ALL_COLUMNS } from './utils/constants'; diff --git a/src/material-table.js b/src/material-table.js index 92b311b7..37ab5523 100644 --- a/src/material-table.js +++ b/src/material-table.js @@ -145,14 +145,17 @@ export default class MaterialTable extends React.Component { let prevCollectionSort = []; if (defaultOrderByCollection && defaultOrderByCollection.length) { - defaultCollectionSort = [...defaultOrderByCollection]; + defaultCollectionSort = [...defaultOrderByCollection].slice( + 0, + maxColumnSort + ); } else { const defaultSorts = getDefaultCollectionSort( props.columns, prevColumns, this.dataManager.maxColumnSort ); - defaultCollectionSort = [...defaultSorts[0]]; + defaultCollectionSort = [...defaultSorts[0]].slice(0, maxColumnSort); prevCollectionSort = [...defaultSorts[1]]; } @@ -170,7 +173,11 @@ export default class MaterialTable extends React.Component { // Default sorting differs from current sorting defaultSort !== currentSort); - if (shouldReorder && defaultCollectionSort.length) { + if ( + shouldReorder && + defaultCollectionSort.length > 0 && + maxColumnSort > 0 + ) { defaultCollectionSort.forEach(({ orderBy, orderDirection, sortOrder }) => this.dataManager.changeColumnOrder(orderBy, orderDirection, sortOrder) ); @@ -1012,6 +1019,7 @@ export default class MaterialTable extends React.Component { treeDataMaxLevel={this.state.treeDataMaxLevel} onColumnResized={this.onColumnResized} scrollWidth={this.state.width} + allowSorting={this.dataManager.maxColumnSort !== 0} /> )} column.sorting === false - ); - const availableColumnsLength = - this.columns.length - notAvailableColumns.length; + const availableColumnsLength = this.columns.filter( + (column) => column.sorting !== false + ).length; - if (maxColumnSort === MANY) { - this.maxColumnSort = availableColumnsLength.length; + if (maxColumnSort === ALL_COLUMNS) { + this.maxColumnSort = availableColumnsLength; } else { - this.maxColumnSort = - maxColumnSort > availableColumnsLength - ? availableColumnsLength - : maxColumnSort; + this.maxColumnSort = Math.min(maxColumnSort, availableColumnsLength); } } @@ -404,7 +398,7 @@ export default class DataManager { return list.sort((a, b) => { if (!a.sortOrder) return 1; if (!b.sortOrder) return -1; - return a.sortOrder < b.sortOrder ? -1 : a.sortOrder > b.sortOrder ? 1 : 0; + return a.sortOrder - b.sortOrder; }); }; @@ -778,15 +772,27 @@ export default class DataManager { } } - sortBy(list, columnsDefs, orderCollection) { + sortList(list) { + const collectionIds = this.orderByCollection.map( + (collection) => collection.orderBy + ); + const columnsDefs = new Map(); + this.columns.forEach((column) => { + const columnId = column.tableData.id; + if (collectionIds.includes(columnId)) { + columnsDefs.set(columnId, column); + } + }); + const sort = this.sort; const getFieldValue = this.getFieldValue; + const orderByCollection = this.orderByCollection; return list.sort(function sortData( a, b, columns = columnsDefs, - collection = orderCollection + collection = orderByCollection ) { const { orderBy, orderDirection } = collection[0]; @@ -822,21 +828,6 @@ export default class DataManager { }); } - sortList(list) { - const collectionIds = this.orderByCollection.map( - (collection) => collection.orderBy - ); - const columnsDefs = new Map(); - this.columns.forEach((column) => { - const columnId = column.tableData.id; - if (collectionIds.includes(columnId)) { - columnsDefs.set(columnId, column); - } - }); - - return this.sortBy(list, columnsDefs, this.orderByCollection); - } - getRenderState = () => { if (this.filtered === false) { this.filterData(); @@ -1282,9 +1273,12 @@ export default class DataManager { element.groupsIndex = getGroupsIndex(element.groups); sortGroupData(element.groups, level + 1); } else { - if (this.maxColumnSort > 0 && this.orderByCollection.length) { + if ( + this.maxColumnSort > 0 && + this.getOrderByCollection().length > 0 + ) { element.data = this.sortList(element.data); - } else if (!this.orderByCollection.length) { + } else if (this.maxColumnSort > 0) { element.data = element.data.sort((a, b) => { return ( this.data.findIndex( @@ -1303,7 +1297,7 @@ export default class DataManager { sortGroupData(this.sortedData, 1); } else if (this.isDataType('tree')) { this.sortedData = [...this.treefiedData]; - if (this.maxColumnSort > 0 && this.orderByCollection.length) { + if (this.maxColumnSort > 0 && this.getOrderByCollection().length > 0) { this.sortedData = this.sortList(this.sortedData); const sortTree = (list) => { @@ -1323,7 +1317,7 @@ export default class DataManager { this.sortedData = [...this.searchedData]; if ( this.maxColumnSort > 0 && - this.getOrderByCollection().length && + this.getOrderByCollection().length > 0 && this.applySort ) { this.sortedData = this.sortList(this.sortedData);