Skip to content

Commit

Permalink
Feature/issue 937 array and custom update filter support (#1067)
Browse files Browse the repository at this point in the history
* Add ability for custom filter list render to use arrays

* Update example to demonstrate new feature

* Rename arguments for clarity

* Add prop type check and deprecation notice

* Allow default filter update when not specified in options

* Fix deprecation warnings in examples

* Improve deprecation verbage for responsive option

* Prettify files

* Update serverside filter list render to include new feature

* Update documentation

* Update tests with deprecation notices and future API

* Add test for custom filter update function call

* Pretiffy test file
  • Loading branch information
gabrielliwerant authored Nov 21, 2019
1 parent 29b9299 commit 4f9f2a4
Show file tree
Hide file tree
Showing 7 changed files with 224 additions and 46 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,8 @@ const columns = [
|**`viewColumns`**|boolean|true|Allow user to toggle column visibility through 'View Column' list
|**`filterList`**|array||Filter value list [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/column-filters/index.js)
|**`filterOptions`**|{names, logic, display}||With filter options, it's possible to use custom names for the filter fields [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/column-filters/index.js), custom filter logic [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/customize-filter/index.js), and custom rendering [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/customize-filter/index.js)
|**`customFilterListRender`**|function||Function that returns a string used as the chip label. `function(value) => string` [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/column-filters/index.js)
|**`customFilterListRender` DEPRECATED **|function||Function that returns a string or array of strings used as the chip label(s). `function(value) => string | arrayOfStrings` [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/column-filters/index.js)
|**`customFilterListOptions` **|{render: function, update: function}|| `render` returns a string or array of strings used as the chip label(s). `function(value) => string | arrayOfStrings`, `update` returns a `filterList (see above)` allowing for custom filter updates when removing the filter chip [Example](https://github.com/gregnb/mui-datatables/blob/master/examples/column-filters/index.js)
|**`filter`**|boolean|true|Display column in filter list
|**`filterType `**|string|'dropdown'|Choice of filtering view. Takes priority over global filterType option.`enum('checkbox', 'dropdown', 'multiselect', 'textField', 'custom')` Use 'custom' if you are supplying your own rendering via `filterOptions`.
|**`sort`**|boolean|true|Enable/disable sorting on column
Expand Down
10 changes: 5 additions & 5 deletions examples/column-filters/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Example extends React.Component {
options: {
filter: true,
filterList: ['Franky Miles'],
customFilterListRender: v => `Name: ${v}`,
customFilterListOptions: { render: v => `Name: ${v}` },
filterOptions: {
names: ['a', 'b', 'c', 'Business Analyst']
},
Expand All @@ -23,7 +23,7 @@ class Example extends React.Component {
options: {
filter: true,
filterList: ['Business Analyst'],
customFilterListRender: v => `Title: ${v}`,
customFilterListOptions: { render: v => `Title: ${v}` },
filterType: 'textField' // set filterType's at the column level
}
},
Expand All @@ -37,14 +37,14 @@ class Example extends React.Component {
name: "Age",
options: {
filter: true,
customFilterListRender: v => `Age: ${v}`,
customFilterListOptions: { render: v => `Age: ${v}` },
}
},
{
name: "Salary",
options: {
filter: true,
customFilterListRender: v => `Salary: ${v}`,
customFilterListOptions: { render: v => `Salary: ${v}` },
sort: false
}
}
Expand Down Expand Up @@ -87,7 +87,7 @@ class Example extends React.Component {
onFilterChange: (changedColumn, filterList) => {
console.log(changedColumn, filterList);
},
selectableRows: true,
selectableRows: 'multiple',
filterType: 'dropdown',
responsive: 'stacked',
rowsPerPage: 10,
Expand Down
8 changes: 4 additions & 4 deletions examples/column-options-update/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ class Example extends React.Component {
filter: true,
display: this.state.display[0],
filterList: filterList[0].length ? filterList[0] : null,
customFilterListRender: v => `Name: ${v}`,
customFilterListOptions: { render: v => `Name: ${v}` },
filterOptions: {
names: filterOptions
},
Expand All @@ -85,7 +85,7 @@ class Example extends React.Component {
display: this.state.display[1],
filter: true,
filterList: filterList[1].length ? filterList[1] : null,
customFilterListRender: v => `Title: ${v}`,
customFilterListOptions: { render: v => `Title: ${v}` },
filterType: 'textField' // set filterType's at the column level
}
},
Expand All @@ -103,7 +103,7 @@ class Example extends React.Component {
display: this.state.display[3],
filter: true,
filterList: filterList[3].length ? filterList[3] : null,
customFilterListRender: v => `Age: ${v}`,
customFilterListOptions: { render: v => `Age: ${v}` },
}
},
{
Expand All @@ -112,7 +112,7 @@ class Example extends React.Component {
display: this.state.display[4],
filter: true,
filterList: filterList[4].length ? filterList[4] : null,
customFilterListRender: v => `Salary: ${v}`,
customFilterListOptions: { render: v => `Salary: ${v}` },
sort: false
}
}
Expand Down
53 changes: 42 additions & 11 deletions examples/customize-filter/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { FormGroup, FormLabel, TextField } from '@material-ui/core';
import { FormGroup, FormLabel, TextField, Checkbox, FormControlLabel, Grid } from '@material-ui/core';
import React from 'react';
import MUIDataTable from '../../src';

class Example extends React.Component {
state = {
ageFilterChecked: false
};

render() {
const columns = [
{
Expand Down Expand Up @@ -32,15 +36,32 @@ class Example extends React.Component {
filter: true,
filterType: 'custom',
filterList: [25, 50],
customFilterListRender: v => {
if (v[0] && v[1]) {
return `Min Age: ${v[0]}, Max Age: ${v[1]}`;
} else if (v[0]) {
return `Min Age: ${v[0]}`;
} else if (v[1]) {
return `Max Age: ${v[1]}`;
}
return false;
customFilterListOptions: {
render: v => {
if (v[0] && v[1] && this.state.ageFilterChecked) {
return [`Min Age: ${v[0]}`, `Max Age: ${v[1]}`];
} else if (v[0] && v[1] && !this.state.ageFilterChecked) {
return `Min Age: ${v[0]}, Max Age: ${v[1]}`;
} else if (v[0]) {
return `Min Age: ${v[0]}`;
} else if (v[1]) {
return `Max Age: ${v[1]}`;
}
return false;
},
update: (filterList, filterPos, index) => {
console.log('customFilterListOnDelete: ', filterList, filterPos, index);

if (filterPos === 0) {
filterList[index].splice(filterPos, 1, '');
} else if (filterPos === 1) {
filterList[index].splice(filterPos, 1);
} else if (filterPos === -1) {
filterList[index] = [];
}

return filterList;
},
},
filterOptions: {
names: [],
Expand Down Expand Up @@ -76,6 +97,16 @@ class Example extends React.Component {
}}
style={{ width: '45%' }}
/>
<FormControlLabel
control={
<Checkbox
checked={this.state.ageFilterChecked}
onChange={event => this.setState({ ageFilterChecked: event.target.checked })}
/>
}
label="Separate Values"
style={{ marginLeft: '0px' }}
/>
</FormGroup>
</div>
),
Expand Down Expand Up @@ -139,7 +170,7 @@ class Example extends React.Component {

const options = {
filter: true,
filterType: 'dropdown',
filterType: 'multiselect',
responsive: 'scrollMaxHeight',
};

Expand Down
34 changes: 29 additions & 5 deletions src/MUIDataTable.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,12 @@ class MUIDataTable extends React.Component {
filterType: PropTypes.oneOf(['dropdown', 'checkbox', 'multiselect', 'textField', 'custom']),
customHeadRender: PropTypes.func,
customBodyRender: PropTypes.func,
customFilterListOptions: PropTypes.oneOfType([
PropTypes.shape({
render: PropTypes.func,
update: PropTypes.func,
}),
]),
customFilterListRender: PropTypes.func,
}),
}),
Expand Down Expand Up @@ -327,8 +333,16 @@ class MUIDataTable extends React.Component {
);
}
if (this.options.responsive === 'scroll') {
console.error('This option has been deprecated. It is being replaced by scrollMaxHeight');
console.error('The "scroll" responsive option has been deprecated. It is being replaced by "scrollMaxHeight"');
}

this.props.columns.map(c => {
if (c.options && c.options.customFilterListRender) {
console.error(
'The customFilterListRender option has been deprecated. It is being replaced by customFilterListOptions.render (Specify customFilterListOptions: { render: Function } in column options.)',
);
}
});
};

/*
Expand Down Expand Up @@ -997,10 +1011,10 @@ class MUIDataTable extends React.Component {
);
};

filterUpdate = (index, value, column, type) => {
filterUpdate = (index, value, column, type, customUpdate) => {
this.setState(
prevState => {
const filterList = prevState.filterList.slice(0);
let filterList = prevState.filterList.slice(0);
const filterPos = filterList[index].indexOf(value);

switch (type) {
Expand All @@ -1017,7 +1031,8 @@ class MUIDataTable extends React.Component {
filterList[index] = value;
break;
case 'custom':
filterList[index] = value;
if (customUpdate) filterList = customUpdate(filterList, filterPos, index);
else filterList[index] = value;
break;
default:
filterList[index] = filterPos >= 0 || value === '' ? [] : [value];
Expand Down Expand Up @@ -1359,7 +1374,16 @@ class MUIDataTable extends React.Component {
options={this.options}
serverSideFilterList={this.props.options.serverSideFilterList || []}
filterListRenderers={columns.map(c => {
return c.customFilterListRender ? c.customFilterListRender : f => f;
if (c.customFilterListOptions && c.customFilterListOptions.render) return c.customFilterListOptions.render;
// DEPRECATED: This option is being replaced with customFilterListOptions.render
if (c.customFilterListRender) return c.customFilterListRender;

return f => f;
})}
customFilterListUpdate={columns.map(c => {
return c.customFilterListOptions && c.customFilterListOptions.update
? c.customFilterListOptions.update
: null;
})}
filterList={filterList}
filterUpdate={this.filterUpdate}
Expand Down
95 changes: 82 additions & 13 deletions src/components/TableFilterList.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,58 @@ class TableFilterList extends React.Component {
};

render() {
const { classes, filterList, filterUpdate, filterListRenderers, columnNames, serverSideFilterList } = this.props;
const {
classes,
filterList,
filterUpdate,
filterListRenderers,
columnNames,
serverSideFilterList,
customFilterListUpdate,
} = this.props;
const { serverSide } = this.props.options;

const customFilterChip = (item, index) => (
<Chip
label={filterListRenderers[index](item)}
key={index}
onDelete={filterUpdate.bind(null, index, [], columnNames[index].name, columnNames[index].filterType)}
className={classes.chip}
/>
);
const customFilterChipMultiValue = (customFilterItem, index, customFilterItemIndex, item, orig) => {
let label = '';
const type = customFilterListUpdate[index] ? 'custom' : 'chip';

if (Array.isArray(orig)) label = filterListRenderers[customFilterItemIndex](customFilterItem);
else label = filterListRenderers[index](item);

return (
<Chip
label={label}
key={customFilterItemIndex}
onDelete={filterUpdate.bind(
null,
index,
item[customFilterItemIndex],
columnNames[index].name,
type,
customFilterListUpdate[index],
)}
className={classes.chip}
/>
);
};

const customFilterChipSingleValue = (index, item) => {
return (
<Chip
label={filterListRenderers[index](item)}
key={index}
onDelete={filterUpdate.bind(
null,
index,
[],
columnNames[index].name,
columnNames[index].filterType,
customFilterListUpdate[index],
)}
className={classes.chip}
/>
);
};

const filterChip = (index, data, colIndex) => (
<Chip
Expand All @@ -60,15 +101,43 @@ class TableFilterList extends React.Component {
<div className={classes.root}>
{serverSide
? serverSideFilterList.map((item, index) => {
if (columnNames[index].filterType === 'custom' && filterListRenderers[index](item)) {
return customFilterChip(item, index);
const filterListRenderersValue = filterListRenderers[index](item);

if (columnNames[index].filterType === 'custom' && filterListRenderersValue) {
if (Array.isArray(filterListRenderersValue)) {
return filterListRenderersValue.map((customFilterItem, customFilterItemIndex) =>
customFilterChipMultiValue(
customFilterItem,
index,
customFilterItemIndex,
item,
filterListRenderersValue,
),
);
} else {
return customFilterChipSingleValue(index, item);
}
}

return item.map((data, colIndex) => filterChip(index, data, colIndex));
})
: filterList.map((item, index) => {
if (columnNames[index].filterType === 'custom' && filterListRenderers[index](item)) {
return customFilterChip(item, index);
const customFilterListRenderersValue = filterListRenderers[index](item);

if (columnNames[index].filterType === 'custom' && customFilterListRenderersValue) {
if (Array.isArray(customFilterListRenderersValue)) {
return customFilterListRenderersValue.map((customFilterItem, customFilterItemIndex) =>
customFilterChipMultiValue(
customFilterItem,
index,
customFilterItemIndex,
item,
customFilterListRenderersValue,
),
);
} else {
return customFilterChipSingleValue(index, item);
}
}

return item.map((data, colIndex) => filterChip(index, data, colIndex));
Expand Down
Loading

0 comments on commit 4f9f2a4

Please sign in to comment.