From a0e675554bcfafcbe585f90fce76aedf3f2f0ae3 Mon Sep 17 00:00:00 2001 From: Gabriel Liwerant Date: Tue, 10 Sep 2019 01:45:47 -0400 Subject: [PATCH 01/12] Add onFilterDialogOpen callback when filter dialog opens --- README.md | 1 + src/MUIDataTable.js | 1 + src/components/TableToolbar.js | 7 +++++++ 3 files changed, 9 insertions(+) diff --git a/README.md b/README.md index 68f284fcd..652cb8567 100644 --- a/README.md +++ b/README.md @@ -180,6 +180,7 @@ The component accepts the following props: |**`onChangeRowsPerPage`**|function||Callback function that triggers when the number of rows per page has changed. `function(numberOfRows: number) => void` |**`onSearchChange`**|function||Callback function that triggers when the search text value has changed. `function(searchText: string) => void` |**`onSearchOpen`**|function||Callback function that triggers when the searchbox opens. `function() => void` +|**`onFilterDialogOpen`**|function||Callback function that triggers when the filter dialog opens. `function() => void` |**`onFilterChange`**|function||Callback function that triggers when filters have changed. `function(changedColumn: string, filterList: array) => void` |**`onColumnSortChange`**|function||Callback function that triggers when a column has been sorted. `function(changedColumn: string, direction: string) => void` |**`onColumnViewChange`**|function||Callback function that triggers when a column view has been changed. `function(changedColumn: string, action: string) => void` diff --git a/src/MUIDataTable.js b/src/MUIDataTable.js index 3d7eda621..a295bdbb9 100644 --- a/src/MUIDataTable.js +++ b/src/MUIDataTable.js @@ -149,6 +149,7 @@ class MUIDataTable extends React.Component { selectableRowsOnClick: PropTypes.bool, isRowSelectable: PropTypes.func, serverSide: PropTypes.bool, + onFilterDialogOpen: PropTypes.func, onTableChange: PropTypes.func, onTableInit: PropTypes.func, caseSensitive: PropTypes.bool, diff --git a/src/components/TableToolbar.js b/src/components/TableToolbar.js index 6711eca31..b86b1cca5 100644 --- a/src/components/TableToolbar.js +++ b/src/components/TableToolbar.js @@ -126,6 +126,13 @@ class TableToolbar extends React.Component { }; setActiveIcon = iconName => { + if (iconName === 'filter') { + this.props.setTableAction('onFilterDialogOpen'); + if (this.props.options.onFilterDialogOpen) { + this.props.options.onFilterDialogOpen(); + } + } + this.setState(() => ({ showSearch: this.isSearchShown(iconName), iconActive: iconName, From da2076bca5d550c6cf5ddf26e62e726b7265b596 Mon Sep 17 00:00:00 2001 From: Gabriel Liwerant Date: Tue, 10 Sep 2019 01:47:15 -0400 Subject: [PATCH 02/12] Add customFilterDialogFooter for adding a footer to filter dialog --- README.md | 1 + src/MUIDataTable.js | 1 + src/components/TableFilter.js | 3 ++- src/components/TableToolbar.js | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 652cb8567..30597ed0d 100644 --- a/README.md +++ b/README.md @@ -154,6 +154,7 @@ The component accepts the following props: |**`customSort`**|function||Override default sorting with custom function. `function(data: array, colIndex: number, order: string) => array` |**`customSearch `**|function||Override default search with custom function. `customSearch(searchQuery: string, currentRow: array, columns: array) => boolean` |**`customSearchRender `**|function||Render a custom table search. `customSearchRender(searchText: string, handleSearch, hideSearch, options) => React Component` +|**`customFilterDialogFooter `**|function||Add a custom footer to the filter dialog. `customFilterDialogFooter(filterList: array) => React Component` |**`elevation`**|number|4|Shadow depth applied to Paper component |**`caseSensitive `**|boolean|false|Enable/disable case sensitivity for search |**`responsive`**|string|'stacked'|Enable/disable responsive table views. Options: 'stacked', 'scrollMaxHeight' (limits height of table), 'scrollFullHeight' (table takes on as much height as needed to display all rows set in rowsPerPage) diff --git a/src/MUIDataTable.js b/src/MUIDataTable.js index a295bdbb9..310f69b79 100644 --- a/src/MUIDataTable.js +++ b/src/MUIDataTable.js @@ -142,6 +142,7 @@ class MUIDataTable extends React.Component { customFooter: PropTypes.oneOfType([PropTypes.func, PropTypes.element]), customSearchRender: PropTypes.oneOfType([PropTypes.func, PropTypes.element]), customRowRender: PropTypes.func, + customFilterDialogFooter: PropTypes.func, onRowClick: PropTypes.func, onRowsSelect: PropTypes.func, resizableColumns: PropTypes.bool, diff --git a/src/components/TableFilter.js b/src/components/TableFilter.js index 99e3697fb..924dfcb87 100644 --- a/src/components/TableFilter.js +++ b/src/components/TableFilter.js @@ -266,7 +266,7 @@ class TableFilter extends React.Component { } render() { - const { classes, columns, options, onFilterReset } = this.props; + const { classes, columns, options, onFilterReset, customFooter, filterList } = this.props; const textLabels = options.textLabels.filter; const filterGridColumns = columns.filter(col => col.filter).length === 1 ? 1 : 2; @@ -309,6 +309,7 @@ class TableFilter extends React.Component { } })} + {customFooter ? customFooter(filterList) : ''} ); } diff --git a/src/components/TableToolbar.js b/src/components/TableToolbar.js index b86b1cca5..6010349d0 100644 --- a/src/components/TableToolbar.js +++ b/src/components/TableToolbar.js @@ -306,6 +306,7 @@ class TableToolbar extends React.Component { } content={ Date: Tue, 10 Sep 2019 01:53:31 -0400 Subject: [PATCH 03/12] Allow filter chips update to trigger separate filter update type Add type to onFilterChange callback to allow differentiation among filter types, adding a "reset" type for filter reset --- README.md | 2 +- src/MUIDataTable.js | 8 ++++++-- src/components/TableFilterList.js | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 30597ed0d..1f0fdf65b 100644 --- a/README.md +++ b/README.md @@ -182,7 +182,7 @@ The component accepts the following props: |**`onSearchChange`**|function||Callback function that triggers when the search text value has changed. `function(searchText: string) => void` |**`onSearchOpen`**|function||Callback function that triggers when the searchbox opens. `function() => void` |**`onFilterDialogOpen`**|function||Callback function that triggers when the filter dialog opens. `function() => void` -|**`onFilterChange`**|function||Callback function that triggers when filters have changed. `function(changedColumn: string, filterList: array) => void` +|**`onFilterChange`**|function||Callback function that triggers when filters have changed. `function(changedColumn: string, filterList: array, type: enum('checkbox', 'dropdown', 'multiselect', 'textField', 'custom', 'chip', 'reset')) => void` |**`onColumnSortChange`**|function||Callback function that triggers when a column has been sorted. `function(changedColumn: string, direction: string) => void` |**`onColumnViewChange`**|function||Callback function that triggers when a column view has been changed. `function(changedColumn: string, action: string) => void` |**`onTableChange`**|function||Callback function that triggers when table state has changed. `function(action: string, tableState: object) => void` diff --git a/src/MUIDataTable.js b/src/MUIDataTable.js index 310f69b79..1a27d014f 100644 --- a/src/MUIDataTable.js +++ b/src/MUIDataTable.js @@ -150,6 +150,7 @@ class MUIDataTable extends React.Component { selectableRowsOnClick: PropTypes.bool, isRowSelectable: PropTypes.func, serverSide: PropTypes.bool, + onFilterChange: PropTypes.func, onFilterDialogOpen: PropTypes.func, onTableChange: PropTypes.func, onTableInit: PropTypes.func, @@ -940,7 +941,7 @@ class MUIDataTable extends React.Component { () => { this.setTableAction('resetFilters'); if (this.options.onFilterChange) { - this.options.onFilterChange(null, this.state.filterList); + this.options.onFilterChange(null, this.state.filterList, 'reset'); } }, ); @@ -956,6 +957,9 @@ class MUIDataTable extends React.Component { case 'checkbox': filterPos >= 0 ? filterList[index].splice(filterPos, 1) : filterList[index].push(value); break; + case 'chip': + filterPos >= 0 ? filterList[index].splice(filterPos, 1) : filterList[index].push(value); + break; case 'multiselect': filterList[index] = value === '' ? [] : value; break; @@ -978,7 +982,7 @@ class MUIDataTable extends React.Component { () => { this.setTableAction('filterChange'); if (this.options.onFilterChange) { - this.options.onFilterChange(column, this.state.filterList); + this.options.onFilterChange(column, this.state.filterList, type); } }, ); diff --git a/src/components/TableFilterList.js b/src/components/TableFilterList.js index 6866a23c3..a2454db89 100644 --- a/src/components/TableFilterList.js +++ b/src/components/TableFilterList.js @@ -55,7 +55,7 @@ class TableFilterList extends React.Component { )); From 4b2d5a0af1b0dda004e1123099a93478bf4c6ba9 Mon Sep 17 00:00:00 2001 From: Gabriel Liwerant Date: Tue, 10 Sep 2019 01:54:01 -0400 Subject: [PATCH 04/12] Minor enhancements --- README.md | 4 ++-- src/components/Popover.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1f0fdf65b..776043ed3 100644 --- a/README.md +++ b/README.md @@ -137,8 +137,8 @@ The component accepts the following props: |**`serverSide`**|boolean|false|Enable remote data source |**`rowsSelected`**|array||User provided selected rows |**`rowsExpanded`**|array||User provided expanded rows -|**`filterType `**|string||Choice of filtering view. `enum('checkbox', 'dropdown', 'multiselect', 'textField')` -|**`textLabels `**|object||User provided labels to localize text +|**`filterType`**|string||Choice of filtering view. `enum('checkbox', 'dropdown', 'multiselect', 'textField', 'custom')` +|**`textLabels`**|object||User provided labels to localize text |**`pagination`**|boolean|true|Enable/disable pagination |**`selectableRows`**|string|'multiple'|Numbers of rows that can be selected. Options are "multiple", "single", "none". |**`selectableRowsOnClick`**|boolean|false|Enable/disable select toggle when row is clicked. When False, only checkbox will trigger this action. diff --git a/src/components/Popover.js b/src/components/Popover.js index a91231683..f51d97c9b 100644 --- a/src/components/Popover.js +++ b/src/components/Popover.js @@ -78,7 +78,8 @@ class Popover extends React.Component { ref={el => this.popoverEl} anchorOrigin={anchorOriginSpecs} transformOrigin={transformOriginSpecs} - {...providedProps}> + {...providedProps} + > {content} {triggerEl} From 9491226775913adb695843d6425b61c240e653d4 Mon Sep 17 00:00:00 2001 From: Gabriel Liwerant Date: Tue, 10 Sep 2019 01:54:34 -0400 Subject: [PATCH 05/12] Add serverside filter example Uses all of the additional functionality to create a new filter experience for serverside use cases --- examples/serverside-filters/index.js | 191 +++++++++++++++++++++++++++ webpack.config.js | 2 +- 2 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 examples/serverside-filters/index.js diff --git a/examples/serverside-filters/index.js b/examples/serverside-filters/index.js new file mode 100644 index 000000000..23d836809 --- /dev/null +++ b/examples/serverside-filters/index.js @@ -0,0 +1,191 @@ +import { Button, CircularProgress } from '@material-ui/core'; +import React from 'react'; +import ReactDOM from 'react-dom'; +import MUIDataTable from '../../src'; + +class Example extends React.Component { + state = { + filters: [[], [], [], [], []], + isLoading: false, + data: [ + ['Gabby George', 'Business Analyst', 'Minneapolis', 30, '$100,000'], + ['Aiden Lloyd', 'Business Consultant', 'Dallas', 55, '$200,000'], + ['Jaden Collins', 'Attorney', 'Santa Ana', 27, '$500,000'], + ['Franky Rees', 'Business Analyst', 'St. Petersburg', 22, '$50,000'], + ['Aaren Rose', 'Business Consultant', 'Toledo', 28, '$75,000'], + ['Blake Duncan', 'Business Management Analyst', 'San Diego', 65, '$94,000'], + ['Frankie Parry', 'Agency Legal Counsel', 'Jacksonville', 71, '$210,000'], + ['Lane Wilson', 'Commercial Specialist', 'Chicago', 19, '$65,000'], + ['Robin Duncan', 'Business Analyst', 'Los Angeles', 20, '$77,000'], + ['Mel Brooks', 'Business Consultant', 'Oklahoma City', 37, '$135,000'], + ['Harper White', 'Attorney', 'Pittsburgh', 52, '$420,000'], + ['Kris Humphrey', 'Agency Legal Counsel', 'Laredo', 30, '$150,000'], + ['Frankie Long', 'Industrial Analyst', 'Austin', 31, '$170,000'], + ['Brynn Robbins', 'Business Analyst', 'Norfolk', 22, '$90,000'], + ['Justice Mann', 'Business Consultant', 'Chicago', 24, '$133,000'], + ['Addison Navarro', 'Business Management Analyst', 'New York', 50, '$295,000'], + ['Jesse Welch', 'Agency Legal Counsel', 'Seattle', 28, '$200,000'], + ['Eli Mejia', 'Commercial Specialist', 'Long Beach', 65, '$400,000'], + ['Gene Leblanc', 'Industrial Analyst', 'Hartford', 34, '$110,000'], + ['Danny Leon', 'Computer Scientist', 'Newark', 60, '$220,000'], + ['Lane Lee', 'Corporate Counselor', 'Cincinnati', 52, '$180,000'], + ['Jesse Hall', 'Business Analyst', 'Baltimore', 44, '$99,000'], + ['Danni Hudson', 'Agency Legal Counsel', 'Tampa', 37, '$90,000'], + ['Terry Macdonald', 'Commercial Specialist', 'Miami', 39, '$140,000'], + ['Justice Mccarthy', 'Attorney', 'Tucson', 26, '$330,000'], + ['Silver Carey', 'Computer Scientist', 'Memphis', 47, '$250,000'], + ['Franky Miles', 'Industrial Analyst', 'Buffalo', 49, '$190,000'], + ['Glen Nixon', 'Corporate Counselor', 'Arlington', 44, '$80,000'], + ['Gabby Strickland', 'Business Process Consultant', 'Scottsdale', 26, '$45,000'], + ['Mason Ray', 'Computer Scientist', 'San Francisco', 39, '$142,000'], + ] + }; + + // mock async function + xhrRequest = (url, filterList) => { + return new Promise(resolve => { + window.setTimeout( + () => { + const data = [ + ['Gabby George', 'Business Analyst', 'Minneapolis', 30, '$100,000'], + ['Aiden Lloyd', 'Business Consultant', 'Dallas', 55, '$200,000'], + ['Jaden Collins', 'Attorney', 'Santa Ana', 27, '$500,000'], + ['Franky Rees', 'Business Analyst', 'St. Petersburg', 22, '$50,000'], + ['Aaren Rose', 'Business Consultant', 'Toledo', 28, '$75,000'], + ['Blake Duncan', 'Business Management Analyst', 'San Diego', 65, '$94,000'], + ['Frankie Parry', 'Agency Legal Counsel', 'Jacksonville', 71, '$210,000'], + ['Lane Wilson', 'Commercial Specialist', 'Chicago', 19, '$65,000'], + ['Robin Duncan', 'Business Analyst', 'Los Angeles', 20, '$77,000'], + ['Mel Brooks', 'Business Consultant', 'Oklahoma City', 37, '$135,000'], + ['Harper White', 'Attorney', 'Pittsburgh', 52, '$420,000'], + ['Kris Humphrey', 'Agency Legal Counsel', 'Laredo', 30, '$150,000'], + ['Frankie Long', 'Industrial Analyst', 'Austin', 31, '$170,000'], + ['Brynn Robbins', 'Business Analyst', 'Norfolk', 22, '$90,000'], + ['Justice Mann', 'Business Consultant', 'Chicago', 24, '$133,000'], + ['Addison Navarro', 'Business Management Analyst', 'New York', 50, '$295,000'], + ['Jesse Welch', 'Agency Legal Counsel', 'Seattle', 28, '$200,000'], + ['Eli Mejia', 'Commercial Specialist', 'Long Beach', 65, '$400,000'], + ['Gene Leblanc', 'Industrial Analyst', 'Hartford', 34, '$110,000'], + ['Danny Leon', 'Computer Scientist', 'Newark', 60, '$220,000'], + ['Lane Lee', 'Corporate Counselor', 'Cincinnati', 52, '$180,000'], + ['Jesse Hall', 'Business Analyst', 'Baltimore', 44, '$99,000'], + ['Danni Hudson', 'Agency Legal Counsel', 'Tampa', 37, '$90,000'], + ['Terry Macdonald', 'Commercial Specialist', 'Miami', 39, '$140,000'], + ['Justice Mccarthy', 'Attorney', 'Tucson', 26, '$330,000'], + ['Silver Carey', 'Computer Scientist', 'Memphis', 47, '$250,000'], + ['Franky Miles', 'Industrial Analyst', 'Buffalo', 49, '$190,000'], + ['Glen Nixon', 'Corporate Counselor', 'Arlington', 44, '$80,000'], + ['Gabby Strickland', 'Business Process Consultant', 'Scottsdale', 26, '$45,000'], + ['Mason Ray', 'Computer Scientist', 'San Francisco', 39, '$142,000'], + ]; + const newData = [ + ['Lane Wilson', 'Commercial Specialist', 'Chicago', 19, '$65,000'], + ['Justice Mann', 'Business Consultant', 'Chicago', 24, '$133,000'] + ]; + + if ( + !filterList[0].length + && !filterList[1].length + && !filterList[2].length + && !filterList[3].length + && !filterList[4].length + ) { + resolve({ data }); + } else { + resolve({ data: newData }); + } + }, + 2000 + ); + }); + } + + handleFilterSubmit = filterList => () => { + console.log('Submitting filters: ', filterList); + + this.setState({ isLoading: true, filters: filterList }); + + // fake async request + this.xhrRequest(`/myApiServer?filters=${filterList}`, filterList).then(res => { + this.setState({ isLoading: false, data: res.data }); + }); + }; + + render() { + const columns = [ + { + name: 'Name', + options: { + filter: true, + filterList: this.state.filters[0], + }, + }, + { + label: 'Title', + name: 'Title', + options: { + filter: true, + filterList: this.state.filters[1], + }, + }, + { + name: 'Location', + options: { + filter: true, + filterList: this.state.filters[2], + }, + }, + { + name: 'Age', + options: { + filter: true, + filterList: this.state.filters[3], + }, + }, + { + name: 'Salary', + options: { + filter: true, + filterList: this.state.filters[4], + }, + }, + ]; + + const options = { + filter: true, + filterType: 'dropdown', + responsive: 'scrollMaxHeight', + serverSide: true, + onFilterDialogOpen: () => { + console.log('filter dialog opened'); + }, + onFilterChange: (column, filterList, type) => { + if (type === 'chip') { + console.log('updating filters via chip'); + this.handleFilterSubmit(filterList)(); + } + }, + customFilterDialogFooter: filterList => { + return ( +
+ +

*(Simulates selecting "Chicago" from "Location")

+
+ ); + } + }; + + return ( + + {this.state.isLoading && ( +
+ +
+ )} + +
+ ); + } +} + +ReactDOM.render(, document.getElementById('app-root')); diff --git a/webpack.config.js b/webpack.config.js index e507b2ab7..4ee199b6f 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -3,7 +3,7 @@ const webpack = require('webpack'); module.exports = { entry: { - app: "./examples/customize-search/index.js" + app: "./examples/serverside-filters/index.js" }, stats: "verbose", context: __dirname, From 0fbcdd01622db43db1dcc5f124a39f203c50e9c8 Mon Sep 17 00:00:00 2001 From: Gabriel Liwerant Date: Tue, 10 Sep 2019 04:21:14 -0400 Subject: [PATCH 06/12] Add tests for major functionality additions - filterList - customFilterDialogFooter - onFilterDialogOpen --- test/MUIDataTableFilter.test.js | 21 +++++++++++++++++++++ test/MUIDataTableToolbar.test.js | 14 ++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/test/MUIDataTableFilter.test.js b/test/MUIDataTableFilter.test.js index 7c2fcb204..c5b3cb67d 100644 --- a/test/MUIDataTableFilter.test.js +++ b/test/MUIDataTableFilter.test.js @@ -186,6 +186,27 @@ describe('', function() { assert.strictEqual(actualResult.length, 13); }); + it('should render a filter dialog with custom footer when customFooter is provided', () => { + const CustomFooter = () => ; + const options = { textLabels }; + const filterList = [[], [], [], []]; + const onFilterUpdate = spy(); + + const shallowWrapper = shallow( + , + ).dive(); + + const actualResult = shallowWrapper.find('#custom-footer'); + assert.strictEqual(actualResult.length, 1); + }); + it('should trigger onFilterUpdate prop callback when calling method handleCheckboxChange', () => { const options = { filterType: 'checkbox', textLabels }; const filterList = [[], [], [], []]; diff --git a/test/MUIDataTableToolbar.test.js b/test/MUIDataTableToolbar.test.js index 32405edad..a1f708e65 100644 --- a/test/MUIDataTableToolbar.test.js +++ b/test/MUIDataTableToolbar.test.js @@ -182,6 +182,20 @@ describe('', function() { assert.strictEqual(actualResult.length, 0); }); + it('should call onFilterDialogOpen when opening filters via toolbar', () => { + const onFilterDialogOpen = spy(); + const newOptions = { ...options, onFilterDialogOpen }; + const shallowWrapper = shallow( + , + ).dive(); + const instance = shallowWrapper.instance(); + + instance.setActiveIcon('filter'); + shallowWrapper.update(); + + assert.strictEqual(onFilterDialogOpen.callCount, 1); + }); + it('should set icon when calling method setActiveIcon', () => { const shallowWrapper = shallow( , From 8d0680f54275725275c6e9446f87b2bd0186c7f0 Mon Sep 17 00:00:00 2001 From: Gabriel Liwerant Date: Tue, 10 Sep 2019 20:35:03 -0400 Subject: [PATCH 07/12] Fire onFilterDialogOpen after setState for consistency with other callbacks --- src/components/TableToolbar.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/components/TableToolbar.js b/src/components/TableToolbar.js index 6010349d0..346dfc270 100644 --- a/src/components/TableToolbar.js +++ b/src/components/TableToolbar.js @@ -126,17 +126,19 @@ class TableToolbar extends React.Component { }; setActiveIcon = iconName => { - if (iconName === 'filter') { - this.props.setTableAction('onFilterDialogOpen'); - if (this.props.options.onFilterDialogOpen) { - this.props.options.onFilterDialogOpen(); - } - } - - this.setState(() => ({ + this.setState({ showSearch: this.isSearchShown(iconName), iconActive: iconName, - })); + }, () => { + const { iconActive } = this.state; + + if (iconActive === 'filter') { + this.props.setTableAction('onFilterDialogOpen'); + if (this.props.options.onFilterDialogOpen) { + this.props.options.onFilterDialogOpen(); + } + } + }); }; isSearchShown = iconName => { From ccedb551075e7b914107df8124063093251c210f Mon Sep 17 00:00:00 2001 From: Gabriel Liwerant Date: Tue, 10 Sep 2019 20:35:37 -0400 Subject: [PATCH 08/12] Add serverSideFilterList for chip display with serverside filters --- README.md | 1 + examples/serverside-filters/index.js | 4 ++- src/MUIDataTable.js | 3 ++ src/components/TableFilterList.js | 48 +++++++++++++++++----------- 4 files changed, 37 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 776043ed3..8d5591519 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,7 @@ The component accepts the following props: |**`page`**|number||User provided starting page for pagination |**`count`**|number||User provided override for total number of rows |**`serverSide`**|boolean|false|Enable remote data source +|**`serverSideFilterList`**|array|[]|Sets the filter list display when using serverSide: true |**`rowsSelected`**|array||User provided selected rows |**`rowsExpanded`**|array||User provided expanded rows |**`filterType`**|string||Choice of filtering view. `enum('checkbox', 'dropdown', 'multiselect', 'textField', 'custom')` diff --git a/examples/serverside-filters/index.js b/examples/serverside-filters/index.js index 23d836809..54cce1dc3 100644 --- a/examples/serverside-filters/index.js +++ b/examples/serverside-filters/index.js @@ -5,6 +5,7 @@ import MUIDataTable from '../../src'; class Example extends React.Component { state = { + serverSideFilterList: [], filters: [[], [], [], [], []], isLoading: false, data: [ @@ -107,7 +108,7 @@ class Example extends React.Component { // fake async request this.xhrRequest(`/myApiServer?filters=${filterList}`, filterList).then(res => { - this.setState({ isLoading: false, data: res.data }); + this.setState({ isLoading: false, data: res.data, serverSideFilterList: filterList }); }); }; @@ -153,6 +154,7 @@ class Example extends React.Component { const options = { filter: true, + serverSideFilterList: this.state.serverSideFilterList, filterType: 'dropdown', responsive: 'scrollMaxHeight', serverSide: true, diff --git a/src/MUIDataTable.js b/src/MUIDataTable.js index 1a27d014f..ba32d6e1f 100644 --- a/src/MUIDataTable.js +++ b/src/MUIDataTable.js @@ -280,6 +280,7 @@ class MUIDataTable extends React.Component { filterType: 'dropdown', pagination: true, textLabels, + serverSideFilterList: [], expandableRows: false, expandableRowsOnClick: false, resizableColumns: false, @@ -1218,6 +1219,7 @@ class MUIDataTable extends React.Component { previousSelectedRow, expandedRows, searchText, + serverSideFilterList, } = this.state; const rowCount = this.state.count || displayData.length; @@ -1277,6 +1279,7 @@ class MUIDataTable extends React.Component { )} { return c.customFilterListRender ? c.customFilterListRender : f => f; })} diff --git a/src/components/TableFilterList.js b/src/components/TableFilterList.js index a2454db89..61d3c0bcd 100644 --- a/src/components/TableFilterList.js +++ b/src/components/TableFilterList.js @@ -35,30 +35,42 @@ class TableFilterList extends React.Component { }; render() { - const { classes, filterList, filterUpdate, filterListRenderers, columnNames } = this.props; + const { classes, filterList, filterUpdate, filterListRenderers, columnNames, serverSideFilterList } = this.props; + const { serverSide } = this.props.options; + + const customFilterChip = (item, index) => ( + + ); + + const filterChip = (index, data, colIndex) => ( + + ); return (
- {filterList.map((item, index) => { + {serverSide ? serverSideFilterList.map((item, index) => { + if (columnNames[index].filterType === 'custom' && filterListRenderers[index](item)) { + return customFilterChip(item, index); + } + + return item.map((data, colIndex) => filterChip(index, data, colIndex)); + }) : + filterList.map((item, index) => { if (columnNames[index].filterType === 'custom' && filterListRenderers[index](item)) { - return ( - - ); + return customFilterChip(item, index); } - return item.map((data, colIndex) => ( - - )); + return item.map((data, colIndex) => filterChip(index, data, colIndex)); })}
); From 9b5e314b37746438be285d713b0c885e5dff8163 Mon Sep 17 00:00:00 2001 From: Gabriel Liwerant Date: Tue, 10 Sep 2019 20:35:49 -0400 Subject: [PATCH 09/12] Add tests for serverSideFilterList --- test/MUIDataTable.test.js | 51 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/test/MUIDataTable.test.js b/test/MUIDataTable.test.js index 7d4bed0a1..e7c134d80 100644 --- a/test/MUIDataTable.test.js +++ b/test/MUIDataTable.test.js @@ -606,6 +606,7 @@ describe('', function() { const mountWrapper = mount( true} @@ -627,6 +628,7 @@ describe('', function() { const mountWrapper = mount( true} @@ -638,6 +640,55 @@ describe('', function() { assert.strictEqual(actualResult.prop('label'), 'Name: Joe James'); }); + it('should render filter Chip(s) when options.serverSide = true and serverSideFilterList is populated', () => { + const serverSideFilterList = [['Joe James'], [], [], [], []]; + const filterListRenderers = [ + defaultRenderCustomFilterList, + defaultRenderCustomFilterList, + defaultRenderCustomFilterList, + defaultRenderCustomFilterList, + defaultRenderCustomFilterList, + ]; + const columnNames = columns.map(column => ({ name: column.name })); + + const mountWrapper = mount( + true} + columnNames={columnNames} + />, + ); + + const actualResult = mountWrapper.find(Chip); + assert.strictEqual(actualResult.length, 1); + }); + + it('should not render filter Chip(s) when options.serverSide = true and serverSideFilterList is not populated', () => { + const filterListRenderers = [ + defaultRenderCustomFilterList, + defaultRenderCustomFilterList, + defaultRenderCustomFilterList, + defaultRenderCustomFilterList, + defaultRenderCustomFilterList, + ]; + const columnNames = columns.map(column => ({ name: column.name })); + + const mountWrapper = mount( + true} + columnNames={columnNames} + />, + ); + + const actualResult = mountWrapper.find(Chip); + assert.strictEqual(actualResult.length, 0); + }); + it('should remove entry from filterList when calling filterUpdate method with type=dropdown and same arguments a second time', () => { const shallowWrapper = shallow(); const table = shallowWrapper.dive(); From f1a988aedd9b44be019b1da6824782d6dd264d99 Mon Sep 17 00:00:00 2001 From: Gabriel Liwerant Date: Tue, 10 Sep 2019 20:58:06 -0400 Subject: [PATCH 10/12] Add onFilterDialogClose callback option --- README.md | 1 + examples/serverside-filters/index.js | 3 +++ src/MUIDataTable.js | 1 + src/components/TableToolbar.js | 13 ++++++++++--- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 8d5591519..6763862e4 100644 --- a/README.md +++ b/README.md @@ -183,6 +183,7 @@ The component accepts the following props: |**`onSearchChange`**|function||Callback function that triggers when the search text value has changed. `function(searchText: string) => void` |**`onSearchOpen`**|function||Callback function that triggers when the searchbox opens. `function() => void` |**`onFilterDialogOpen`**|function||Callback function that triggers when the filter dialog opens. `function() => void` +|**`onFilterDialogClose`**|function||Callback function that triggers when the filter dialog closes. `function() => void` |**`onFilterChange`**|function||Callback function that triggers when filters have changed. `function(changedColumn: string, filterList: array, type: enum('checkbox', 'dropdown', 'multiselect', 'textField', 'custom', 'chip', 'reset')) => void` |**`onColumnSortChange`**|function||Callback function that triggers when a column has been sorted. `function(changedColumn: string, direction: string) => void` |**`onColumnViewChange`**|function||Callback function that triggers when a column view has been changed. `function(changedColumn: string, action: string) => void` diff --git a/examples/serverside-filters/index.js b/examples/serverside-filters/index.js index 54cce1dc3..9973954a8 100644 --- a/examples/serverside-filters/index.js +++ b/examples/serverside-filters/index.js @@ -161,6 +161,9 @@ class Example extends React.Component { onFilterDialogOpen: () => { console.log('filter dialog opened'); }, + onFilterDialogClose: () => { + console.log('filter dialog closed'); + }, onFilterChange: (column, filterList, type) => { if (type === 'chip') { console.log('updating filters via chip'); diff --git a/src/MUIDataTable.js b/src/MUIDataTable.js index ba32d6e1f..ed4a11fbf 100644 --- a/src/MUIDataTable.js +++ b/src/MUIDataTable.js @@ -152,6 +152,7 @@ class MUIDataTable extends React.Component { serverSide: PropTypes.bool, onFilterChange: PropTypes.func, onFilterDialogOpen: PropTypes.func, + onFilterDialogClose: PropTypes.func, onTableChange: PropTypes.func, onTableInit: PropTypes.func, caseSensitive: PropTypes.bool, diff --git a/src/components/TableToolbar.js b/src/components/TableToolbar.js index 346dfc270..5efdc9687 100644 --- a/src/components/TableToolbar.js +++ b/src/components/TableToolbar.js @@ -126,11 +126,12 @@ class TableToolbar extends React.Component { }; setActiveIcon = iconName => { - this.setState({ + this.setState(prevState => ({ showSearch: this.isSearchShown(iconName), iconActive: iconName, - }, () => { - const { iconActive } = this.state; + prevIconActive: prevState.iconActive + }), () => { + const { iconActive, prevIconActive } = this.state; if (iconActive === 'filter') { this.props.setTableAction('onFilterDialogOpen'); @@ -138,6 +139,12 @@ class TableToolbar extends React.Component { this.props.options.onFilterDialogOpen(); } } + if (iconActive === undefined && prevIconActive === 'filter') { + this.props.setTableAction('onFilterDialogClose'); + if (this.props.options.onFilterDialogClose) { + this.props.options.onFilterDialogClose(); + } + } }); }; From 0cc2ddfa449216ac9b22f2df47926407d71d4c30 Mon Sep 17 00:00:00 2001 From: Gabriel Liwerant Date: Tue, 10 Sep 2019 20:58:17 -0400 Subject: [PATCH 11/12] Add test for onFilterDialogClose --- test/MUIDataTableToolbar.test.js | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/test/MUIDataTableToolbar.test.js b/test/MUIDataTableToolbar.test.js index a1f708e65..ffd1e7c64 100644 --- a/test/MUIDataTableToolbar.test.js +++ b/test/MUIDataTableToolbar.test.js @@ -196,6 +196,22 @@ describe('', function() { assert.strictEqual(onFilterDialogOpen.callCount, 1); }); + it('should call onFilterDialogClose when closing filters dialog', () => { + const onFilterDialogClose = spy(); + const newOptions = { ...options, onFilterDialogClose }; + const shallowWrapper = shallow( + , + ).dive(); + const instance = shallowWrapper.instance(); + + instance.setActiveIcon('filter'); + shallowWrapper.update(); + instance.setActiveIcon(undefined); + shallowWrapper.update(); + + assert.strictEqual(onFilterDialogClose.callCount, 1); + }); + it('should set icon when calling method setActiveIcon', () => { const shallowWrapper = shallow( , From 270578b30de5b58a8f2b1cc857afbe2201d816bb Mon Sep 17 00:00:00 2001 From: Gabriel Liwerant Date: Thu, 3 Oct 2019 01:19:15 -0400 Subject: [PATCH 12/12] Add serverside filter example to new router setup --- examples/examples.js | 2 ++ examples/serverside-filters/index.js | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/examples.js b/examples/examples.js index 59972ed02..0a2619973 100644 --- a/examples/examples.js +++ b/examples/examples.js @@ -22,6 +22,7 @@ import OnDownload from "./on-download"; import OnTableInit from "./on-table-init"; import ResizableColumns from "./resizable-columns"; import SelectableRows from "./selectable-rows"; +import ServerSideFilters from "./serverside-filters"; import ServerSideOptions from "./serverside-options"; import ServerSidePagination from "./serverside-pagination"; import Simple from "./simple"; @@ -57,6 +58,7 @@ export default { 'OnTableInit': OnTableInit, 'resizableColumns': ResizableColumns, 'selectableRows': SelectableRows, + 'ServerSide Filters': ServerSideFilters, 'serverSide Options': ServerSideOptions, 'serverSide Pagination': ServerSidePagination, 'Simple': Simple, diff --git a/examples/serverside-filters/index.js b/examples/serverside-filters/index.js index 9973954a8..6ebfaf756 100644 --- a/examples/serverside-filters/index.js +++ b/examples/serverside-filters/index.js @@ -193,4 +193,4 @@ class Example extends React.Component { } } -ReactDOM.render(, document.getElementById('app-root')); +export default Example;