mirror of
https://github.com/gosticks/react-table.git
synced 2026-08-19 08:20:30 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9acf8f37eb | ||
|
|
64a427d91b | ||
|
|
d21ce226aa | ||
|
|
92c9ae2196 | ||
|
|
23a031b48a | ||
|
|
bd8b273dea | ||
|
|
1a9f0e57b4 |
@@ -0,0 +1,15 @@
|
||||
## Problem Description
|
||||
include a detailed explanation of the problem here...
|
||||
|
||||
## Code Snippet(s)
|
||||
|
||||
## Steps to Reproduce
|
||||
1. list the steps
|
||||
2. to reproduce the issue
|
||||
|
||||
## System Information
|
||||
**Browser & Browser Version:**
|
||||
**Node Version:**
|
||||
**OS Version:**
|
||||
**NPM Version:**
|
||||
**Yarn Version:**
|
||||
@@ -47,7 +47,7 @@
|
||||
- [Props](#props)
|
||||
- [Columns](#columns)
|
||||
- [Column Header Groups](#column-header-groups)
|
||||
- [Custom Cell and Header Rendering](#custom-cell-and-header-rendering)
|
||||
- [Custom Cell and Header and Footer Rendering](#custom-cell-header-and-footer-rendering)
|
||||
- [Styles](#styles)
|
||||
- [Custom Props](#custom-props)
|
||||
- [Pivoting and Aggregation](#pivoting-and-aggregation)
|
||||
@@ -56,6 +56,7 @@
|
||||
- [Fully Controlled Component](#fully-controlled-component)
|
||||
- [Functional Rendering](#functional-rendering)
|
||||
- [Multi-Sort](#multi-sort)
|
||||
- [Filtering](#filtering)
|
||||
- [Component Overrides](#component-overrides)
|
||||
- [Contributing](#contributing)
|
||||
- [Scripts](#scripts)
|
||||
@@ -108,6 +109,7 @@ const columns = [{
|
||||
accessor: 'age',
|
||||
render: props => <span className='number'>{props.value}</span> // Custom cell components!
|
||||
}, {
|
||||
id: 'friendName', // Required because our accessor is not a string
|
||||
header: 'Friend Name',
|
||||
accessor: d => d.friend.name // Custom value accessors!
|
||||
}, {
|
||||
@@ -145,7 +147,10 @@ These are all of the available props (and their default values) for the main `<R
|
||||
defaultSorting: [],
|
||||
showFilters: false,
|
||||
defaultFiltering: [],
|
||||
defaultFilterMethod: (filter, row) => (row[filter.id] == filter.value),
|
||||
defaultFilterMethod: (filter, row, column) => {
|
||||
const id = filter.pivotId || filter.id
|
||||
return row[id] !== undefined ? String(row[id]).startsWith(filter.value) : true
|
||||
},
|
||||
|
||||
// Controlled State Overrides (see Fully Controlled Component section)
|
||||
page: undefined,
|
||||
@@ -223,7 +228,8 @@ These are all of the available props (and their default values) for the main `<R
|
||||
footerClassName: '',
|
||||
footerStyle: {},
|
||||
getFooterProps: () => ({}),
|
||||
filterMethod: undefined
|
||||
filterMethod: undefined,
|
||||
hideFilter: false
|
||||
},
|
||||
|
||||
// Text
|
||||
@@ -265,16 +271,16 @@ Or just define them as props
|
||||
```javascript
|
||||
[{
|
||||
// General
|
||||
accessor: 'propertyName' // or Accessor eg. (row) => row.propertyName (see "Accessors" section for more details)
|
||||
accessor: 'propertyName', // or Accessor eg. (row) => row.propertyName (see "Accessors" section for more details)
|
||||
id: 'myProperty', // Conditional - A unique ID is required if the accessor is not a string or if you would like to override the column name used in server-side calls
|
||||
sortable: true,
|
||||
show: true, // can be used to hide a column
|
||||
width: undefined, // A hardcoded width for the column. This overrides both min and max width options
|
||||
minWidth: 100 // A minimum width for this column. If there is extra room, column will flex to fill available space (up to the max-width, if set)
|
||||
maxWidth: undefined // A maximum width for this column.
|
||||
minWidth: 100, // A minimum width for this column. If there is extra room, column will flex to fill available space (up to the max-width, if set)
|
||||
maxWidth: undefined, // A maximum width for this column.
|
||||
|
||||
// Special
|
||||
expander: false // This option will override all data-related options and designates the column to be used
|
||||
expander: false, // This option will override all data-related options and designates the column to be used
|
||||
// for pivoting and sub-component expansion
|
||||
|
||||
// Cell Options
|
||||
@@ -291,22 +297,23 @@ Or just define them as props
|
||||
header: 'Header Name', a function that returns a primitive, or JSX / React Component eg. ({data, column}) => <div>Header Name</div>,
|
||||
headerClassName: '', // Set the classname of the `th` element of the column
|
||||
headerStyle: {}, // Set the style of the `th` element of the column
|
||||
getHeaderProps: (state, rowInfo, column, instance) => ({}) // a function that returns props to decorate the `th` element of the column
|
||||
getHeaderProps: (state, rowInfo, column, instance) => ({}), // a function that returns props to decorate the `th` element of the column
|
||||
|
||||
// Header Groups only
|
||||
columns: [...] // See Header Groups section below
|
||||
columns: [...], // See Header Groups section below
|
||||
|
||||
// Footer
|
||||
footer: 'Header Name' or JSX eg. ({data, column}) => <div>Header 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
|
||||
getFooterProps: (state, rowInfo, column, instance) => ({}), // A function that returns props to decorate the `td` element of the column's footer
|
||||
|
||||
// Filtering
|
||||
filterMethod: (filter, row, column) => {return true} // A function returning a boolean that specifies the filtering logic for the column
|
||||
// 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]}
|
||||
filterMethod: (filter, row, column) => {return true}, // A function returning a boolean that specifies the filtering logic for the column
|
||||
// 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
|
||||
}]
|
||||
```
|
||||
|
||||
@@ -659,6 +666,17 @@ The possibilities are endless!
|
||||
## Multi-Sort
|
||||
When clicking on a column header, hold shift to multi-sort! You can toggle `ascending` `descending` and `none` for multi-sort columns. Clicking on a header without holding shift will clear the multi-sort and replace it with the single sort of that column. It's quite handy!
|
||||
|
||||
## Filtering
|
||||
Filtering can be enabled by setting the `showFilters` option on the table.
|
||||
|
||||
If you don't want particular column to be filtered you can set the `hideFilter` option on the column.
|
||||
|
||||
By default the table tries to filter by checking if the row's value starts with the filter text. The default method for filtering the table can be set with the table's `defaultFilterMethod` option.
|
||||
|
||||
If you want to override a particular column's filtering method, you can set the `filterMethod` option on a column.
|
||||
|
||||
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
|
||||
Though we confidently stand by the markup and architecture behind it, `react-table` does offer the ability to change the core componentry it uses to render everything. You can extend or override these internal components by passing a react component to it's corresponding prop on either the global props or on a one-off basis like so:
|
||||
```javascript
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<div id="error-display"></div>
|
||||
<script src="static/preview.ea1bdf726a82703c8f6f.bundle.js"></script>
|
||||
<script src="static/preview.31fe462e1d0d70b4c4a4.bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+16
-16
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"static/preview.31fe462e1d0d70b4c4a4.bundle.js","sources":["webpack:///static/preview.31fe462e1d0d70b4c4a4.bundle.js"],"mappings":"AAAA;AAkuDA;AAw+DA;AAurFA;AAgmFA;AA87OA;AAuwFA;AA6xDA;AAy/DA;AA+uDA;AAi8DA;AA+lDA;AA4yDA;AA28CA;AAw7DA;AAooDA;AA67CA;AAiqDA;AAynEA;AAwxDA;AA+rCA;AA+mDA;AA2lDA;AAkqEA;AAs2DA;AA2zDA;AAo4CA;AAosFA;AAmjGA;AA2sDA;AA82CA;AAooCA;AAg9CA;AA67CA;AAoDA;AA+jFA","sourceRoot":""}
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"static/preview.ea1bdf726a82703c8f6f.bundle.js","sources":["webpack:///static/preview.ea1bdf726a82703c8f6f.bundle.js"],"mappings":"AAAA;AAkuDA;AAo/DA;AAgrFA;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;AAksFA;AAmjGA;AAksDA;AA02CA;AAgoCA;AAm/CA;AA+4CA;AAsCA;AA+jFA","sourceRoot":""}
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-table",
|
||||
"version": "5.3.4",
|
||||
"version": "5.4.0",
|
||||
"description": "A fast, lightweight, opinionated table and datagrid built on React",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/tannerlinsley/react-table#readme",
|
||||
@@ -61,7 +61,7 @@
|
||||
"react": "^15.4.2",
|
||||
"react-dom": "^15.4.2",
|
||||
"react-json-tree": "^0.10.1",
|
||||
"rimraf": "^2.6.1",
|
||||
"rimraf": "^2.6.1",
|
||||
"standard": "^8.0.0",
|
||||
"storybook": "^0.0.0",
|
||||
"stylus": "^0.54.5",
|
||||
|
||||
+6
-2
@@ -23,7 +23,10 @@ export default {
|
||||
defaultSorting: [],
|
||||
showFilters: false,
|
||||
defaultFiltering: [],
|
||||
defaultFilterMethod: (filter, row, column) => (row[filter.id] === filter.value),
|
||||
defaultFilterMethod: (filter, row, column) => {
|
||||
const id = filter.pivotId || filter.id
|
||||
return row[id] !== undefined ? String(row[id]).startsWith(filter.value) : true
|
||||
},
|
||||
|
||||
// Controlled State Overrides
|
||||
// page: undefined,
|
||||
@@ -100,7 +103,8 @@ export default {
|
||||
footerClassName: '',
|
||||
footerStyle: {},
|
||||
getFooterProps: emptyObj,
|
||||
filterMethod: undefined
|
||||
filterMethod: undefined,
|
||||
hideFilter: false
|
||||
},
|
||||
|
||||
// Text
|
||||
|
||||
+21
-21
@@ -85,9 +85,7 @@ export default React.createClass({
|
||||
const endRow = startRow + pageSize
|
||||
const pageRows = manual ? resolvedData : sortedData.slice(startRow, endRow)
|
||||
const minRows = this.getMinRows()
|
||||
const padRows = pages > 1 ? _.range(pageSize - pageRows.length)
|
||||
: minRows ? _.range(Math.max(minRows - pageRows.length, 0))
|
||||
: []
|
||||
const padRows = _.range(Math.max(minRows - pageRows.length, 0))
|
||||
|
||||
const hasColumnFooter = allVisibleColumns.some(d => d.footer)
|
||||
|
||||
@@ -416,20 +414,20 @@ export default React.createClass({
|
||||
const pivotCols = []
|
||||
for (let i = 0; i < column.pivotColumns.length; i++) {
|
||||
const col = column.pivotColumns[i]
|
||||
const filter = filtering.find(filter => filter.id === col.id)
|
||||
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}}>
|
||||
<input type='text'
|
||||
style={{
|
||||
flex: 1,
|
||||
width: 20,
|
||||
backgroundColor: i > 0 ? '#f3f3f3' : '#fff'
|
||||
}}
|
||||
value={filter ? filter.value : ''}
|
||||
disabled={i > 0}
|
||||
onChange={(event) => this.filterColumn(col, event)}
|
||||
/>
|
||||
{!col.hideFilter ? (
|
||||
<input type='text'
|
||||
style={{
|
||||
flex: 1,
|
||||
width: 20
|
||||
}}
|
||||
value={filter ? filter.value : ''}
|
||||
onChange={(event) => this.filterColumn(column, event, col)}
|
||||
/>
|
||||
) : null}
|
||||
</span>
|
||||
)
|
||||
if (i < column.pivotColumns.length - 1) {
|
||||
@@ -490,13 +488,15 @@ export default React.createClass({
|
||||
}}
|
||||
{...rest}
|
||||
>
|
||||
<input type='text'
|
||||
style={{
|
||||
width: `100%`
|
||||
}}
|
||||
value={filter ? filter.value : ''}
|
||||
onChange={(event) => this.filterColumn(column, event)}
|
||||
/>
|
||||
{!column.hideFilter ? (
|
||||
<input type='text'
|
||||
style={{
|
||||
width: `100%`
|
||||
}}
|
||||
value={filter ? filter.value : ''}
|
||||
onChange={(event) => this.filterColumn(column, event)}
|
||||
/>
|
||||
) : null}
|
||||
</ThComponent>
|
||||
)
|
||||
}
|
||||
|
||||
+48
-9
@@ -241,10 +241,9 @@ export default {
|
||||
|
||||
// Resolve the data from either manual data or sorted data
|
||||
return {
|
||||
sortedData: manual ? resolvedData : this.sortData(resolvedData, sorting, showFilters, filtering, defaultFilterMethod, allVisibleColumns)
|
||||
sortedData: manual ? resolvedData : this.sortData(this.filterData(resolvedData, showFilters, filtering, defaultFilterMethod, allVisibleColumns), sorting)
|
||||
}
|
||||
},
|
||||
|
||||
fireOnChange () {
|
||||
this.props.onChange(this.getResolvedState(), this)
|
||||
},
|
||||
@@ -254,7 +253,7 @@ export default {
|
||||
getStateOrProp (key) {
|
||||
return _.getFirstDefined(this.state[key], this.props[key])
|
||||
},
|
||||
sortData (data, sorting, showFilters, filtering, defaultFilterMethod, allVisibleColumns) {
|
||||
filterData (data, showFilters, filtering, defaultFilterMethod, allVisibleColumns) {
|
||||
let filteredData = data
|
||||
|
||||
if (showFilters && filtering.length) {
|
||||
@@ -262,20 +261,49 @@ export default {
|
||||
(filteredSoFar, nextFilter) => {
|
||||
return filteredSoFar.filter(
|
||||
(row) => {
|
||||
const column = allVisibleColumns.find(x => x.id === nextFilter.id) || {}
|
||||
let column
|
||||
|
||||
if (nextFilter.pivotId) {
|
||||
const parentColumn = allVisibleColumns.find(x => x.id === nextFilter.id)
|
||||
column = parentColumn.pivotColumns.find(x => x.id === nextFilter.pivotId)
|
||||
} else {
|
||||
column = allVisibleColumns.find(x => x.id === nextFilter.id)
|
||||
}
|
||||
|
||||
const filterMethod = column.filterMethod || defaultFilterMethod
|
||||
|
||||
return filterMethod(nextFilter, row, column)
|
||||
})
|
||||
}
|
||||
, filteredData
|
||||
)
|
||||
|
||||
// Apply the filter to the subrows if we are pivoting, and then
|
||||
// filter any rows without subcolumns because it would be strange to show
|
||||
filteredData = filteredData.map(row => {
|
||||
if (!row[this.props.subRowsKey]) {
|
||||
return row
|
||||
}
|
||||
return {
|
||||
...row,
|
||||
[this.props.subRowsKey]: this.filterData(row[this.props.subRowsKey], showFilters, filtering, defaultFilterMethod, allVisibleColumns)
|
||||
}
|
||||
}).filter(row => {
|
||||
if (!row[this.props.subRowsKey]) {
|
||||
return true
|
||||
}
|
||||
return row[this.props.subRowsKey].length > 0
|
||||
})
|
||||
}
|
||||
|
||||
return filteredData
|
||||
},
|
||||
sortData (data, sorting) {
|
||||
if (!sorting.length) {
|
||||
return filteredData
|
||||
return data
|
||||
}
|
||||
|
||||
const sorted = _.orderBy(filteredData, sorting.map(sort => {
|
||||
const sorted = _.orderBy(data, sorting.map(sort => {
|
||||
return row => {
|
||||
if (row[sort.id] === null || row[sort.id] === undefined) {
|
||||
return -Infinity
|
||||
@@ -419,7 +447,7 @@ export default {
|
||||
this.fireOnChange()
|
||||
})
|
||||
},
|
||||
filterColumn (column, event) {
|
||||
filterColumn (column, event, pivotColumn) {
|
||||
const {filtering} = this.getResolvedState()
|
||||
const {onFilteringChange} = this.props
|
||||
|
||||
@@ -428,12 +456,23 @@ export default {
|
||||
}
|
||||
|
||||
// Remove old filter first if it exists
|
||||
const newFiltering = (filtering || []).filter(x => x.id !== column.id)
|
||||
const newFiltering = (filtering || []).filter(x => {
|
||||
if (x.id !== column.id) {
|
||||
return true
|
||||
}
|
||||
if (x.pivotId) {
|
||||
if (pivotColumn) {
|
||||
return x.pivotId !== pivotColumn.id
|
||||
}
|
||||
return true
|
||||
}
|
||||
})
|
||||
|
||||
if (event.target.value !== '') {
|
||||
newFiltering.push({
|
||||
id: column.id,
|
||||
value: event.target.value
|
||||
value: event.target.value,
|
||||
pivotId: pivotColumn ? pivotColumn.id : undefined
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ class Filtering extends React.Component {
|
||||
data={this.state.data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
defaultFilterMethod={(filter, row) => ((row[filter.id] + "").startsWith(filter.value))}
|
||||
defaultFilterMethod={(filter, row) => (String(row[filter.id]) === filter.value)}
|
||||
{...this.state.tableOptions}
|
||||
SubComponent={(row) => {
|
||||
return (
|
||||
@@ -122,8 +122,8 @@ 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 be an exact match. Example: age == "23".</p>
|
||||
<p>This example overrides the default filter behavior by setting the <strong>defaultFilterMethod</strong> table option to match on values that start with the filter text. Example: age.startsWith("2")</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>
|
||||
@@ -172,7 +172,7 @@ export default (
|
||||
data={data}
|
||||
columns={columns}
|
||||
defaultPageSize={10}
|
||||
defaultFilterMethod={(filter, row) => ((row[filter.id] + \"\").startsWith(filter.value))}
|
||||
defaultFilterMethod={(filter, row) => (String(row[filter.id]) === filter.value)}
|
||||
{...otherOptions}
|
||||
SubComponent={(row) => {
|
||||
return (
|
||||
|
||||
@@ -38,7 +38,8 @@ export default () => {
|
||||
}, {
|
||||
header: 'Visits',
|
||||
accessor: 'visits',
|
||||
aggregate: vals => _.sum(vals)
|
||||
aggregate: vals => _.sum(vals),
|
||||
hideFilter: true
|
||||
}]
|
||||
}]
|
||||
|
||||
@@ -111,7 +112,8 @@ const columns = [{
|
||||
}, {
|
||||
header: 'Visits',
|
||||
accessor: 'visits',
|
||||
aggregate: vals => _.sum(vals)
|
||||
aggregate: vals => _.sum(vals),
|
||||
hideFilter: true
|
||||
}]
|
||||
}]
|
||||
|
||||
|
||||
@@ -18,13 +18,13 @@ const requestData = (pageSize, page, sorting, filtering) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// On the server, you'll likely use SQL or noSQL or some other query language to do this.
|
||||
// For this mock, we'll just use lodash
|
||||
let filteredData = rawData
|
||||
let filteredData = rawData;
|
||||
if (filtering.length) {
|
||||
filteredData = filtering.reduce(
|
||||
(filteredSoFar, nextFilter) => {
|
||||
return filteredSoFar.filter(
|
||||
(row) => {
|
||||
return (row[nextFilter.id] + '').includes(nextFilter.value)
|
||||
return (row[nextFilter.id]+"").includes(nextFilter.value)
|
||||
})
|
||||
}
|
||||
, filteredData)
|
||||
@@ -93,7 +93,7 @@ const ServerSide = React.createClass({
|
||||
}]}
|
||||
manual // Forces table not to paginate or sort automatically, so we can handle it server-side
|
||||
defaultPageSize={10}
|
||||
showFilters
|
||||
showFilters={true}
|
||||
data={this.state.data} // Set the rows to be displayed
|
||||
pages={this.state.pages} // Display the total number of pages
|
||||
loading={this.state.loading} // Display the loading overlay when we need it
|
||||
|
||||
Reference in New Issue
Block a user