Add filterRender column option to customize the filter that is shown (#162)

* Add filterRender column option to completely customize the filter that is shown

* Refactor filterRender as a defaultProp.
This commit is contained in:
Aaron Schwartz 2017-03-31 14:41:57 -07:00 committed by Tanner Linsley
parent e8ebf7c780
commit cef7fbd780
10 changed files with 144 additions and 63 deletions

View File

@ -14,4 +14,5 @@ h1 {
p {
margin-bottom: 15px;
line-height: 22px;
}

View File

@ -229,7 +229,16 @@ These are all of the available props (and their default values) for the main `<R
footerStyle: {},
getFooterProps: () => ({}),
filterMethod: undefined,
hideFilter: false
hideFilter: false,
filterRender: ({filter, onFilterChange}) => (
<input type='text'
style={{
width: '100%'
}}
value={filter ? filter.value : ''}
onChange={(event) => onFilterChange(event.target.value)}
/>
)
},
// Text
@ -303,7 +312,7 @@ Or just define them as props
columns: [...], // See Header Groups section below
// Footer
footer: 'Header Name' or JSX eg. ({data, column}) => <div>Header Name</div>,
footer: 'Footer Name' or JSX eg. ({data, column}) => <div>Footer Name</div>,
footerClassName: '', // Set the classname of the `td` element of the column's footer
footerStyle: {}, // Set the style of the `td` element of the column's footer
getFooterProps: (state, rowInfo, column, instance) => ({}), // A function that returns props to decorate the `td` element of the column's footer
@ -313,7 +322,8 @@ Or just define them as props
// filter == an object specifying which filter is being applied. Format: {id: [the filter column's id], value: [the value the user typed in the filter field], pivotId: [if filtering on a pivot column, the pivotId will be set to the pivot column's id and the `id` field will be set to the top level pivoting column]}
// row == the row of data supplied to the table
// column == the column that the filter is on
hideFilter: false // If `showFilters` is set on the table, this option will let you selectively hide the filter on a particular row
hideFilter: false, // If `showFilters` is set on the table, this option will let you selectively hide the filter on a particular row
filterRender: JSX // eg. ({filter, onFilterChange}) => <select onChange={event => onFilterChange(event.target.value)} value={filter ? filter.value : ''}></select> // The value passed to onFilterChange will be the value passed to filter.value of the filterMethod
}]
```
@ -622,7 +632,7 @@ Here are the props and their corresponding callbacks that control the state of t
onPageSizeChange={(pageSize, pageIndex) => {...}} // Called when the pageSize is changed by the user. The resolve page is also sent to maintain approximate position in the data
onSortingChange={(column, shiftKey) => {...}} // Called when a sortable column header is clicked with the column itself and if the shiftkey was held. If the column is a pivoted column, `column` will be an array of columns
onExpandRow={(index, event) => {...}} // Called when an expander is clicked. Use this to manage `expandedRows`
onFilteringChange={(column, event) => {...}} // Called when a user enters a value into a filter input field. The event is the onChange event of the input field.
onFilteringChange={(column, value) => {...}} // Called when a user enters a value into a filter input field or the value passed to the onFilterChange handler by the filterRender option.
/>
```
@ -675,6 +685,8 @@ By default the table tries to filter by checking if the row's value starts with
If you want to override a particular column's filtering method, you can set the `filterMethod` option on a column.
To completely override the filter that is shown, you can set the `filterRender` column option. Using this option you can specify the JSX that is shown. The option is passed an `onFilterChange` method which must be called with the the value that you wan't to pass to the `filterMethod` option whenever the filter has changed.
See <a href="http://react-table.js.org/?selectedKind=2.%20Demos&selectedStory=Custom%20Filtering&full=0&down=1&left=1&panelRight=0&downPanel=kadirahq%2Fstorybook-addon-actions%2Factions-panel" target="\_parent">Custom Filtering</a> demo for examples.
## Component Overrides

View File

@ -16,7 +16,7 @@
<body>
<div id="root"></div>
<div id="error-display"></div>
<script src="static/preview.1bf8468d20ff1a92c704.bundle.js"></script>
<script src="static/preview.2b73b4112ed711a12fa8.bundle.js"></script>
</body>
</html>

View File

@ -1 +0,0 @@
{"version":3,"file":"static/preview.1bf8468d20ff1a92c704.bundle.js","sources":["webpack:///static/preview.1bf8468d20ff1a92c704.bundle.js"],"mappings":"AAAA;AAkuDA;AAm/DA;AA+qFA;AAmmFA;AA07OA;AAuwFA;AAwtDA;AA+jEA;AA4uDA;AAi8DA;AA+lDA;AA4yDA;AA28CA;AAw7DA;AAooDA;AA67CA;AAiqDA;AAynEA;AAwxDA;AA+rCA;AA+mDA;AA2lDA;AAkqEA;AAs2DA;AA2zDA;AAo4CA;AAosFA;AAmjGA;AA6sDA;AAw2CA;AAkoCA;AAq8CA;AAu8CA;AAoDA;AA+jFA","sourceRoot":""}

View File

@ -0,0 +1 @@
{"version":3,"file":"static/preview.2b73b4112ed711a12fa8.bundle.js","sources":["webpack:///static/preview.2b73b4112ed711a12fa8.bundle.js"],"mappings":"AAAA;AAkuDA;AA8+DA;AA+qFA;AAmmFA;AA07OA;AAuwFA;AAwtDA;AA+jEA;AA4uDA;AAi8DA;AA+lDA;AA4yDA;AA28CA;AAw7DA;AAooDA;AA67CA;AAiqDA;AAynEA;AAwxDA;AA+rCA;AA+mDA;AA2lDA;AAkqEA;AAs2DA;AA2zDA;AAo4CA;AAitFA;AAmjGA;AA6sDA;AAi1CA;AAosCA;AAm2CA;AAs/CA;AA4HA;AA+jFA","sourceRoot":""}

View File

@ -104,7 +104,16 @@ export default {
footerStyle: {},
getFooterProps: emptyObj,
filterMethod: undefined,
hideFilter: false
hideFilter: false,
filterRender: ({filter, onFilterChange}) => (
<input type='text'
style={{
width: '100%'
}}
value={filter ? filter.value : ''}
onChange={(event) => onFilterChange(event.target.value)}
/>
)
},
// Text

View File

@ -417,16 +417,16 @@ export default React.createClass({
const filter = filtering.find(filter => filter.id === column.id && filter.pivotId === col.id)
pivotCols.push(
<span key={col.id}
style={{display: 'flex', alignContent: 'flex-end', flex: 1}}>
style={{flex: 1}}>
{!col.hideFilter ? (
<input type='text'
style={{
flex: 1,
width: 20
}}
value={filter ? filter.value : ''}
onChange={(event) => this.filterColumn(column, event, col)}
/>
_.normalizeComponent(col.filterRender,
{
col,
filter,
onFilterChange: (value) => (this.filterColumn(column, value, col))
},
defaults.column.filterRender
)
) : null}
</span>
)
@ -489,13 +489,14 @@ export default React.createClass({
{...rest}
>
{!column.hideFilter ? (
<input type='text'
style={{
width: `100%`
}}
value={filter ? filter.value : ''}
onChange={(event) => this.filterColumn(column, event)}
/>
_.normalizeComponent(column.filterRender,
{
column,
filter,
onFilterChange: (value) => (this.filterColumn(column, value))
},
defaults.column.filterRender
)
) : null}
</ThComponent>
)
@ -593,15 +594,15 @@ export default React.createClass({
{...rowInfo}
value={rowInfo.rowValues[pivotValKey]}
/>
) : <span>{row[pivotValKey]} ({rowInfo.subRows.length})</span>}
) : <span>{row[pivotValKey]} ({rowInfo.subRows.length})</span>}
</span>
) : SubComponent ? (
<span>
<ExpanderComponent
isExpanded={isExpanded}
/>
</span>
) : null}
) : SubComponent ? (
<span>
<ExpanderComponent
isExpanded={isExpanded}
/>
</span>
) : null}
</TdComponent>
)
}
@ -887,7 +888,7 @@ export default React.createClass({
style={paginationProps.style}
{...paginationProps.rest}
/>
) : null}
) : null}
{!pageRows.length && (
<NoDataComponent
{...noDataProps}

View File

@ -440,12 +440,12 @@ export default {
this.fireOnChange()
})
},
filterColumn (column, event, pivotColumn) {
filterColumn (column, value, pivotColumn) {
const {filtering} = this.getResolvedState()
const {onFilteringChange} = this.props
if (onFilteringChange) {
return onFilteringChange(column, event)
return onFilteringChange(column, value)
}
// Remove old filter first if it exists
@ -461,10 +461,10 @@ export default {
}
})
if (event.target.value !== '') {
if (value !== '') {
newFiltering.push({
id: column.id,
value: event.target.value,
value: value,
pivotId: pivotColumn ? pivotColumn.id : undefined
})
}

View File

@ -7,7 +7,7 @@ import ReactTable from '../src/index'
class Filtering extends React.Component {
constructor(props) {
constructor (props) {
super(props)
const data = _.map(_.range(5553), d => {
@ -33,10 +33,10 @@ class Filtering extends React.Component {
data: data
}
this.setTableOption = this.setTableOption.bind(this);
this.setTableOption = this.setTableOption.bind(this)
}
render() {
render () {
const columns = [{
header: 'Name',
columns: [{
@ -54,26 +54,51 @@ class Filtering extends React.Component {
columns: [{
header: 'Age',
accessor: 'age'
}]
}, {
header: 'Over 21',
accessor: 'age',
id: 'over',
render: ({value}) => (value >= 21 ? 'Yes' : 'No'),
filterMethod: (filter, row) => {
if (filter.value === 'all') {
return true
}
if (filter.value === 'true') {
return row[filter.id] >= 21
}
return row[filter.id] < 21
},
filterRender: ({filter, onFilterChange}) => (
<select
onChange={event => onFilterChange(event.target.value)}
style={{width: '100%'}}
value={filter ? filter.value : 'all'}>
<option value="all"></option>
<option value="true">Can Drink</option>
<option value="false">Can't Drink</option>
</select>
)
}
]
}]
return (
<div>
<div style={{float: "left"}}>
<div style={{float: 'left'}}>
<h1>Table Options</h1>
<table>
<tbody>
{
Object.keys(this.state.tableOptions).map(optionKey => {
const optionValue = this.state.tableOptions[optionKey];
const optionValue = this.state.tableOptions[optionKey]
return (
<tr key={optionKey}>
<td>{optionKey}</td>
<td style={{paddingLeft: 10, paddingTop: 5}}>
<input type="checkbox"
name={optionKey}
checked={optionValue}
onChange={this.setTableOption}
name={optionKey}
checked={optionValue}
onChange={this.setTableOption}
/>
</td>
</tr>
@ -122,21 +147,30 @@ class Filtering extends React.Component {
</div>
<div>
<h1>Custom Filters In This Example</h1>
<p>The default filter for all columns of a table if it is not specified in the configuration is set to match on values that start with the filter text. Example: age.startsWith("2").</p>
<p>This example overrides the default filter behavior by setting the <strong>defaultFilterMethod</strong> table option to match on values that are exactly equal to the filter text. Example: age == "23")</p>
<p>The default filter for all columns of a table if it is not specified in the configuration is set to match
on values that start with the filter text. Example: age.startsWith("2").</p>
<p>This example overrides the default filter behavior by setting
the <strong>defaultFilterMethod</strong> table option to match on values that are exactly equal to the
filter text. Example: age == "23")</p>
<p>Each column can also be customized with the column <strong>filterMethod</strong> option:</p>
<p>In this example the firstName column filters on the value starting with and ending with the filter value.</p>
<p>In this example the lastName column filters on the value including the filter value anywhere in its text.</p>
<p>In this example the firstName column filters on the value starting with and ending with the filter
value.</p>
<p>In this example the lastName column filters on the value including the filter value anywhere in its
text.</p>
<p>To completely override the filter that is shown, you can set the <strong>filterRender</strong> column
option. Using this option you can specify the JSX that is shown. The option is passed
an <strong>onFilterChange</strong> method that must be called with the value that you wan't to
pass to the <strong>filterMethod</strong> option whenever the filter has changed.</p>
</div>
<CodeHighlight>{() => this.getCode()}</CodeHighlight>
</div>
)
}
setTableOption(event) {
const target = event.target;
const value = target.type === 'checkbox' ? target.checked : target.value;
const name = target.name;
setTableOption (event) {
const target = event.target
const value = target.type === 'checkbox' ? target.checked : target.value
const name = target.name
this.setState({
tableOptions: {
...this.state.tableOptions,
@ -145,7 +179,7 @@ class Filtering extends React.Component {
})
}
getCode() {
getCode () {
return `
const columns = [{
header: 'Name',
@ -164,6 +198,30 @@ const columns = [{
columns: [{
header: 'Age',
accessor: 'age'
}, {
header: 'Over 21',
accessor: 'age',
id: 'over',
render: ({value}) => (value >= 21 ? 'Yes' : 'No'),
filterMethod: (filter, row) => {
if (filter.value === 'all') {
return true
}
if (filter.value === 'true') {
return row[filter.id] >= 21
}
return row[filter.id] < 21
},
filterRender: ({filter, onFilterChange}) => (
<select
onChange={event => onFilterChange(event.target.value)}
style={{width: '100%'}}
value={filter ? filter.value : 'all'}>
<option value="all"></option>
<option value="true">Can Drink</option>
<option value="false">Can't Drink</option>
</select>
)
}]
}]
@ -203,4 +261,4 @@ export default (
}
}
export default () => <Filtering/>
export default () => <Filtering />