Skip to content
Permalink
Browse files

Add global filtering support via useGlobalFilter

  • Loading branch information
tannerlinsley committed Dec 18, 2019
1 parent 63d54b1 commit d18f1ba4d887299a28603097007e329de6c1ea45
@@ -1,20 +1,20 @@
{
"dist/index.js": {
"bundled": 108756,
"minified": 50398,
"gzipped": 13421
"bundled": 112754,
"minified": 52324,
"gzipped": 13765
},
"dist/index.es.js": {
"bundled": 107845,
"minified": 49586,
"gzipped": 13253,
"bundled": 111817,
"minified": 51488,
"gzipped": 13599,
"treeshaked": {
"rollup": {
"code": 80,
"import_statements": 21
},
"webpack": {
"code": 8168
"code": 8484
}
}
},
@@ -1,3 +1,10 @@
## 7.0.0-rc.15

- Added `useGlobalFilter` hook for performing table-wide filtering
- Filter function signature has changed to supply an array of column IDs (to support both the tranditional single column style and the new multi-column search style introduced with `useGlobalFilter`).
- Removed the `column` parameter from the filter function signature as it was unused and no longer made sense with the array of IDs change above.
- Updated the `filtering` example to use a global filter in addition to the column filters

## 7.0.0-rc.14

- Changed the function signature for all propGetter hooks to accept a single object of named meta properties instead of a variable length of meta arguments. The user props object has also been added as a property to all prop getters. For example, `hooks.getRowProps.push((props, instance, row) => [...])` is now written `hooks.getRowProps.push((props, { instance, row, userProps }) => [...])`
@@ -3,7 +3,7 @@
- Plugin Hook
- Optional

`useFilters` is the hook that implements **row filtering**.
`useFilters` is the hook that implements **row filtering** and can even be used in conjunction with `useGlobalFilter`. It's also important to note that this hook can be used either **before or after** `useGlobalFilter`, depending on the performance characteristics you want to code for.

### Table Options

@@ -0,0 +1,52 @@
# `useGlobalFilter`

- Plugin Hook
- Optional

`useGlobalFilter` is the hook that implements **global row filtering** and can even be used in conjunction with `useFilters`. It's also important to note that this hook can be used either **before or after** `useFilters`, depending on the performance characteristics you want to code for.

### Table Options

The following options are supported via the main options object passed to `useTable(options)`

- `initialState.globalFilter: any`
- Must be **memoized**
- An array of objects containing columnId's and their corresponding filter values. This information is stored in state since the table is allowed to manipulate the filter through user interaction.

This comment has been minimized.

Copy link
@kizu

kizu Dec 23, 2019

Contributor

I think this is an incorrect description: this is basically a filterValue

This comment has been minimized.

Copy link
@tannerlinsley

tannerlinsley Dec 23, 2019

Author Owner

Correct. This was a copy/paste artifact.

- `globalFilter: String | Function`
- Optional
- Defaults to `text`
- The resolved function from the this string/function will be used to filter the table's data.
- If a `string` is passed, the function with that name located on either the custom `filterTypes` option or the built-in filtering types object will be used. If
- If a `function` is passed, it will be used directly.
- For more information on filter types, see Filtering
- If a **function** is passed, it must be **memoized**
- `manualGlobalFilter: Bool`
- Enables filter detection functionality, but does not automatically perform row filtering.
- Turn this on if you wish to implement your own row filter outside of the table (eg. server-side or manual row grouping/nesting)
- `filterTypes: Object<filterKey: filterType>`
- Must be **memoized**
- Allows overriding or adding additional filter types for the table to use. If the globalFilter type isn't found on this object, it will default to using the built-in filter types.
- For more information on filter types, see Filtering
- `autoResetGlobalFilter: Boolean`
- Defaults to `true`
- When `true`, the `globalFilter` state will automatically reset if any of the following conditions are met:
- `data` is changed
- To disable, set to `false`
- For more information see the FAQ ["How do I stop my table state from automatically resetting when my data changes?"](./faq#how-do-i-stop-my-table-state-from-automatically-resetting-when-my-data-changes)

### Instance Properties

The following values are provided to the table `instance`:

- `rows: Array<Row>`
- An array of **filtered** rows.
- `preGlobalFilteredRows: Array<Row>`
- The array of rows **used right before filtering**.
- Among many other use-cases, these rows are directly useful for building option lists in filters, since the resulting filtered `rows` do not contain every possible option.
- `setGlobalFilter: Function(columnId, filterValue) => void`
- An instance-level function used to update the filter value for a specific column.

### Example

- [Source](https://github.com/tannerlinsley/react-table/tree/master/examples/filtering)
- [Open in CodeSandbox](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/filtering)
@@ -1,6 +1,6 @@
import React from 'react'
import styled from 'styled-components'
import { useTable, useFilters } from 'react-table'
import { useTable, useFilters, useGlobalFilter } from 'react-table'
// A great library for fuzzy filtering/sorting items
import matchSorter from 'match-sorter'

@@ -35,6 +35,32 @@ const Styles = styled.div`
}
`

// Define a default UI for filtering
function GlobalFilter({
preGlobalFilteredRows,
globalFilter,
setGlobalFilter,
}) {
const count = preGlobalFilteredRows.length

return (
<span>
Search:{' '}
<input
value={globalFilter || ''}
onChange={e => {
setGlobalFilter(e.target.value || undefined) // Set undefined to remove the filter entirely
}}
placeholder={`${count} records...`}
style={{
fontSize: '1.1rem',
border: '0',
}}
/>
</span>
)
}

// Define a default UI for filtering
function DefaultColumnFilter({
column: { filterValue, preFilteredRows, setFilter },
@@ -217,14 +243,18 @@ function Table({ columns, data }) {
rows,
prepareRow,
state,
flatColumns,
preGlobalFilteredRows,
setGlobalFilter,
} = useTable(
{
columns,
data,
defaultColumn, // Be sure to pass the defaultColumn option
filterTypes,
},
useFilters // useFilters!
useFilters, // useFilters!
useGlobalFilter // useGlobalFilter!
)

// We don't want to render all of the rows for this example, so cap
@@ -233,11 +263,6 @@ function Table({ columns, data }) {

return (
<>
<div>
<pre>
<code>{JSON.stringify(state.filters, null, 2)}</code>
</pre>
</div>
<table {...getTableProps()}>
<thead>
{headerGroups.map(headerGroup => (
@@ -251,25 +276,41 @@ function Table({ columns, data }) {
))}
</tr>
))}
<tr>
<th
colSpan={flatColumns.length}
style={{
textAlign: 'left',
}}
>
<GlobalFilter
preGlobalFilteredRows={preGlobalFilteredRows}
globalFilter={state.globalFilter}
setGlobalFilter={setGlobalFilter}
/>
</th>
</tr>
</thead>
<tbody {...getTableBodyProps()}>
{firstPageRows.map(
(row, i) => {
prepareRow(row);
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return (
<td {...cell.getCellProps()}>{cell.render('Cell')}</td>
)
})}
</tr>
)}
)}
{firstPageRows.map((row, i) => {
prepareRow(row)
return (
<tr {...row.getRowProps()}>
{row.cells.map(cell => {
return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
})}
</tr>
)
})}
</tbody>
</table>
<br />
<div>Showing the first 20 results of {rows.length} rows</div>
<div>
<pre>
<code>{JSON.stringify(state.filters, null, 2)}</code>
</pre>
</div>
</>
)
}
@@ -8094,10 +8094,10 @@ react-scripts@3.0.1:
optionalDependencies:
fsevents "2.0.6"

react-table@next:
version "7.0.0-alpha.7"
resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.0.0-alpha.7.tgz#0cb6da6f32adb397e68505b7cdd4880d15d73017"
integrity sha512-oXE9RRkE2CFk1OloNCSTPQ9qxOdujgkCoW5b/srbJsBog/ySkWuozBTQkxH1wGNmnSxGyTrTxJqXdXPQam7VAw==
react-table@latest:
version "7.0.0-rc.14"
resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.0.0-rc.14.tgz#bb4171fbdd56d78dd5f6b9e18694a36c00bd6261"
integrity sha512-9NyzAE0kLH8HA+DK86ynxVDxniO5ZdggAqI7nDKauWXQGVYNFId8+JJSescJ2vwP9nR2JKyCEDG9c+CjIaLNkA==

react@^16.8.6:
version "16.8.6"
@@ -1,79 +1,93 @@
export const text = (rows, id, filterValue) => {
export const text = (rows, ids, filterValue) => {
rows = rows.filter(row => {
const rowValue = row.values[id]
return String(rowValue)
.toLowerCase()
.includes(String(filterValue).toLowerCase())
return ids.some(id => {
const rowValue = row.values[id]
return String(rowValue)
.toLowerCase()
.includes(String(filterValue).toLowerCase())
})
})
return rows
}

text.autoRemove = val => !val

export const exactText = (rows, id, filterValue) => {
export const exactText = (rows, ids, filterValue) => {
return rows.filter(row => {
const rowValue = row.values[id]
return rowValue !== undefined
? String(rowValue).toLowerCase() === String(filterValue).toLowerCase()
: true
return ids.some(id => {
const rowValue = row.values[id]
return rowValue !== undefined
? String(rowValue).toLowerCase() === String(filterValue).toLowerCase()
: true
})
})
}

exactText.autoRemove = val => !val

export const exactTextCase = (rows, id, filterValue) => {
export const exactTextCase = (rows, ids, filterValue) => {
return rows.filter(row => {
const rowValue = row.values[id]
return rowValue !== undefined
? String(rowValue) === String(filterValue)
: true
return ids.some(id => {
const rowValue = row.values[id]
return rowValue !== undefined
? String(rowValue) === String(filterValue)
: true
})
})
}

exactTextCase.autoRemove = val => !val

export const includes = (rows, id, filterValue) => {
export const includes = (rows, ids, filterValue) => {
return rows.filter(row => {
const rowValue = row.values[id]
return filterValue.includes(rowValue)
return ids.some(id => {
const rowValue = row.values[id]
return filterValue.includes(rowValue)
})
})
}

includes.autoRemove = val => !val || !val.length

export const includesAll = (rows, id, filterValue) => {
export const includesAll = (rows, ids, filterValue) => {
return rows.filter(row => {
const rowValue = row.values[id]
return (
rowValue &&
rowValue.length &&
filterValue.every(val => rowValue.includes(val))
)
return ids.some(id => {
const rowValue = row.values[id]
return (
rowValue &&
rowValue.length &&
filterValue.every(val => rowValue.includes(val))
)
})
})
}

includesAll.autoRemove = val => !val || !val.length

export const exact = (rows, id, filterValue) => {
export const exact = (rows, ids, filterValue) => {
return rows.filter(row => {
const rowValue = row.values[id]
return rowValue === filterValue
return ids.some(id => {
const rowValue = row.values[id]
return rowValue === filterValue
})
})
}

exact.autoRemove = val => typeof val === 'undefined'

export const equals = (rows, id, filterValue) => {
export const equals = (rows, ids, filterValue) => {
return rows.filter(row => {
const rowValue = row.values[id]
// eslint-disable-next-line eqeqeq
return rowValue == filterValue
return ids.some(id => {
const rowValue = row.values[id]
// eslint-disable-next-line eqeqeq
return rowValue == filterValue
})
})
}

equals.autoRemove = val => val == null

export const between = (rows, id, filterValue) => {
export const between = (rows, ids, filterValue) => {
let [min, max] = filterValue || []

min = typeof min === 'number' ? min : -Infinity
@@ -86,8 +100,10 @@ export const between = (rows, id, filterValue) => {
}

return rows.filter(row => {
const rowValue = row.values[id]
return rowValue >= min && rowValue <= max
return ids.some(id => {
const rowValue = row.values[id]
return rowValue >= min && rowValue <= max
})
})
}

@@ -79,8 +79,6 @@ function reducer(state, action, previousState, instance) {
? action.value
: !state.hiddenColumns.includes(action.columnId)

console.log(action, should)

const hiddenColumns = should
? [...state.hiddenColumns, action.columnId]
: state.hiddenColumns.filter(d => d !== action.columnId)

0 comments on commit d18f1ba

Please sign in to comment.
You can’t perform that action at this time.