From af5de98f29b3cca3e60045a4a6ea0adbeb07c28c Mon Sep 17 00:00:00 2001 From: Patrick Gillespie Date: Tue, 10 Sep 2019 00:10:31 -0400 Subject: [PATCH] Filter Popover Options updates --- README.md | 10 +- package.json | 2 +- src/MUIDataTable.js | 6 +- src/components/TableFilter.js | 125 +++++++++++++++++------ src/components/TableToolbar.js | 4 +- src/textLabels.js | 1 + test/MUIDataTable.test.js | 6 +- test/MUIDataTableFilter.test.js | 176 +++++++++++++++++++++----------- 8 files changed, 225 insertions(+), 105 deletions(-) diff --git a/README.md b/README.md index a28599c..84ee159 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,3 @@ -
- -
- # MUI-DT - Datatables for Material-UI [![Build Status](https://travis-ci.org/patorjk/mui-dt.svg?branch=master)](https://travis-ci.org/patorjk/mui-dt) @@ -159,6 +155,7 @@ The component accepts the following props: |**`onColumnViewChange`**|function||Callback function that triggers when a column view has been changed. `function(changedColumn: string, action: string) => void` |**`onDownload`**|function||A callback function that triggers when the user downloads the CSV file. In the callback, you can control what is written to the CSV file. `function(buildHead: (columns) => string, buildBody: (data) => string, columns, data) => string`. Return `false` to cancel download of file. |**`onFilterChange`**|function||Callback function that triggers when filters have changed. `function(changedColumnName: string, filterList: array, changedColumnIndex: number) => void` +|**`onFilterConfirm`**|function||Callback function that is triggered when a user presses the "confirm" button on the filter popover. This occurs only if you've set filterPopoverOptions.mustConfirm option to true. `function(filterList: array) => void` |**`onRowClick`**|function||Callback function that triggers when a row is clicked. `function(rowData: string[], rowMeta: { dataIndex: number, rowIndex: number }) => void` |**`onRowsDelete`**|function||Callback function that triggers when row(s) are deleted. `function(rowsDeleted: object(lookup: {[dataIndex]: boolean}, data: arrayOfObjects: {index: number, dataIndex: number})) => void OR false` (Returning `false` prevents row deletion.) |**`onRowsExpand`**|function||Callback function that triggers when a row is expanded or collapsed. The rowsExpanded parameter can be used to save off the value for options.rowsExpanded for state changes. `function(affectedRows: array, allRowsExpanded: array, rowsExpanded: array) => void` [Example](https://github.com/patorjk/mui-dt/blob/master/examples/expandable-rows/index.js) @@ -383,6 +380,11 @@ const options = { } ``` +## Breaking Changes with mui-datatables +This library started as a fork of mui-datatables. I'm aiming to keep it similar enough so that someone could use this as a drop in replacement, however, the more I work on this library, the more it will evolve. Below I list list "breaking" changes with mui-datatables. + +* The "resetFilters" event that occurs for the onTableChange function is now called "clearFilters". (reason: with the addition of filterPopoverOptions and the ability to confirm filters, there needed to be a reset and a clear function. "reset" already existed and functioned as a "clear", so it was renamed) + ## Contributing Thanks for taking an interest in the library and the github community! diff --git a/package.json b/package.json index 71b0b53..6a9dbf6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mui-dt", - "version": "0.3.1", + "version": "0.4.0", "description": "Datatables for React using Material-UI", "main": "dist/index.js", "files": [ diff --git a/src/MUIDataTable.js b/src/MUIDataTable.js index cd89ae0..ebfcfad 100644 --- a/src/MUIDataTable.js +++ b/src/MUIDataTable.js @@ -963,7 +963,7 @@ class MUIDataTable extends React.Component { ); }; - resetFilters = () => { + clearFilters = () => { this.setState( prevState => { const filterList = prevState.columns.map(() => []); @@ -976,7 +976,7 @@ class MUIDataTable extends React.Component { }; }, () => { - this.setTableAction('resetFilters'); + this.setTableAction('clearFilters'); if (this.options.onFilterChange) { this.options.onFilterChange(null, this.state.filterList, null); } @@ -1344,7 +1344,7 @@ class MUIDataTable extends React.Component { filterPopoverOptions={this.options.filterPopoverOptions} filterUpdate={this.filterUpdate} options={this.options} - resetFilters={this.resetFilters} + clearFilters={this.clearFilters} searchText={searchText} searchTextUpdate={this.searchTextUpdate} setTableAction={this.setTableAction} diff --git a/src/components/TableFilter.js b/src/components/TableFilter.js index 007d652..8fa8fcc 100644 --- a/src/components/TableFilter.js +++ b/src/components/TableFilter.js @@ -40,19 +40,40 @@ export const useStyles = makeStyles(theme => ({ noMargin: { marginLeft: '0px', }, + actionButtons: { + '@media screen and (max-width: 800px)': { + marginTop: '14px', + }, + }, confirmButton: { - marginLeft: '24px', + marginLeft: '0px', fontSize: '12px', cursor: 'pointer', }, - reset: { + toolbar: { alignSelf: 'left', + display: 'flex', + justifyContent: 'space-between', + width:'100%', }, - resetLink: { - marginLeft: '16px', + withConfirm: { + '@media screen and (max-width: 800px)': { + display: 'block', + }, + }, + menuButtons: { + marginLeft: '8px', fontSize: '12px', cursor: 'pointer', + '@media screen and (max-width: 800px)': { + marginLeft: 0 + }, }, + normalToolbarButtons: { + marginLeft: '16px', + fontSize: '12px', + cursor: 'pointer', + }, filtersSelected: { alignSelf: 'right', }, @@ -99,7 +120,7 @@ function TableFilter(props) { const handleCheckboxChange = (index, value, column) => { filterUpdate(index, value, column, 'checkbox'); - if (props.filterPopoverOptions.mustConfirm === false) { + if (props.filterPopoverOptions.mustConfirm !== true) { props.onFilterUpdate(index, value, column, 'checkbox'); } }; @@ -110,7 +131,7 @@ function TableFilter(props) { filterUpdate(index, value, column, 'dropdown'); - if (props.filterPopoverOptions.mustConfirm === false) { + if (props.filterPopoverOptions.mustConfirm !== true) { props.onFilterUpdate(index, value, column, 'dropdown'); } }; @@ -118,7 +139,7 @@ function TableFilter(props) { const handleMultiselectChange = (index, value, column) => { filterUpdate(index, value, column, 'multiselect'); - if (props.filterPopoverOptions.mustConfirm === false) { + if (props.filterPopoverOptions.mustConfirm !== true) { props.onFilterUpdate(index, value, column, 'multiselect'); } }; @@ -126,7 +147,7 @@ function TableFilter(props) { const handleTextFieldChange = (event, index, column) => { filterUpdate(index, event.target.value, column, 'textField'); - if (props.filterPopoverOptions.mustConfirm === false) { + if (props.filterPopoverOptions.mustConfirm !== true) { props.onFilterUpdate(index, event.target.value, column, 'textField'); } }; @@ -134,7 +155,7 @@ function TableFilter(props) { const handleCustomChange = (value, index, column) => { filterUpdate(index, value, column.name, column.filterType); - if (props.filterPopoverOptions.mustConfirm === false) { + if (props.filterPopoverOptions.mustConfirm !== true) { props.onFilterUpdate(index, value, column.name, column.filterType); } }; @@ -278,19 +299,36 @@ function TableFilter(props) { props.onFilterUpdate(index, filter, columns[index].name, 'custom'); }); props.handleClose(); + + if (options.onFilterConfirm) { + options.onFilterConfirm(filterList); + } + }; + + // only available when filters need to be confirmed + const resetFilters = () => { + setFilterList(props.filterList.map(item => item.slice())); + }; + + const clearFilters = () => { + setFilterList(filterList.map(item => [])); + + if (props.filterPopoverOptions.mustConfirm !== true) { + props.onFilterClear(); + } }; - const { columns, options, onFilterReset } = props; + const { columns, options } = props; const textLabels = options.textLabels.filter; const filterGridColumns = columns.filter(col => col.filter).length === 1 ? 1 : 2; - console.log('TableFilter render'); - console.dir(props); - return (
-
+
{textLabels.title} - {props.filterPopoverOptions.mustConfirm && +
+ {props.filterPopoverOptions.mustConfirm && + <> + + + + } - } - +
@@ -349,8 +408,8 @@ TableFilter.propTypes = { filterList: PropTypes.array.isRequired, /** Options for the filter popover */ filterPopoverOptions: PropTypes.object.isRequired, - /** Callback to trigger filter reset */ - onFilterRest: PropTypes.func, + /** Callback to trigger filter clear */ + onFilterClear: PropTypes.func, /** Callback to trigger filter update */ onFilterUpdate: PropTypes.func, /** Options used to describe table */ diff --git a/src/components/TableToolbar.js b/src/components/TableToolbar.js index bb55a8a..086e827 100644 --- a/src/components/TableToolbar.js +++ b/src/components/TableToolbar.js @@ -192,7 +192,7 @@ class TableToolbar extends React.Component { filterPopoverOptions, filterUpdate, options, - resetFilters, + clearFilters, tableRef, title, toggleViewColumn, @@ -323,7 +323,7 @@ class TableToolbar extends React.Component { filterPopoverOptions={filterPopoverOptions} handleClose={closeFilterPopover} onFilterUpdate={filterUpdate} - onFilterReset={resetFilters} + onFilterClear={clearFilters} updateFilterByType={updateFilterByType} /> } diff --git a/src/textLabels.js b/src/textLabels.js index c92ebb4..0fb31c7 100644 --- a/src/textLabels.js +++ b/src/textLabels.js @@ -25,6 +25,7 @@ const textLabels = { all: 'All', title: 'FILTERS', reset: 'RESET', + clear: 'CLEAR', }, viewColumns: { title: 'Show Columns', diff --git a/test/MUIDataTable.test.js b/test/MUIDataTable.test.js index 08fbb2d..73ae0aa 100644 --- a/test/MUIDataTable.test.js +++ b/test/MUIDataTable.test.js @@ -655,7 +655,7 @@ describe('', function() { assert.deepEqual(state.filterList, [[], [], [], [], []]); }); - it('should properly reset internal filterList when calling resetFilters method', () => { + it('should properly reset internal filterList when calling clearFilters method', () => { // set a filter const shallowWrapper = shallow(); const table = shallowWrapper.dive(); @@ -667,7 +667,7 @@ describe('', function() { let state = table.state(); assert.deepEqual(state.filterList, [['Joe James'], [], [], [], []]); - instance.resetFilters(); + instance.clearFilters(); table.update(); state = table.state(); assert.deepEqual(state.filterList, [[], [], [], [], []]); @@ -704,7 +704,7 @@ describe('', function() { assert.strictEqual(changedColumn, 'Company'); // test reset filters - instance.resetFilters(); + instance.clearFilters(); table.update(); assert.strictEqual(changedColumn, null); }); diff --git a/test/MUIDataTableFilter.test.js b/test/MUIDataTableFilter.test.js index 7c2fcb2..574e4d0 100644 --- a/test/MUIDataTableFilter.test.js +++ b/test/MUIDataTableFilter.test.js @@ -41,7 +41,13 @@ describe('', function() { const options = { filterType: 'checkbox', textLabels }; const filterList = [[], [], [], []]; const shallowWrapper = mount( - , + {}} + filterPopoverOptions={{}}/>, ); const labels = shallowWrapper .find(Typography) @@ -54,7 +60,13 @@ describe('', function() { const options = { filterType: 'checkbox', textLabels }; const filterList = [[], [], [], []]; const shallowWrapper = mount( - , + {}} + filterPopoverOptions={{}} />, ); const actualResult = shallowWrapper.find(Checkbox); @@ -67,7 +79,13 @@ describe('', function() { columns = columns.map(item => (item.filter = false)); const mountWrapper = mount( - , + {}} + filterPopoverOptions={{}} />, ); const actualResult = mountWrapper.find(Checkbox); @@ -79,7 +97,13 @@ describe('', function() { const filterList = [['Joe James'], [], [], []]; const mountWrapper = mount( - , + {}} + filterPopoverOptions={{}} />, ); const actualResult = mountWrapper.find(Select); @@ -92,7 +116,13 @@ describe('', function() { columns = columns.map(item => (item.filter = false)); const mountWrapper = mount( - , + {}} + filterPopoverOptions={{}} />, ); const actualResult = mountWrapper.find(Select); @@ -104,7 +134,13 @@ describe('', function() { const filterList = [['Joe James', 'John Walsh'], [], [], []]; const mountWrapper = mount( - , + {}} + filterPopoverOptions={{}} />, ); const actualResult = mountWrapper.find(Select); @@ -129,7 +165,13 @@ describe('', function() { }; const filterList = [[], [], [], []]; const mountWrapper = mount( - , + {}} + filterPopoverOptions={{}} />, ); const actualResult = mountWrapper.find('#custom-filter-render'); @@ -140,7 +182,13 @@ describe('', function() { const options = { filterType: 'textField', textLabels }; const filterList = [[], [], [], []]; const shallowWrapper = mount( - , + {}} + filterPopoverOptions={{}} />, ); const labels = shallowWrapper .find(TextField) @@ -153,7 +201,13 @@ describe('', function() { const options = { filterType: 'textField', textLabels }; const filterList = [[], [], [], []]; const shallowWrapper = mount( - , + {}} + filterPopoverOptions={{}} />, ); const actualResult = shallowWrapper.find(TextField); @@ -166,7 +220,13 @@ describe('', function() { columns = columns.map(item => (item.filter = false)); const shallowWrapper = mount( - , + {}} + filterPopoverOptions={{}} />, ); const actualResult = shallowWrapper.find(TextField); @@ -179,126 +239,124 @@ describe('', function() { columns.forEach(item => (item.filterType = 'checkbox')); const shallowWrapper = mount( - , + {}} + filterPopoverOptions={{}} />, ); const actualResult = shallowWrapper.find(Checkbox); assert.strictEqual(actualResult.length, 13); }); - it('should trigger onFilterUpdate prop callback when calling method handleCheckboxChange', () => { + it('should trigger onFilterUpdate prop callback when calling method checkbox onchange triggered', () => { const options = { filterType: 'checkbox', textLabels }; const filterList = [[], [], [], []]; const onFilterUpdate = spy(); - const shallowWrapper = shallow( + const fullWrapper = mount( , - ).dive(); - const instance = shallowWrapper.instance(); + updateFilterByType={() => {}} + filterPopoverOptions={{}} />, + ); - //const event = { target: { value: 0 }}; - instance.handleCheckboxChange(0, 0); + fullWrapper.find('input[type="checkbox"]').at(0).simulate('change'); assert.strictEqual(onFilterUpdate.callCount, 1); }); - it('should trigger onFilterUpdate prop callback when calling method handleDropdownChange', () => { + it('should trigger onFilterUpdate prop callback when Select onChange event occurs for dropdown', () => { const options = { filterType: 'select', textLabels }; const filterList = [[], [], [], []]; const onFilterUpdate = spy(); - const shallowWrapper = shallow( + const fullWrapper = mount( , - ).dive(); - const instance = shallowWrapper.instance(); + updateFilterByType={() => {}} + filterPopoverOptions={{}} />, + ); - let event = { target: { value: 'All' } }; - instance.handleDropdownChange(event, 0); + let event = { target: { value: 'Joe James' } }; + fullWrapper.find(Select).at(0).props().onChange(event); assert.strictEqual(onFilterUpdate.callCount, 1); - event = { target: { value: 'test' } }; - instance.handleDropdownChange(event, 0); + event = { target: { value: 'All' } }; + fullWrapper.find(Select).at(0).props().onChange(event); assert.strictEqual(onFilterUpdate.callCount, 2); - shallowWrapper - .find(Select) - .first() - .simulate('change', event); + event = { target: { value: 'Joe James' } }; + fullWrapper.find(Select).at(0).props().onChange(event); assert.strictEqual(onFilterUpdate.callCount, 3); }); - it('should trigger onFilterUpdate prop callback when calling method handleMultiselectChange', () => { + it('should trigger onFilterUpdate prop callback when Select onChange event occurs for multi-select', () => { const options = { filterType: 'multiselect', textLabels }; const filterList = [[], [], [], []]; const onFilterUpdate = spy(); - const shallowWrapper = shallow( + const fullWrapper = mount( , - ).dive(); - const instance = shallowWrapper.instance(); - - let event = { target: { value: 'All' } }; + updateFilterByType={() => {}} + filterPopoverOptions={{}} />, + ); - instance.handleMultiselectChange(event, 0); + let event = { target: { value: 'Joe James' } }; + fullWrapper.find(Select).at(0).props().onChange(event); assert.strictEqual(onFilterUpdate.callCount, 1); - event = { target: { value: 'test' } }; - instance.handleMultiselectChange(event, 0); + event = { target: { value: 'All' } }; + fullWrapper.find(Select).at(0).props().onChange(event); assert.strictEqual(onFilterUpdate.callCount, 2); - shallowWrapper - .find(Select) - .first() - .simulate('change', event); + event = { target: { value: 'Joe James' } }; + fullWrapper.find(Select).at(0).props().onChange(event); assert.strictEqual(onFilterUpdate.callCount, 3); }); - it('should trigger onFilterUpdate prop callback when calling method handleTextFieldChange', () => { + it('should trigger onFilterUpdate prop callback when calling onChange for text filter', () => { const options = { filterType: 'textField', textLabels }; const filterList = [[], [], [], []]; const onFilterUpdate = spy(); - const shallowWrapper = shallow( + const fullWrapper = mount( , - ).dive(); - const instance = shallowWrapper.instance(); - - let event = { target: { value: 'All' } }; + updateFilterByType={() => {}} + filterPopoverOptions={{}} />, + ); - instance.handleTextFieldChange(event, 0); + let event = { target: { value: 'Joe James' } }; + fullWrapper.find(TextField).at(0).props().onChange(event); assert.strictEqual(onFilterUpdate.callCount, 1); - event = { target: { value: 'test' } }; - instance.handleTextFieldChange(event, 0); + event = { target: { value: '' } }; + fullWrapper.find(TextField).at(0).props().onChange(event); assert.strictEqual(onFilterUpdate.callCount, 2); - shallowWrapper - .find(TextField) - .first() - .simulate('change', event); + event = { target: { value: 'La la la' } }; + fullWrapper.find(TextField).at(0).props().onChange(event); assert.strictEqual(onFilterUpdate.callCount, 3); + }); });