mirror of
https://github.com/gosticks/react-table.git
synced 2025-10-16 11:55:36 +00:00
Changed: Tests, aggregation, hooks, columnVisibility, docs
This commit is contained in:
parent
94a9b49187
commit
b989a8fa76
3
.babelrc
3
.babelrc
@ -11,7 +11,8 @@
|
|||||||
],
|
],
|
||||||
"env": {
|
"env": {
|
||||||
"test": {
|
"test": {
|
||||||
"presets": ["@babel/preset-env", "@babel/react"]
|
"presets": ["@babel/preset-env", "@babel/react"],
|
||||||
|
"plugins": ["@babel/plugin-transform-runtime"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -11,3 +11,4 @@ react-table.css
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
.history
|
.history
|
||||||
package-lock.json
|
package-lock.json
|
||||||
|
coverage
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
{
|
{
|
||||||
"dist/index.js": {
|
"dist/index.js": {
|
||||||
"bundled": 112721,
|
"bundled": 126199,
|
||||||
"minified": 52301,
|
"minified": 59470,
|
||||||
"gzipped": 13754
|
"gzipped": 15298
|
||||||
},
|
},
|
||||||
"dist/index.es.js": {
|
"dist/index.es.js": {
|
||||||
"bundled": 111784,
|
"bundled": 125286,
|
||||||
"minified": 51465,
|
"minified": 58658,
|
||||||
"gzipped": 13587,
|
"gzipped": 15131,
|
||||||
"treeshaked": {
|
"treeshaked": {
|
||||||
"rollup": {
|
"rollup": {
|
||||||
"code": 80,
|
"code": 80,
|
||||||
"import_statements": 21
|
"import_statements": 21
|
||||||
},
|
},
|
||||||
"webpack": {
|
"webpack": {
|
||||||
"code": 8461
|
"code": 8471
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
23
CHANGELOG.md
23
CHANGELOG.md
@ -1,3 +1,24 @@
|
|||||||
|
## 7.0.0-rc.16
|
||||||
|
|
||||||
|
- Moved away from snapshot tests. No more testing implementation details.
|
||||||
|
- Added `visibleColumns` and `visibleColumnsDeps` hooks to manipulate columns after all data is processed. Further visibility processing may result in these columns not being visible, such as `hiddenColumn` state
|
||||||
|
- The `useRows` hook has been deprecated due to its dangerous nature 💀
|
||||||
|
- Added the `instance.rowsById` object
|
||||||
|
- Renamed `instance.flatColumns` to `instance.allColumns` which now accumulates ALL columns created for the table, visible or not.
|
||||||
|
- Added the `instance.visibleColumns` object
|
||||||
|
- Fix an issue where `useAsyncDebounce` would crash when passed arguments
|
||||||
|
- Started development on the `usePivotColumns` plugin, which can be tested currently using the `_UNSTABLE_usePivoteColumns` export.
|
||||||
|
- Renamed `cell.isRepeatedValue` to `cell.isPlaceholder`
|
||||||
|
- Removed `useConsumeHookGetter` as it was inefficient most of the time and noisy
|
||||||
|
- All hooks are now "consumed" right after main plugin functions are run. This means that any attempt to add a plugin after that will result in a runtime error (for good reason, since using hook points should not be a conditional or async operation)
|
||||||
|
- Added `instance.getHooks` for getting the list of hooks that was captured after plugins are run
|
||||||
|
- Normalized all "toggle" actions to use an optional `value` property to set the value instead of toggle. Previously properties like `selected`, `groupBy`, etc. were used, but not any more!
|
||||||
|
- Undocument `instance.dispatch`. Both plugins and users should be interacting with the table via methods assigned to the instance and other structures on the table. This should both reduce the surface API that React Table needs to expose and also the amount of documentation that is needed to understand how to use the API.
|
||||||
|
- `useRowState`'s `initialRowStateAccessor` and `initialCellStateAccessor` options now have a default of `row => ({})` and `cell => ({})` respectively.
|
||||||
|
- Removed the concept of complex aggregations (eg. `column.aggregate = ['sum', 'count']`). Instead, a better aggregation function signature is now used to allow for leaf node aggregation when needed.
|
||||||
|
- Added the `column.aggregateValue` option which allows resolving (or pre-aggregating) a cell's value before it is grouped and aggregated across rows. This is useful for cell values that are not primitive, eg. an array of values that you may want to unique and count before summing that count across your groupings
|
||||||
|
- The function signature for aggregation functions has changed to be `(leafValues, aggregatedValues) => aggregatedValue` where `leafValues` is a flat array containing all leaf rows currently grouped at the aggregation level and `aggregatedValues` is an array containing the aggregated values from the immediate child sub rows. Each has purpose in the types of aggregations they power where optimizations are made for either accuracy or performance.
|
||||||
|
|
||||||
## 7.0.0-rc.15
|
## 7.0.0-rc.15
|
||||||
|
|
||||||
- Added `useGlobalFilter` hook for performing table-wide filtering
|
- Added `useGlobalFilter` hook for performing table-wide filtering
|
||||||
@ -9,7 +30,7 @@
|
|||||||
|
|
||||||
- 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 }) => [...])`
|
- 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 }) => [...])`
|
||||||
- Changed the function signature for all reduceHooks accept a single object of named meta properties instead of a variable length of meta arguments. For example, `hooks.flatColumns.push((flatColumns, instance) => flatColumns)` is now written `hooks.flatColumns.push((flatColumns, { instance }) => flatColumns)`
|
- Changed the function signature for all reduceHooks accept a single object of named meta properties instead of a variable length of meta arguments. For example, `hooks.flatColumns.push((flatColumns, instance) => flatColumns)` is now written `hooks.flatColumns.push((flatColumns, { instance }) => flatColumns)`
|
||||||
- Changed the function signature for all loopHooks accept a single object of named meta properties instead of a variable length of meta arguments. For example, `hooks.prepareRow.push((row, instance) => void)` is now written `hooks.prepareRow.push((row { instance }) => void)`
|
- Changed the function signature for all loopHooks accept a single object of named meta properties instead of a variable length of meta arguments. For example, `hooks.prepareRow.push((row, instance) => void)` is now written `hooks.prepareRow.push((row, { instance }) => void)`
|
||||||
|
|
||||||
## 7.0.0-rc.13
|
## 7.0.0-rc.13
|
||||||
|
|
||||||
|
|||||||
@ -13,5 +13,5 @@ module.exports = {
|
|||||||
rootDir: path.resolve(__dirname, '../../'),
|
rootDir: path.resolve(__dirname, '../../'),
|
||||||
roots: ['<rootDir>/src', __dirname],
|
roots: ['<rootDir>/src', __dirname],
|
||||||
transformIgnorePatterns: ['node_modules'],
|
transformIgnorePatterns: ['node_modules'],
|
||||||
snapshotSerializers: [require.resolve('snapshot-diff/serializer.js')],
|
collectCoverageFrom: ['src/**/*.js', '!**/*.test.js'],
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,2 +1 @@
|
|||||||
import '@testing-library/jest-dom/extend-expect'
|
import '@testing-library/jest-dom/extend-expect'
|
||||||
import 'snapshot-diff/extend-expect'
|
|
||||||
|
|||||||
@ -38,12 +38,18 @@ The following options are supported on any `Column` object passed to the `column
|
|||||||
- Receives the table instance and cell model as props
|
- Receives the table instance and cell model as props
|
||||||
- Must return valid JSX
|
- Must return valid JSX
|
||||||
- This function (or component) formats this column's value when it is being grouped and aggregated, eg. If this column was showing the number of visits for a user to a website and it was currently being grouped to show an **average** of the values, the `Aggregated` function for this column could format that value to `1,000 Avg. Visits`
|
- This function (or component) formats this column's value when it is being grouped and aggregated, eg. If this column was showing the number of visits for a user to a website and it was currently being grouped to show an **average** of the values, the `Aggregated` function for this column could format that value to `1,000 Avg. Visits`
|
||||||
- `aggregate: String | [String, String] | Function(values, rows, isAggregated: Bool) => any`
|
- `aggregate: String | Function(leafValues, aggregatedValues) => any`
|
||||||
|
- Optional
|
||||||
|
- Used to aggregate values across rows, eg. `average`-ing the ages of many cells in a table"
|
||||||
- If a single `String` is passed, it must be the key of either a user defined or predefined `aggregations` function.
|
- If a single `String` is passed, it must be the key of either a user defined or predefined `aggregations` function.
|
||||||
- If a tuple array of `[String, String]` is passed, both must be a key of either a user defined or predefined `aggregations` function.
|
- If a `Function` is passed, this function will receive both the leaf-row values and (if the rows have already been aggregated, the previously aggregated values) to be aggregated into a single value.
|
||||||
- The first is used to aggregate raw values, eg. `sum`-ing raw values together
|
- The function signature for all aggregation functions is `(leafValues, aggregatedValues) => aggregatedValue` where `leafValues` is a flat array containing all leaf rows currently grouped at the aggregation level and `aggregatedValues` is an array containing the aggregated values from the immediate child sub rows. Each has purpose in the types of aggregations they power where optimizations are made for either accuracy or performance.
|
||||||
- The second is used to aggregate values that have already been aggregated, eg. `average`-ing the sums produced by the raw aggregation level
|
- For examples on how an aggregation functions work, see the source code for the built in aggregations in the [src/aggregations.js](../../src/aggregations.js) file.
|
||||||
- If a `Function` is passed, this function will receive the `values`, original `rows` of those values, and an `isAggregated` `Bool` of whether or not the values and rows have already been aggregated.
|
- `aggregateValue: String | Function(values, row, column) => any`
|
||||||
|
- Optional
|
||||||
|
- When attempting to group/aggregate non primitive cell values (eg. arrays of items) you will likely need to resolve a stable primitive value like a number or string to use in normal row aggregations. This property can be used to aggregate or simply access the value to be used in aggregations eg. `count`-ing the unique number of items in a cell's array value before `sum`-ing that count across the table.
|
||||||
|
- If a single `String` is passed, it must be the key of either a user defined or predefined `aggregations` function.
|
||||||
|
- If a `Function` is passed, this function will receive the cell's accessed value, the original `row` object and the `column` associated with the cell
|
||||||
- `disableGroupBy: Boolean`
|
- `disableGroupBy: Boolean`
|
||||||
- Defaults to `false`
|
- Defaults to `false`
|
||||||
- If `true`, will disable grouping for this column.
|
- If `true`, will disable grouping for this column.
|
||||||
@ -92,6 +98,8 @@ The following properties are available on every `Row` object returned by the tab
|
|||||||
- This object contains the **aggregated** values for this row's sub rows
|
- This object contains the **aggregated** values for this row's sub rows
|
||||||
- `subRows: Array<Row>`
|
- `subRows: Array<Row>`
|
||||||
- If the row is a materialized group row, this property is the array of materialized subRows that were grouped inside of this row.
|
- If the row is a materialized group row, this property is the array of materialized subRows that were grouped inside of this row.
|
||||||
|
- `leafRows: Array<Row>`
|
||||||
|
- If the row is a materialized group row, this property is an array containing all leaf node rows aggregated into this row.
|
||||||
- `depth: Int`
|
- `depth: Int`
|
||||||
- If the row is a materialized group row, this is the grouping depth at which this row was created.
|
- If the row is a materialized group row, this is the grouping depth at which this row was created.
|
||||||
- `id: String`
|
- `id: String`
|
||||||
@ -108,7 +116,7 @@ The following additional properties are available on every `Cell` object returne
|
|||||||
|
|
||||||
- `isGrouped: Bool`
|
- `isGrouped: Bool`
|
||||||
- If `true`, this cell is a grouped cell, meaning it contains a grouping value and should usually display and expander.
|
- If `true`, this cell is a grouped cell, meaning it contains a grouping value and should usually display and expander.
|
||||||
- `isRepeatedValue: Bool`
|
- `isPlaceholder: Bool`
|
||||||
- If `true`, this cell is a repeated value cell, meaning it contains a value that is already being displayed elsewhere (usually by a parent row's cell).
|
- If `true`, this cell is a repeated value cell, meaning it contains a value that is already being displayed elsewhere (usually by a parent row's cell).
|
||||||
- Most of the time, this cell is not required to be displayed and can safely be hidden during rendering
|
- Most of the time, this cell is not required to be displayed and can safely be hidden during rendering
|
||||||
- `isAggregated: Bool`
|
- `isAggregated: Bool`
|
||||||
|
|||||||
@ -15,10 +15,16 @@ The following options are supported via the main options object passed to `useTa
|
|||||||
- If a row's ID is found in this array, it will have the state of the value corresponding to that key.
|
- If a row's ID is found in this array, it will have the state of the value corresponding to that key.
|
||||||
- Individual row states can contain anything, but they also contain a `cellState` key, which provides cell-level state based on column ID's to every
|
- Individual row states can contain anything, but they also contain a `cellState` key, which provides cell-level state based on column ID's to every
|
||||||
**prepared** cell in the table.
|
**prepared** cell in the table.
|
||||||
- `initialRowStateAccessor: Function`
|
- `initialRowStateAccessor: Function(originalRow) => Object<any>`
|
||||||
- Optional
|
- Optional
|
||||||
- This function may optionally return the initial state for a row.
|
- Defaults to: `row => ({})`
|
||||||
|
- This function should return the initial state for a row.
|
||||||
- If this function is defined, it will be passed a `Row` object, from which you can return a value to use as the initial state, eg. `row => row.original.initialState`
|
- If this function is defined, it will be passed a `Row` object, from which you can return a value to use as the initial state, eg. `row => row.original.initialState`
|
||||||
|
- `initialCellStateAccessor: Function(originalRow) => Object<any>`
|
||||||
|
- **Optional**
|
||||||
|
- Defaults to: `cell => ({})`
|
||||||
|
- This function should return the initial state for a cell.
|
||||||
|
- If this function is defined, it will be passed a `Cell` object, from which you can return a value to use as the initial state, eg. `cell => cell.row.original.initialCellState[cell.column.id]`
|
||||||
- `autoResetRowState: Boolean`
|
- `autoResetRowState: Boolean`
|
||||||
- Defaults to `true`
|
- Defaults to `true`
|
||||||
- When `true`, the `rowState` state will automatically reset if any of the following conditions are met:
|
- When `true`, the `rowState` state will automatically reset if any of the following conditions are met:
|
||||||
|
|||||||
@ -67,7 +67,7 @@ The following options are supported via the main options object passed to `useTa
|
|||||||
|
|
||||||
The following options are supported on any column object you can pass to `columns`.
|
The following options are supported on any column object you can pass to `columns`.
|
||||||
|
|
||||||
- `accessor: String | Function`
|
- `accessor: String | Function(originalRow, rowIndex) => any`
|
||||||
- **Required**
|
- **Required**
|
||||||
- This string/function is used to build the data model for your column.
|
- This string/function is used to build the data model for your column.
|
||||||
- The data returned by an accessor should be **primitive** and sortable.
|
- The data returned by an accessor should be **primitive** and sortable.
|
||||||
@ -120,18 +120,15 @@ The following properties are available on the table instance returned from `useT
|
|||||||
- `state: Object`
|
- `state: Object`
|
||||||
- **Memoized** - This object reference will not change unless the internal table state is modified.
|
- **Memoized** - This object reference will not change unless the internal table state is modified.
|
||||||
- This is the final state object of the table, which is the product of the `initialState`, internal table reducer and (optionally) a custom `reducer` supplied by the user.
|
- This is the final state object of the table, which is the product of the `initialState`, internal table reducer and (optionally) a custom `reducer` supplied by the user.
|
||||||
- `dispatch: Function({ type: Actions[type], ...payload }) => void`
|
|
||||||
- This function is used both internally by React Table, and optionally by you (the developer) to update the table state programmatically.
|
|
||||||
- `type: Actions[type] | String`
|
|
||||||
- The action type corresponding to what action being taken against the state.
|
|
||||||
- `...payload`
|
|
||||||
- Any other action data that is associated with the action
|
|
||||||
- `columns: Array<Column>`
|
- `columns: Array<Column>`
|
||||||
- A **nested** array of final column objects, **similar in structure to the original columns configuration option**.
|
- A **nested** array of final column objects, **similar in structure to the original columns configuration option**.
|
||||||
- See [Column Properties](#column-properties) for more information
|
- See [Column Properties](#column-properties) for more information
|
||||||
- `flatColumns: Array<Column>`
|
- `allColumns: Array<Column>`
|
||||||
- A **flat** array of all final column objects.
|
- A **flat** array of all final column objects.
|
||||||
- See [Column Properties](#column-properties) for more information
|
- See [Column Properties](#column-properties) for more information
|
||||||
|
- `visibleColumns: Array<Column>`
|
||||||
|
- A **flat** array of all visible column objects derived from `allColumns`.
|
||||||
|
- See [Column Properties](#column-properties) for more information
|
||||||
- `headerGroups: Array<HeaderGroup>`
|
- `headerGroups: Array<HeaderGroup>`
|
||||||
- An array of normalized header groups, each containing a flattened array of final column objects for that row.
|
- An array of normalized header groups, each containing a flattened array of final column objects for that row.
|
||||||
- **Some of these headers may be materialized as placeholders**
|
- **Some of these headers may be materialized as placeholders**
|
||||||
|
|||||||
@ -205,7 +205,7 @@ function Table({ columns, data }) {
|
|||||||
getTableBodyProps,
|
getTableBodyProps,
|
||||||
headerGroups,
|
headerGroups,
|
||||||
rows,
|
rows,
|
||||||
flatColumns,
|
visibleColumns,
|
||||||
prepareRow,
|
prepareRow,
|
||||||
setColumnOrder,
|
setColumnOrder,
|
||||||
state,
|
state,
|
||||||
@ -230,7 +230,7 @@ function Table({ columns, data }) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const randomizeColumns = () => {
|
const randomizeColumns = () => {
|
||||||
setColumnOrder(shuffle(flatColumns.map(d => d.id)))
|
setColumnOrder(shuffle(visibleColumns.map(d => d.id)))
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -267,30 +267,29 @@ function Table({ columns, data }) {
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody {...getTableBodyProps()}>
|
<tbody {...getTableBodyProps()}>
|
||||||
<AnimatePresence>
|
<AnimatePresence>
|
||||||
{rows.slice(0, 10).map(
|
{rows.slice(0, 10).map((row, i) => {
|
||||||
(row, i) => {
|
prepareRow(row)
|
||||||
prepareRow(row);
|
return (
|
||||||
return (
|
<motion.tr
|
||||||
<motion.tr
|
{...row.getRowProps({
|
||||||
{...row.getRowProps({
|
layoutTransition: spring,
|
||||||
layoutTransition: spring,
|
exit: { opacity: 0, maxHeight: 0 },
|
||||||
exit: { opacity: 0, maxHeight: 0 },
|
})}
|
||||||
})}
|
>
|
||||||
>
|
{row.cells.map((cell, i) => {
|
||||||
{row.cells.map((cell, i) => {
|
return (
|
||||||
return (
|
<motion.td
|
||||||
<motion.td
|
{...cell.getCellProps({
|
||||||
{...cell.getCellProps({
|
layoutTransition: spring,
|
||||||
layoutTransition: spring,
|
})}
|
||||||
})}
|
>
|
||||||
>
|
{cell.render('Cell')}
|
||||||
{cell.render('Cell')}
|
</motion.td>
|
||||||
</motion.td>
|
)
|
||||||
)
|
})}
|
||||||
})}
|
</motion.tr>
|
||||||
</motion.tr>
|
)
|
||||||
)}
|
})}
|
||||||
)}
|
|
||||||
</AnimatePresence>
|
</AnimatePresence>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@ -59,17 +59,16 @@ function Table({ columns, data }) {
|
|||||||
))}
|
))}
|
||||||
</thead>
|
</thead>
|
||||||
<tbody {...getTableBodyProps()}>
|
<tbody {...getTableBodyProps()}>
|
||||||
{rows.map(
|
{rows.map((row, i) => {
|
||||||
(row, i) => {
|
prepareRow(row)
|
||||||
prepareRow(row);
|
return (
|
||||||
return (
|
<tr {...row.getRowProps()}>
|
||||||
<tr {...row.getRowProps()}>
|
{row.cells.map(cell => {
|
||||||
{row.cells.map(cell => {
|
return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
|
||||||
return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
|
})}
|
||||||
})}
|
</tr>
|
||||||
</tr>
|
)
|
||||||
)}
|
})}
|
||||||
)}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
)
|
)
|
||||||
|
|||||||
@ -53,7 +53,7 @@ function Table({ columns, data }) {
|
|||||||
headerGroups,
|
headerGroups,
|
||||||
rows,
|
rows,
|
||||||
prepareRow,
|
prepareRow,
|
||||||
flatColumns,
|
allColumns,
|
||||||
getToggleHideAllColumnsProps,
|
getToggleHideAllColumnsProps,
|
||||||
state,
|
state,
|
||||||
} = useTable({
|
} = useTable({
|
||||||
@ -69,7 +69,7 @@ function Table({ columns, data }) {
|
|||||||
<IndeterminateCheckbox {...getToggleHideAllColumnsProps()} /> Toggle
|
<IndeterminateCheckbox {...getToggleHideAllColumnsProps()} /> Toggle
|
||||||
All
|
All
|
||||||
</div>
|
</div>
|
||||||
{flatColumns.map(column => (
|
{allColumns.map(column => (
|
||||||
<div key={column.id}>
|
<div key={column.id}>
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" {...column.getToggleHiddenProps()} />{' '}
|
<input type="checkbox" {...column.getToggleHiddenProps()} />{' '}
|
||||||
|
|||||||
@ -50,7 +50,7 @@ function Table({ columns, data }) {
|
|||||||
getTableBodyProps,
|
getTableBodyProps,
|
||||||
headerGroups,
|
headerGroups,
|
||||||
rows,
|
rows,
|
||||||
flatColumns,
|
visibleColumns,
|
||||||
prepareRow,
|
prepareRow,
|
||||||
setColumnOrder,
|
setColumnOrder,
|
||||||
state,
|
state,
|
||||||
@ -63,7 +63,7 @@ function Table({ columns, data }) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const randomizeColumns = () => {
|
const randomizeColumns = () => {
|
||||||
setColumnOrder(shuffle(flatColumns.map(d => d.id)))
|
setColumnOrder(shuffle(visibleColumns.map(d => d.id)))
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -80,19 +80,16 @@ function Table({ columns, data }) {
|
|||||||
))}
|
))}
|
||||||
</thead>
|
</thead>
|
||||||
<tbody {...getTableBodyProps()}>
|
<tbody {...getTableBodyProps()}>
|
||||||
{rows.slice(0, 10).map(
|
{rows.slice(0, 10).map((row, i) => {
|
||||||
(row, i) => {
|
prepareRow(row)
|
||||||
prepareRow(row);
|
return (
|
||||||
return (
|
<tr {...row.getRowProps()}>
|
||||||
<tr {...row.getRowProps()}>
|
{row.cells.map((cell, i) => {
|
||||||
{row.cells.map((cell, i) => {
|
return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
|
||||||
return (
|
})}
|
||||||
<td {...cell.getCellProps()}>{cell.render('Cell')}</td>
|
</tr>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</tr>
|
|
||||||
)}
|
|
||||||
)}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<pre>
|
<pre>
|
||||||
|
|||||||
@ -8087,10 +8087,10 @@ react-scripts@3.0.1:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "2.0.6"
|
fsevents "2.0.6"
|
||||||
|
|
||||||
react-table@next:
|
react-table@latest:
|
||||||
version "7.0.0-alpha.7"
|
version "7.0.0-rc.15"
|
||||||
resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.0.0-alpha.7.tgz#0cb6da6f32adb397e68505b7cdd4880d15d73017"
|
resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.0.0-rc.15.tgz#bb855e4e2abbb4aaf0ed2334404a41f3ada8e13a"
|
||||||
integrity sha512-oXE9RRkE2CFk1OloNCSTPQ9qxOdujgkCoW5b/srbJsBog/ySkWuozBTQkxH1wGNmnSxGyTrTxJqXdXPQam7VAw==
|
integrity sha512-ofMOlgrioHhhvHjvjsQkxvfQzU98cqwy6BjPGNwhLN1vhgXeWi0mUGreaCPvRenEbTiXsQbMl4k3Xmx3Mut8Rw==
|
||||||
|
|
||||||
react@^16.8.6:
|
react@^16.8.6:
|
||||||
version "16.8.6"
|
version "16.8.6"
|
||||||
|
|||||||
@ -243,7 +243,7 @@ function Table({ columns, data }) {
|
|||||||
rows,
|
rows,
|
||||||
prepareRow,
|
prepareRow,
|
||||||
state,
|
state,
|
||||||
flatColumns,
|
visibleColumns,
|
||||||
preGlobalFilteredRows,
|
preGlobalFilteredRows,
|
||||||
setGlobalFilter,
|
setGlobalFilter,
|
||||||
} = useTable(
|
} = useTable(
|
||||||
@ -278,7 +278,7 @@ function Table({ columns, data }) {
|
|||||||
))}
|
))}
|
||||||
<tr>
|
<tr>
|
||||||
<th
|
<th
|
||||||
colSpan={flatColumns.length}
|
colSpan={visibleColumns.length}
|
||||||
style={{
|
style={{
|
||||||
textAlign: 'left',
|
textAlign: 'left',
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -65,7 +65,7 @@ function Table({ columns, data }) {
|
|||||||
// Our custom plugin to add the expander column
|
// Our custom plugin to add the expander column
|
||||||
hooks => {
|
hooks => {
|
||||||
hooks.useControlledState.push(useControlledState)
|
hooks.useControlledState.push(useControlledState)
|
||||||
hooks.flatColumns.push((columns, { instance }) => {
|
hooks.visibleColumns.push((columns, { instance }) => {
|
||||||
if (!instance.state.groupBy.length) {
|
if (!instance.state.groupBy.length) {
|
||||||
return columns
|
return columns
|
||||||
}
|
}
|
||||||
@ -74,9 +74,9 @@ function Table({ columns, data }) {
|
|||||||
{
|
{
|
||||||
id: 'expander', // Make sure it has an ID
|
id: 'expander', // Make sure it has an ID
|
||||||
// Build our expander column
|
// Build our expander column
|
||||||
Header: ({ flatColumns, state: { groupBy } }) => {
|
Header: ({ allColumns, state: { groupBy } }) => {
|
||||||
return groupBy.map(columnId => {
|
return groupBy.map(columnId => {
|
||||||
const column = flatColumns.find(d => d.id === columnId)
|
const column = allColumns.find(d => d.id === columnId)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span {...column.getHeaderProps()}>
|
<span {...column.getHeaderProps()}>
|
||||||
@ -166,7 +166,7 @@ function Table({ columns, data }) {
|
|||||||
? '#0aff0082'
|
? '#0aff0082'
|
||||||
: cell.isAggregated
|
: cell.isAggregated
|
||||||
? '#ffa50078'
|
? '#ffa50078'
|
||||||
: cell.isRepeatedValue
|
: cell.isPlaceholder
|
||||||
? '#ff000042'
|
? '#ff000042'
|
||||||
: 'white',
|
: 'white',
|
||||||
}}
|
}}
|
||||||
@ -175,7 +175,7 @@ function Table({ columns, data }) {
|
|||||||
? // If the cell is aggregated, use the Aggregated
|
? // If the cell is aggregated, use the Aggregated
|
||||||
// renderer for cell
|
// renderer for cell
|
||||||
cell.render('Aggregated')
|
cell.render('Aggregated')
|
||||||
: cell.isRepeatedValue
|
: cell.isPlaceholder
|
||||||
? null // For cells with repeated values, render null
|
? null // For cells with repeated values, render null
|
||||||
: // Otherwise, just render the regular cell
|
: // Otherwise, just render the regular cell
|
||||||
cell.render('Cell')}
|
cell.render('Cell')}
|
||||||
@ -225,20 +225,20 @@ function Legend() {
|
|||||||
padding: '0.5rem',
|
padding: '0.5rem',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Repeated Value
|
Placeholder
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a custom aggregator that
|
// This is a custom aggregator that
|
||||||
// takes in an array of values and
|
// takes in an array of leaf values and
|
||||||
// returns the rounded median
|
// returns the rounded median
|
||||||
function roundedMedian(values) {
|
function roundedMedian(leafValues) {
|
||||||
let min = values[0] || ''
|
let min = leafValues[0] || 0
|
||||||
let max = values[0] || ''
|
let max = leafValues[0] || 0
|
||||||
|
|
||||||
values.forEach(value => {
|
leafValues.forEach(value => {
|
||||||
min = Math.min(min, value)
|
min = Math.min(min, value)
|
||||||
max = Math.max(max, value)
|
max = Math.max(max, value)
|
||||||
})
|
})
|
||||||
@ -259,7 +259,7 @@ function App() {
|
|||||||
// count the total rows being aggregated,
|
// count the total rows being aggregated,
|
||||||
// then sum any of those counts if they are
|
// then sum any of those counts if they are
|
||||||
// aggregated further
|
// aggregated further
|
||||||
aggregate: ['sum', 'count'],
|
aggregate: 'count',
|
||||||
Aggregated: ({ cell: { value } }) => `${value} Names`,
|
Aggregated: ({ cell: { value } }) => `${value} Names`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -269,7 +269,7 @@ function App() {
|
|||||||
// first count the UNIQUE values from the rows
|
// first count the UNIQUE values from the rows
|
||||||
// being aggregated, then sum those counts if
|
// being aggregated, then sum those counts if
|
||||||
// they are aggregated further
|
// they are aggregated further
|
||||||
aggregate: ['sum', 'uniqueCount'],
|
aggregate: 'uniqueCount',
|
||||||
Aggregated: ({ cell: { value } }) => `${value} Unique Names`,
|
Aggregated: ({ cell: { value } }) => `${value} Unique Names`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@ -79,50 +79,49 @@ function Table({ columns, data }) {
|
|||||||
))}
|
))}
|
||||||
</thead>
|
</thead>
|
||||||
<tbody {...getTableBodyProps()}>
|
<tbody {...getTableBodyProps()}>
|
||||||
{firstPageRows.map(
|
{firstPageRows.map((row, i) => {
|
||||||
(row, i) => {
|
prepareRow(row)
|
||||||
prepareRow(row);
|
return (
|
||||||
return (
|
<tr {...row.getRowProps()}>
|
||||||
<tr {...row.getRowProps()}>
|
{row.cells.map(cell => {
|
||||||
{row.cells.map(cell => {
|
return (
|
||||||
return (
|
<td
|
||||||
<td
|
// For educational purposes, let's color the
|
||||||
// For educational purposes, let's color the
|
// cell depending on what type it is given
|
||||||
// cell depending on what type it is given
|
// from the useGroupBy hook
|
||||||
// from the useGroupBy hook
|
{...cell.getCellProps()}
|
||||||
{...cell.getCellProps()}
|
style={{
|
||||||
style={{
|
background: cell.isGrouped
|
||||||
background: cell.isGrouped
|
? '#0aff0082'
|
||||||
? '#0aff0082'
|
: cell.isAggregated
|
||||||
: cell.isAggregated
|
? '#ffa50078'
|
||||||
? '#ffa50078'
|
: cell.isPlaceholder
|
||||||
: cell.isRepeatedValue
|
? '#ff000042'
|
||||||
? '#ff000042'
|
: 'white',
|
||||||
: 'white',
|
}}
|
||||||
}}
|
>
|
||||||
>
|
{cell.isGrouped ? (
|
||||||
{cell.isGrouped ? (
|
// If it's a grouped cell, add an expander and row count
|
||||||
// If it's a grouped cell, add an expander and row count
|
<>
|
||||||
<>
|
<span {...row.getExpandedToggleProps()}>
|
||||||
<span {...row.getExpandedToggleProps()}>
|
{row.isExpanded ? '👇' : '👉'}
|
||||||
{row.isExpanded ? '👇' : '👉'}
|
</span>{' '}
|
||||||
</span>{' '}
|
{cell.render('Cell')} ({row.subRows.length})
|
||||||
{cell.render('Cell')} ({row.subRows.length})
|
</>
|
||||||
</>
|
) : cell.isAggregated ? (
|
||||||
) : cell.isAggregated ? (
|
// If the cell is aggregated, use the Aggregated
|
||||||
// If the cell is aggregated, use the Aggregated
|
// renderer for cell
|
||||||
// renderer for cell
|
cell.render('Aggregated')
|
||||||
cell.render('Aggregated')
|
) : cell.isPlaceholder ? null : ( // For cells with repeated values, render null
|
||||||
) : cell.isRepeatedValue ? null : ( // For cells with repeated values, render null
|
// Otherwise, just render the regular cell
|
||||||
// Otherwise, just render the regular cell
|
cell.render('Cell')
|
||||||
cell.render('Cell')
|
)}
|
||||||
)}
|
</td>
|
||||||
</td>
|
)
|
||||||
)
|
})}
|
||||||
})}
|
</tr>
|
||||||
</tr>
|
)
|
||||||
)}
|
})}
|
||||||
)}
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<br />
|
<br />
|
||||||
@ -170,13 +169,13 @@ function Legend() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// This is a custom aggregator that
|
// This is a custom aggregator that
|
||||||
// takes in an array of values and
|
// takes in an array of leaf values and
|
||||||
// returns the rounded median
|
// returns the rounded median
|
||||||
function roundedMedian(values) {
|
function roundedMedian(leafValues) {
|
||||||
let min = values[0] || ''
|
let min = leafValues[0] || 0
|
||||||
let max = values[0] || ''
|
let max = leafValues[0] || 0
|
||||||
|
|
||||||
values.forEach(value => {
|
leafValues.forEach(value => {
|
||||||
min = Math.min(min, value)
|
min = Math.min(min, value)
|
||||||
max = Math.max(max, value)
|
max = Math.max(max, value)
|
||||||
})
|
})
|
||||||
@ -197,7 +196,7 @@ function App() {
|
|||||||
// count the total rows being aggregated,
|
// count the total rows being aggregated,
|
||||||
// then sum any of those counts if they are
|
// then sum any of those counts if they are
|
||||||
// aggregated further
|
// aggregated further
|
||||||
aggregate: ['sum', 'count'],
|
aggregate: 'count',
|
||||||
Aggregated: ({ cell: { value } }) => `${value} Names`,
|
Aggregated: ({ cell: { value } }) => `${value} Names`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -207,7 +206,7 @@ function App() {
|
|||||||
// first count the UNIQUE values from the rows
|
// first count the UNIQUE values from the rows
|
||||||
// being aggregated, then sum those counts if
|
// being aggregated, then sum those counts if
|
||||||
// they are aggregated further
|
// they are aggregated further
|
||||||
aggregate: ['sum', 'uniqueCount'],
|
aggregate: 'uniqueCount',
|
||||||
Aggregated: ({ cell: { value } }) => `${value} Unique Names`,
|
Aggregated: ({ cell: { value } }) => `${value} Unique Names`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -220,7 +219,8 @@ function App() {
|
|||||||
accessor: 'age',
|
accessor: 'age',
|
||||||
// Aggregate the average age of visitors
|
// Aggregate the average age of visitors
|
||||||
aggregate: 'average',
|
aggregate: 'average',
|
||||||
Aggregated: ({ cell: { value } }) => `${value} (avg)`,
|
Aggregated: ({ cell: { value } }) =>
|
||||||
|
`${Math.round(value * 100) / 100} (avg)`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Visits',
|
Header: 'Visits',
|
||||||
|
|||||||
@ -308,7 +308,7 @@ function Table({ columns, data, updateMyData, skipPageReset }) {
|
|||||||
usePagination,
|
usePagination,
|
||||||
useRowSelect,
|
useRowSelect,
|
||||||
hooks => {
|
hooks => {
|
||||||
hooks.flatColumns.push(columns => [
|
hooks.visibleColumns.push(columns => [
|
||||||
{
|
{
|
||||||
id: 'selection',
|
id: 'selection',
|
||||||
// Make this column a groupByBoundary. This ensures that groupBy columns
|
// Make this column a groupByBoundary. This ensures that groupBy columns
|
||||||
@ -388,7 +388,7 @@ function Table({ columns, data, updateMyData, skipPageReset }) {
|
|||||||
// If the cell is aggregated, use the Aggregated
|
// If the cell is aggregated, use the Aggregated
|
||||||
// renderer for cell
|
// renderer for cell
|
||||||
cell.render('Aggregated')
|
cell.render('Aggregated')
|
||||||
) : cell.isRepeatedValue ? null : ( // For cells with repeated values, render null
|
) : cell.isPlaceholder ? null : ( // For cells with repeated values, render null
|
||||||
// Otherwise, just render the regular cell
|
// Otherwise, just render the regular cell
|
||||||
cell.render('Cell', { editable: true })
|
cell.render('Cell', { editable: true })
|
||||||
)}
|
)}
|
||||||
@ -486,13 +486,13 @@ function filterGreaterThan(rows, id, filterValue) {
|
|||||||
filterGreaterThan.autoRemove = val => typeof val !== 'number'
|
filterGreaterThan.autoRemove = val => typeof val !== 'number'
|
||||||
|
|
||||||
// This is a custom aggregator that
|
// This is a custom aggregator that
|
||||||
// takes in an array of values and
|
// takes in an array of leaf values and
|
||||||
// returns the rounded median
|
// returns the rounded median
|
||||||
function roundedMedian(values) {
|
function roundedMedian(leafValues) {
|
||||||
let min = values[0] || ''
|
let min = leafValues[0] || 0
|
||||||
let max = values[0] || ''
|
let max = leafValues[0] || 0
|
||||||
|
|
||||||
values.forEach(value => {
|
leafValues.forEach(value => {
|
||||||
min = Math.min(min, value)
|
min = Math.min(min, value)
|
||||||
max = Math.max(max, value)
|
max = Math.max(max, value)
|
||||||
})
|
})
|
||||||
@ -513,7 +513,7 @@ function App() {
|
|||||||
// count the total rows being aggregated,
|
// count the total rows being aggregated,
|
||||||
// then sum any of those counts if they are
|
// then sum any of those counts if they are
|
||||||
// aggregated further
|
// aggregated further
|
||||||
aggregate: ['sum', 'count'],
|
aggregate: 'count',
|
||||||
Aggregated: ({ cell: { value } }) => `${value} Names`,
|
Aggregated: ({ cell: { value } }) => `${value} Names`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -525,7 +525,7 @@ function App() {
|
|||||||
// first count the UNIQUE values from the rows
|
// first count the UNIQUE values from the rows
|
||||||
// being aggregated, then sum those counts if
|
// being aggregated, then sum those counts if
|
||||||
// they are aggregated further
|
// they are aggregated further
|
||||||
aggregate: ['sum', 'uniqueCount'],
|
aggregate: 'uniqueCount',
|
||||||
Aggregated: ({ cell: { value } }) => `${value} Unique Names`,
|
Aggregated: ({ cell: { value } }) => `${value} Unique Names`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@ -308,7 +308,7 @@ function Table({ columns, data, updateMyData, skipReset }) {
|
|||||||
useRowSelect,
|
useRowSelect,
|
||||||
// Here we will use a plugin to add our selection column
|
// Here we will use a plugin to add our selection column
|
||||||
hooks => {
|
hooks => {
|
||||||
hooks.flatColumns.push(columns => {
|
hooks.visibleColumns.push(columns => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
id: 'selection',
|
id: 'selection',
|
||||||
@ -390,7 +390,7 @@ function Table({ columns, data, updateMyData, skipReset }) {
|
|||||||
// If the cell is aggregated, use the Aggregated
|
// If the cell is aggregated, use the Aggregated
|
||||||
// renderer for cell
|
// renderer for cell
|
||||||
cell.render('Aggregated')
|
cell.render('Aggregated')
|
||||||
) : cell.isRepeatedValue ? null : ( // For cells with repeated values, render null
|
) : cell.isPlaceholder ? null : ( // For cells with repeated values, render null
|
||||||
// Otherwise, just render the regular cell
|
// Otherwise, just render the regular cell
|
||||||
cell.render('Cell', { editable: true })
|
cell.render('Cell', { editable: true })
|
||||||
)}
|
)}
|
||||||
@ -488,13 +488,13 @@ function filterGreaterThan(rows, id, filterValue) {
|
|||||||
filterGreaterThan.autoRemove = val => typeof val !== 'number'
|
filterGreaterThan.autoRemove = val => typeof val !== 'number'
|
||||||
|
|
||||||
// This is a custom aggregator that
|
// This is a custom aggregator that
|
||||||
// takes in an array of values and
|
// takes in an array of leaf values and
|
||||||
// returns the rounded median
|
// returns the rounded median
|
||||||
function roundedMedian(values) {
|
function roundedMedian(leafValues) {
|
||||||
let min = values[0] || ''
|
let min = leafValues[0] || 0
|
||||||
let max = values[0] || ''
|
let max = leafValues[0] || 0
|
||||||
|
|
||||||
values.forEach(value => {
|
leafValues.forEach(value => {
|
||||||
min = Math.min(min, value)
|
min = Math.min(min, value)
|
||||||
max = Math.max(max, value)
|
max = Math.max(max, value)
|
||||||
})
|
})
|
||||||
@ -532,7 +532,7 @@ function App() {
|
|||||||
// count the total rows being aggregated,
|
// count the total rows being aggregated,
|
||||||
// then sum any of those counts if they are
|
// then sum any of those counts if they are
|
||||||
// aggregated further
|
// aggregated further
|
||||||
aggregate: ['sum', 'count'],
|
aggregate: 'count',
|
||||||
Aggregated: ({ cell: { value } }) => `${value} Names`,
|
Aggregated: ({ cell: { value } }) => `${value} Names`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -544,7 +544,7 @@ function App() {
|
|||||||
// first count the UNIQUE values from the rows
|
// first count the UNIQUE values from the rows
|
||||||
// being aggregated, then sum those counts if
|
// being aggregated, then sum those counts if
|
||||||
// they are aggregated further
|
// they are aggregated further
|
||||||
aggregate: ['sum', 'uniqueCount'],
|
aggregate: 'uniqueCount',
|
||||||
Aggregated: ({ cell: { value } }) => `${value} Unique Names`,
|
Aggregated: ({ cell: { value } }) => `${value} Unique Names`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
4
examples/pivoting/.babelrc
Normal file
4
examples/pivoting/.babelrc
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"presets": ["react-app"],
|
||||||
|
"plugins": ["styled-components"]
|
||||||
|
}
|
||||||
1
examples/pivoting/.env
Normal file
1
examples/pivoting/.env
Normal file
@ -0,0 +1 @@
|
|||||||
|
SKIP_PREFLIGHT_CHECK=true
|
||||||
7
examples/pivoting/.eslintrc
Normal file
7
examples/pivoting/.eslintrc
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"extends": ["react-app", "prettier"],
|
||||||
|
"rules": {
|
||||||
|
// "eqeqeq": 0,
|
||||||
|
// "jsx-a11y/anchor-is-valid": 0
|
||||||
|
}
|
||||||
|
}
|
||||||
23
examples/pivoting/.gitignore
vendored
Normal file
23
examples/pivoting/.gitignore
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
/.pnp
|
||||||
|
.pnp.js
|
||||||
|
|
||||||
|
# testing
|
||||||
|
/coverage
|
||||||
|
|
||||||
|
# production
|
||||||
|
/build
|
||||||
|
|
||||||
|
# misc
|
||||||
|
.DS_Store
|
||||||
|
.env.local
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
29
examples/pivoting/.rescriptsrc.js
Normal file
29
examples/pivoting/.rescriptsrc.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
const path = require('path')
|
||||||
|
const resolveFrom = require('resolve-from')
|
||||||
|
|
||||||
|
const fixLinkedDependencies = config => {
|
||||||
|
config.resolve = {
|
||||||
|
...config.resolve,
|
||||||
|
alias: {
|
||||||
|
...config.resolve.alias,
|
||||||
|
react$: resolveFrom(path.resolve('node_modules'), 'react'),
|
||||||
|
'react-dom$': resolveFrom(path.resolve('node_modules'), 'react-dom'),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
const includeSrcDirectory = config => {
|
||||||
|
config.resolve = {
|
||||||
|
...config.resolve,
|
||||||
|
modules: [path.resolve('src'), ...config.resolve.modules],
|
||||||
|
}
|
||||||
|
return config
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
['use-babel-config', '.babelrc'],
|
||||||
|
['use-eslint-config', '.eslintrc'],
|
||||||
|
fixLinkedDependencies,
|
||||||
|
// includeSrcDirectory,
|
||||||
|
]
|
||||||
6
examples/pivoting/README.md
Normal file
6
examples/pivoting/README.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app) and Rescripts.
|
||||||
|
|
||||||
|
You can:
|
||||||
|
|
||||||
|
- [Open this example in a new CodeSandbox](https://codesandbox.io/s/github/tannerlinsley/react-table/tree/master/examples/pivoting)
|
||||||
|
- `yarn` and `yarn start` to run and edit the example
|
||||||
36
examples/pivoting/package.json
Normal file
36
examples/pivoting/package.json
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"start": "rescripts start",
|
||||||
|
"build": "rescripts build",
|
||||||
|
"test": "rescripts test",
|
||||||
|
"eject": "rescripts eject"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"dayjs": "^1.8.18",
|
||||||
|
"namor": "^1.1.2",
|
||||||
|
"react": "^16.8.6",
|
||||||
|
"react-dom": "^16.8.6",
|
||||||
|
"react-scripts": "3.0.1",
|
||||||
|
"react-table": "latest",
|
||||||
|
"styled-components": "^4.3.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@rescripts/cli": "^0.0.11",
|
||||||
|
"@rescripts/rescript-use-babel-config": "^0.0.8",
|
||||||
|
"@rescripts/rescript-use-eslint-config": "^0.0.9",
|
||||||
|
"babel-eslint": "10.0.1"
|
||||||
|
},
|
||||||
|
"browserslist": {
|
||||||
|
"production": [
|
||||||
|
">0.2%",
|
||||||
|
"not dead",
|
||||||
|
"not op_mini all"
|
||||||
|
],
|
||||||
|
"development": [
|
||||||
|
"last 1 chrome version",
|
||||||
|
"last 1 firefox version",
|
||||||
|
"last 1 safari version"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
examples/pivoting/public/favicon.ico
Normal file
BIN
examples/pivoting/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
38
examples/pivoting/public/index.html
Normal file
38
examples/pivoting/public/index.html
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="theme-color" content="#000000" />
|
||||||
|
<!--
|
||||||
|
manifest.json provides metadata used when your web app is installed on a
|
||||||
|
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||||
|
-->
|
||||||
|
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||||
|
<!--
|
||||||
|
Notice the use of %PUBLIC_URL% in the tags above.
|
||||||
|
It will be replaced with the URL of the `public` folder during the build.
|
||||||
|
Only files inside the `public` folder can be referenced from the HTML.
|
||||||
|
|
||||||
|
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||||
|
work correctly both with client-side routing and a non-root public URL.
|
||||||
|
Learn how to configure a non-root public URL by running `npm run build`.
|
||||||
|
-->
|
||||||
|
<title>React App</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||||
|
<div id="root"></div>
|
||||||
|
<!--
|
||||||
|
This HTML file is a template.
|
||||||
|
If you open it directly in the browser, you will see an empty page.
|
||||||
|
|
||||||
|
You can add webfonts, meta tags, or analytics to this file.
|
||||||
|
The build step will place the bundled scripts into the <body> tag.
|
||||||
|
|
||||||
|
To begin the development, run `npm start` or `yarn start`.
|
||||||
|
To create a production bundle, use `npm run build` or `yarn build`.
|
||||||
|
-->
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
15
examples/pivoting/public/manifest.json
Normal file
15
examples/pivoting/public/manifest.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"short_name": "React App",
|
||||||
|
"name": "Create React App Sample",
|
||||||
|
"icons": [
|
||||||
|
{
|
||||||
|
"src": "favicon.ico",
|
||||||
|
"sizes": "64x64 32x32 24x24 16x16",
|
||||||
|
"type": "image/x-icon"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"start_url": ".",
|
||||||
|
"display": "standalone",
|
||||||
|
"theme_color": "#000000",
|
||||||
|
"background_color": "#ffffff"
|
||||||
|
}
|
||||||
298
examples/pivoting/src/App.js
Normal file
298
examples/pivoting/src/App.js
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
import {
|
||||||
|
useTable,
|
||||||
|
useGroupBy,
|
||||||
|
useExpanded,
|
||||||
|
_UNSTABLE_usePivoteColumns,
|
||||||
|
} from 'react-table'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import localizedFormat from 'dayjs/plugin/localizedFormat'
|
||||||
|
|
||||||
|
import makeData from './makeData'
|
||||||
|
|
||||||
|
dayjs.extend(localizedFormat)
|
||||||
|
|
||||||
|
const Styles = styled.div`
|
||||||
|
padding: 1rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-spacing: 0;
|
||||||
|
border: 1px solid black;
|
||||||
|
|
||||||
|
tr {
|
||||||
|
:last-child {
|
||||||
|
td {
|
||||||
|
border-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-bottom: 1px solid black;
|
||||||
|
border-right: 1px solid black;
|
||||||
|
|
||||||
|
:last-child {
|
||||||
|
border-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
const IndeterminateCheckbox = React.forwardRef(
|
||||||
|
({ indeterminate, ...rest }, ref) => {
|
||||||
|
const defaultRef = React.useRef()
|
||||||
|
const resolvedRef = ref || defaultRef
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
resolvedRef.current.indeterminate = indeterminate
|
||||||
|
}, [resolvedRef, indeterminate])
|
||||||
|
|
||||||
|
return <input type="checkbox" ref={resolvedRef} {...rest} />
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const renderHeaderToggles = headers => (
|
||||||
|
<>
|
||||||
|
{headers.map(column => (
|
||||||
|
<div key={column.id}>
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" {...column.getToggleHiddenProps()} />{' '}
|
||||||
|
{column.id}
|
||||||
|
</label>
|
||||||
|
{column.headers && column.headers.length ? (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
paddingLeft: '2rem',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{renderHeaderToggles(column.headers)}
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
|
||||||
|
function Table({ columns, data }) {
|
||||||
|
const {
|
||||||
|
getTableProps,
|
||||||
|
getTableBodyProps,
|
||||||
|
headerGroups,
|
||||||
|
rows,
|
||||||
|
prepareRow,
|
||||||
|
state,
|
||||||
|
visibleColumns,
|
||||||
|
allColumns,
|
||||||
|
toggleGroupBy,
|
||||||
|
togglePivot,
|
||||||
|
getToggleHideAllColumnsProps,
|
||||||
|
headers,
|
||||||
|
} = useTable(
|
||||||
|
{
|
||||||
|
columns,
|
||||||
|
data,
|
||||||
|
},
|
||||||
|
useGroupBy,
|
||||||
|
_UNSTABLE_usePivoteColumns,
|
||||||
|
useExpanded // useGroupBy and _UNSTABLE_usePivoteColumns would be pretty useless without useExpanded ;)
|
||||||
|
)
|
||||||
|
|
||||||
|
// We don't want to render all of the rows for this example, so cap
|
||||||
|
// it at 20 for this use case
|
||||||
|
const firstPageRows = rows.slice(0, 25)
|
||||||
|
|
||||||
|
const options = allColumns.filter(
|
||||||
|
d => !d.isGrouped && !d.isPivoted && !d.isPivotSource
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<table {...getTableProps()}>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<td colSpan={visibleColumns.length}>
|
||||||
|
Group By:{' '}
|
||||||
|
{state.groupBy.map(columnId => {
|
||||||
|
const column = allColumns.find(d => d.id === columnId)
|
||||||
|
return (
|
||||||
|
<span key={column.id}>
|
||||||
|
<button onClick={() => column.toggleGroupBy()}>
|
||||||
|
🛑 {column.render('Header')}
|
||||||
|
</button>{' '}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<select
|
||||||
|
onChange={e => toggleGroupBy(e.target.value, true)}
|
||||||
|
value=""
|
||||||
|
>
|
||||||
|
<option disabled selected value="">
|
||||||
|
Add column...{' '}
|
||||||
|
</option>
|
||||||
|
{options.map(column => (
|
||||||
|
<option key={column.id} value={column.id}>
|
||||||
|
{column.render('Header')}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colSpan={visibleColumns.length}>
|
||||||
|
Pivot Columns:
|
||||||
|
{state.pivotColumns.map(columnId => {
|
||||||
|
const column = allColumns.find(d => d.id === columnId)
|
||||||
|
return (
|
||||||
|
<span key={column.id}>
|
||||||
|
<button onClick={() => column.togglePivot()}>
|
||||||
|
🛑 {column.render('Header')}
|
||||||
|
</button>{' '}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
<select
|
||||||
|
onChange={e => togglePivot(e.target.value, true)}
|
||||||
|
value=""
|
||||||
|
>
|
||||||
|
<option disabled selected value="">
|
||||||
|
Add column...{' '}
|
||||||
|
</option>
|
||||||
|
{options.map(column => (
|
||||||
|
<option key={column.id} value={column.id}>
|
||||||
|
{column.render('Header')}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td colSpan={visibleColumns.length}>
|
||||||
|
<div>
|
||||||
|
<IndeterminateCheckbox {...getToggleHideAllColumnsProps()} />{' '}
|
||||||
|
Toggle All
|
||||||
|
</div>
|
||||||
|
{renderHeaderToggles(headers)}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{headerGroups.map(headerGroup => (
|
||||||
|
<tr {...headerGroup.getHeaderGroupProps()}>
|
||||||
|
{headerGroup.headers.map(column => (
|
||||||
|
<th {...column.getHeaderProps()}>{column.render('Header')}</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</thead>
|
||||||
|
<tbody {...getTableBodyProps()}>
|
||||||
|
{firstPageRows.map((row, i) => {
|
||||||
|
prepareRow(row)
|
||||||
|
return (
|
||||||
|
<tr {...row.getRowProps()}>
|
||||||
|
{row.cells.map(cell => {
|
||||||
|
return (
|
||||||
|
<td {...cell.getCellProps()}>
|
||||||
|
{cell.isGrouped ? (
|
||||||
|
<>
|
||||||
|
<span {...row.getExpandedToggleProps()}>
|
||||||
|
{row.isExpanded ? '👇' : '👉'} {cell.render('Cell')}{' '}
|
||||||
|
({row.subRows.length})
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
) : cell.isAggregated ? (
|
||||||
|
cell.render('Aggregated')
|
||||||
|
) : cell.isPlaceholder ? null : (
|
||||||
|
cell.render('Cell')
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<br />
|
||||||
|
<div>Showing the first 25 results of {rows.length} rows</div>
|
||||||
|
<pre>
|
||||||
|
<code>{JSON.stringify(state, null, 2)}</code>
|
||||||
|
</pre>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const columns = React.useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
Header: 'Order Date',
|
||||||
|
id: 'date',
|
||||||
|
accessor: d => new Date(d.date),
|
||||||
|
sortType: 'basic',
|
||||||
|
aggregate: 'count',
|
||||||
|
Cell: ({ cell: { value } }) => (value ? dayjs(value).format('l') : ''),
|
||||||
|
Aggregated: ({ cell: { value } }) => `${value} Orders`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Employee',
|
||||||
|
accessor: 'rep',
|
||||||
|
aggregate: 'uniqueCount',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Region',
|
||||||
|
accessor: 'region',
|
||||||
|
aggregate: 'uniqueCount',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Item',
|
||||||
|
accessor: 'item',
|
||||||
|
aggregate: 'count',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Units',
|
||||||
|
accessor: 'units',
|
||||||
|
aggregate: 'sum',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Unit Cost',
|
||||||
|
accessor: 'unitCost',
|
||||||
|
aggregate: 'average',
|
||||||
|
Cell: ({ cell: { value } }) =>
|
||||||
|
typeof value !== 'undefined' ? (
|
||||||
|
<div
|
||||||
|
style={{ textAlign: 'right', fontVariantNumeric: 'tabular-nums' }}
|
||||||
|
>
|
||||||
|
${(Math.floor(value * 100) / 100).toLocaleString()}
|
||||||
|
</div>
|
||||||
|
) : null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Total',
|
||||||
|
accessor: 'total',
|
||||||
|
aggregate: 'sum',
|
||||||
|
Cell: ({ cell: { value } }) =>
|
||||||
|
typeof value !== 'undefined' ? (
|
||||||
|
<div
|
||||||
|
style={{ textAlign: 'right', fontVariantNumeric: 'tabular-nums' }}
|
||||||
|
>
|
||||||
|
${(Math.floor(value * 100) / 100).toLocaleString()}
|
||||||
|
</div>
|
||||||
|
) : null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
|
const data = React.useMemo(() => makeData(10000), [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Styles>
|
||||||
|
<Table columns={columns} data={data} />
|
||||||
|
</Styles>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default App
|
||||||
9
examples/pivoting/src/App.test.js
Normal file
9
examples/pivoting/src/App.test.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import ReactDOM from 'react-dom'
|
||||||
|
import App from './App'
|
||||||
|
|
||||||
|
it('renders without crashing', () => {
|
||||||
|
const div = document.createElement('div')
|
||||||
|
ReactDOM.render(<App />, div)
|
||||||
|
ReactDOM.unmountComponentAtNode(div)
|
||||||
|
})
|
||||||
13
examples/pivoting/src/index.css
Normal file
13
examples/pivoting/src/index.css
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
|
||||||
|
'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
|
||||||
|
sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
|
||||||
|
monospace;
|
||||||
|
}
|
||||||
6
examples/pivoting/src/index.js
Normal file
6
examples/pivoting/src/index.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import ReactDOM from 'react-dom'
|
||||||
|
import './index.css'
|
||||||
|
import App from './App'
|
||||||
|
|
||||||
|
ReactDOM.render(<App />, document.getElementById('root'))
|
||||||
95
examples/pivoting/src/makeData.js
Normal file
95
examples/pivoting/src/makeData.js
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
const skewLow = n => (n < 0.9 ? n / 2 : n)
|
||||||
|
|
||||||
|
const sample = items => {
|
||||||
|
const length = items.length
|
||||||
|
const rand = Math.floor(skewLow(Math.random() * length))
|
||||||
|
return items[rand]
|
||||||
|
}
|
||||||
|
|
||||||
|
const reps = [
|
||||||
|
'Jones',
|
||||||
|
'Kivell',
|
||||||
|
'Jardine',
|
||||||
|
'Gill',
|
||||||
|
'Sorvino',
|
||||||
|
'Andrews',
|
||||||
|
'Thompson',
|
||||||
|
'Morgan',
|
||||||
|
'Howard',
|
||||||
|
'Parent',
|
||||||
|
'Smith',
|
||||||
|
]
|
||||||
|
|
||||||
|
const regions = ['East', 'Central', 'West', 'International']
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
['Pencil', 1.99, 100],
|
||||||
|
['Binder', 19.99, 1, 50],
|
||||||
|
['Pen', 7.99, 1, 100],
|
||||||
|
['Desk', 299.99, 1, 10],
|
||||||
|
['Notebook', 10.99, 1, 30],
|
||||||
|
]
|
||||||
|
|
||||||
|
const dates = [
|
||||||
|
'1/6/2018',
|
||||||
|
'1/23/2018',
|
||||||
|
'2/9/2018',
|
||||||
|
'2/26/2018',
|
||||||
|
'3/15/2018',
|
||||||
|
'4/1/2018',
|
||||||
|
'4/18/2018',
|
||||||
|
'5/5/2018',
|
||||||
|
'5/22/2018',
|
||||||
|
'6/8/2018',
|
||||||
|
'6/25/2018',
|
||||||
|
'7/12/2018',
|
||||||
|
'7/29/2018',
|
||||||
|
'8/15/2018',
|
||||||
|
'9/1/2018',
|
||||||
|
'9/18/2018',
|
||||||
|
'10/5/2018',
|
||||||
|
'10/22/2018',
|
||||||
|
'11/8/2018',
|
||||||
|
'11/25/2018',
|
||||||
|
'12/12/2018',
|
||||||
|
'12/29/2018',
|
||||||
|
'1/15/2019',
|
||||||
|
'2/1/2019',
|
||||||
|
'2/18/2019',
|
||||||
|
'3/7/2019',
|
||||||
|
'3/24/2019',
|
||||||
|
'4/10/2019',
|
||||||
|
'4/27/2019',
|
||||||
|
'5/14/2019',
|
||||||
|
'5/31/2019',
|
||||||
|
'6/17/2019',
|
||||||
|
'7/4/2019',
|
||||||
|
'7/21/2019',
|
||||||
|
'8/7/2019',
|
||||||
|
'8/24/2019',
|
||||||
|
'9/10/2019',
|
||||||
|
'9/27/2019',
|
||||||
|
'10/14/2019',
|
||||||
|
'10/31/2019',
|
||||||
|
'11/17/2019',
|
||||||
|
'12/4/2019',
|
||||||
|
'12/21/2019',
|
||||||
|
]
|
||||||
|
|
||||||
|
export default function makeData() {
|
||||||
|
return Array.from(new Array(10000)).map(() => {
|
||||||
|
const [item, unitCost, stock] = sample(items)
|
||||||
|
const units = Math.ceil(skewLow(Math.random()) * stock)
|
||||||
|
const total = units * unitCost
|
||||||
|
|
||||||
|
return {
|
||||||
|
date: sample(dates),
|
||||||
|
rep: sample(reps),
|
||||||
|
region: sample(regions),
|
||||||
|
item,
|
||||||
|
unitCost,
|
||||||
|
units,
|
||||||
|
total,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
10150
examples/pivoting/yarn.lock
Normal file
10150
examples/pivoting/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
@ -67,7 +67,7 @@ function Table({ columns, data }) {
|
|||||||
},
|
},
|
||||||
useRowSelect,
|
useRowSelect,
|
||||||
hooks => {
|
hooks => {
|
||||||
hooks.flatColumns.push(columns => [
|
hooks.visibleColumns.push(columns => [
|
||||||
// Let's make a column for selection
|
// Let's make a column for selection
|
||||||
{
|
{
|
||||||
id: 'selection',
|
id: 'selection',
|
||||||
|
|||||||
@ -43,7 +43,7 @@ function Table({ columns: userColumns, data, renderRowSubComponent }) {
|
|||||||
headerGroups,
|
headerGroups,
|
||||||
rows,
|
rows,
|
||||||
prepareRow,
|
prepareRow,
|
||||||
flatColumns,
|
visibleColumns,
|
||||||
state: { expanded },
|
state: { expanded },
|
||||||
} = useTable(
|
} = useTable(
|
||||||
{
|
{
|
||||||
@ -88,7 +88,7 @@ function Table({ columns: userColumns, data, renderRowSubComponent }) {
|
|||||||
*/}
|
*/}
|
||||||
{row.isExpanded ? (
|
{row.isExpanded ? (
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan={flatColumns.length}>
|
<td colSpan={visibleColumns.length}>
|
||||||
{/*
|
{/*
|
||||||
Inside it, call our renderRowSubComponent function. In reality,
|
Inside it, call our renderRowSubComponent function. In reality,
|
||||||
you could pass whatever you want as props to
|
you could pass whatever you want as props to
|
||||||
|
|||||||
11
package.json
11
package.json
@ -21,7 +21,8 @@
|
|||||||
"test": "is-ci \"test:ci\" \"test:dev\"",
|
"test": "is-ci \"test:ci\" \"test:dev\"",
|
||||||
"test:dev": "jest --watch",
|
"test:dev": "jest --watch",
|
||||||
"test:ci": "yarn test:jest",
|
"test:ci": "yarn test:jest",
|
||||||
"test:jest": "jest",
|
"test:jest": "jest --coverage",
|
||||||
|
"test:coverage": "yarn test:jest; open coverage/lcov-report/index.html",
|
||||||
"build": "cross-env NODE_ENV=production rollup -c",
|
"build": "cross-env NODE_ENV=production rollup -c",
|
||||||
"start": "rollup -c -w",
|
"start": "rollup -c -w",
|
||||||
"prepare": "yarn build",
|
"prepare": "yarn build",
|
||||||
@ -77,6 +78,7 @@
|
|||||||
"is-ci-cli": "^2.0.0",
|
"is-ci-cli": "^2.0.0",
|
||||||
"jest": "^24.9.0",
|
"jest": "^24.9.0",
|
||||||
"jest-cli": "^24.9.0",
|
"jest-cli": "^24.9.0",
|
||||||
|
"jest-diff": "^25.1.0",
|
||||||
"jest-runner-eslint": "^0.7.5",
|
"jest-runner-eslint": "^0.7.5",
|
||||||
"jest-watch-select-projects": "^1.0.0",
|
"jest-watch-select-projects": "^1.0.0",
|
||||||
"jest-watch-typeahead": "^0.4.2",
|
"jest-watch-typeahead": "^0.4.2",
|
||||||
@ -93,12 +95,15 @@
|
|||||||
"rollup-plugin-size": "^0.2.1",
|
"rollup-plugin-size": "^0.2.1",
|
||||||
"rollup-plugin-size-snapshot": "^0.10.0",
|
"rollup-plugin-size-snapshot": "^0.10.0",
|
||||||
"rollup-plugin-terser": "^5.1.2",
|
"rollup-plugin-terser": "^5.1.2",
|
||||||
"snapshot-diff": "^0.6.1"
|
"serve": "^11.2.0"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"commitizen": {
|
"commitizen": {
|
||||||
"path": "node_modules/cz-conventional-changelog"
|
"path": "node_modules/cz-conventional-changelog"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"browserslist": "> 0.25%, not dead"
|
"browserslist": "> 0.25%, not dead",
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/plugin-transform-runtime": "^7.8.3"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,76 @@
|
|||||||
export function sum(values, rows) {
|
export function sum(values, aggregatedValues) {
|
||||||
return values.reduce((sum, next) => sum + next, 0)
|
// It's faster to just add the aggregations together instead of
|
||||||
|
// process leaf nodes individually
|
||||||
|
return aggregatedValues.reduce(
|
||||||
|
(sum, next) => sum + (typeof next === 'number' ? next : 0),
|
||||||
|
0
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function average(values, rows) {
|
export function min(values) {
|
||||||
return Math.round((sum(values, rows) / values.length) * 100) / 100
|
let min = 0
|
||||||
|
|
||||||
|
values.forEach(value => {
|
||||||
|
if (typeof value === 'number') {
|
||||||
|
min = Math.min(min, value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return min
|
||||||
|
}
|
||||||
|
|
||||||
|
export function max(values) {
|
||||||
|
let max = 0
|
||||||
|
|
||||||
|
values.forEach(value => {
|
||||||
|
if (typeof value === 'number') {
|
||||||
|
max = Math.max(max, value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return max
|
||||||
|
}
|
||||||
|
|
||||||
|
export function minMax(values) {
|
||||||
|
let min = 0
|
||||||
|
let max = 0
|
||||||
|
|
||||||
|
values.forEach(value => {
|
||||||
|
if (typeof value === 'number') {
|
||||||
|
min = Math.min(min, value)
|
||||||
|
max = Math.max(max, value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return `${min}..${max}`
|
||||||
|
}
|
||||||
|
|
||||||
|
export function average(values) {
|
||||||
|
return sum(null, values) / values.length
|
||||||
}
|
}
|
||||||
|
|
||||||
export function median(values) {
|
export function median(values) {
|
||||||
values = values.length ? values : [0]
|
if (!values.length) {
|
||||||
let min = Math.min(...values)
|
return null
|
||||||
let max = Math.max(...values)
|
}
|
||||||
|
|
||||||
|
let min = 0
|
||||||
|
let max = 0
|
||||||
|
|
||||||
|
values.forEach(value => {
|
||||||
|
if (typeof value === 'number') {
|
||||||
|
min = Math.min(min, value)
|
||||||
|
max = Math.max(max, value)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return (min + max) / 2
|
return (min + max) / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function unique(values) {
|
||||||
|
return [...new Set(values).values()]
|
||||||
|
}
|
||||||
|
|
||||||
export function uniqueCount(values) {
|
export function uniqueCount(values) {
|
||||||
return new Set(values).size
|
return new Set(values).size
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,116 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`renders a basic table 1`] = `
|
|
||||||
<DocumentFragment>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
colspan="2"
|
|
||||||
>
|
|
||||||
Name
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="4"
|
|
||||||
>
|
|
||||||
Info
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
First Name
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Last Name
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Age
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Visits
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Status
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Profile Progress
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
tanner
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
29
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
100
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
In Relationship
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
derek
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
perkins
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
40
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
40
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
Single
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
80
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
joe
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
bergevin
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
45
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
20
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
Complicated
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
10
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</DocumentFragment>
|
|
||||||
`;
|
|
||||||
@ -115,14 +115,12 @@ function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test('renders a basic table', () => {
|
test('renders a basic table', () => {
|
||||||
const { getByText, asFragment } = render(<App />)
|
const rtl = render(<App />)
|
||||||
|
|
||||||
expect(getByText('tanner')).toBeInTheDocument()
|
expect(rtl.getByText('tanner')).toBeInTheDocument()
|
||||||
expect(getByText('linsley')).toBeInTheDocument()
|
expect(rtl.getByText('linsley')).toBeInTheDocument()
|
||||||
expect(getByText('29')).toBeInTheDocument()
|
expect(rtl.getByText('29')).toBeInTheDocument()
|
||||||
expect(getByText('100')).toBeInTheDocument()
|
expect(rtl.getByText('100')).toBeInTheDocument()
|
||||||
expect(getByText('In Relationship')).toBeInTheDocument()
|
expect(rtl.getByText('In Relationship')).toBeInTheDocument()
|
||||||
expect(getByText('50')).toBeInTheDocument()
|
expect(rtl.getByText('50')).toBeInTheDocument()
|
||||||
|
|
||||||
expect(asFragment()).toMatchSnapshot()
|
|
||||||
})
|
})
|
||||||
|
|||||||
@ -4,9 +4,8 @@ import {
|
|||||||
actions,
|
actions,
|
||||||
functionalUpdate,
|
functionalUpdate,
|
||||||
useGetLatest,
|
useGetLatest,
|
||||||
useConsumeHookGetter,
|
|
||||||
makePropGetter,
|
makePropGetter,
|
||||||
} from '../utils'
|
} from '../publicUtils'
|
||||||
|
|
||||||
actions.resetHiddenColumns = 'resetHiddenColumns'
|
actions.resetHiddenColumns = 'resetHiddenColumns'
|
||||||
actions.toggleHideColumn = 'toggleHideColumn'
|
actions.toggleHideColumn = 'toggleHideColumn'
|
||||||
@ -104,7 +103,7 @@ function reducer(state, action, previousState, instance) {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
hiddenColumns: shouldAll ? instance.flatColumns.map(d => d.id) : [],
|
hiddenColumns: shouldAll ? instance.allColumns.map(d => d.id) : [],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -150,13 +149,14 @@ function useInstance(instance) {
|
|||||||
const {
|
const {
|
||||||
flatHeaders,
|
flatHeaders,
|
||||||
dispatch,
|
dispatch,
|
||||||
flatColumns,
|
allColumns,
|
||||||
|
getHooks,
|
||||||
state: { hiddenColumns },
|
state: { hiddenColumns },
|
||||||
} = instance
|
} = instance
|
||||||
|
|
||||||
const getInstance = useGetLatest(instance)
|
const getInstance = useGetLatest(instance)
|
||||||
|
|
||||||
const allColumnsHidden = flatColumns.length === hiddenColumns.length
|
const allColumnsHidden = allColumns.length === hiddenColumns.length
|
||||||
|
|
||||||
const toggleHideColumn = React.useCallback(
|
const toggleHideColumn = React.useCallback(
|
||||||
(columnId, value) =>
|
(columnId, value) =>
|
||||||
@ -174,23 +174,11 @@ function useInstance(instance) {
|
|||||||
[dispatch]
|
[dispatch]
|
||||||
)
|
)
|
||||||
|
|
||||||
// Snapshot hook and disallow more from being added
|
|
||||||
const getToggleHideAllColumnsPropsHooks = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
|
||||||
'getToggleHideAllColumnsProps'
|
|
||||||
)
|
|
||||||
|
|
||||||
const getToggleHideAllColumnsProps = makePropGetter(
|
const getToggleHideAllColumnsProps = makePropGetter(
|
||||||
getToggleHideAllColumnsPropsHooks(),
|
getHooks().getToggleHideAllColumnsProps,
|
||||||
{ instance: getInstance() }
|
{ instance: getInstance() }
|
||||||
)
|
)
|
||||||
|
|
||||||
// Snapshot hook and disallow more from being added
|
|
||||||
const getToggleHiddenPropsHooks = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
|
||||||
'getToggleHiddenProps'
|
|
||||||
)
|
|
||||||
|
|
||||||
flatHeaders.forEach(column => {
|
flatHeaders.forEach(column => {
|
||||||
column.toggleHidden = value => {
|
column.toggleHidden = value => {
|
||||||
dispatch({
|
dispatch({
|
||||||
@ -200,10 +188,13 @@ function useInstance(instance) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
column.getToggleHiddenProps = makePropGetter(getToggleHiddenPropsHooks(), {
|
column.getToggleHiddenProps = makePropGetter(
|
||||||
instance: getInstance(),
|
getHooks().getToggleHiddenProps,
|
||||||
column,
|
{
|
||||||
})
|
instance: getInstance(),
|
||||||
|
column,
|
||||||
|
}
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
Object.assign(instance, {
|
Object.assign(instance, {
|
||||||
|
|||||||
@ -2,17 +2,23 @@ import React from 'react'
|
|||||||
|
|
||||||
//
|
//
|
||||||
import {
|
import {
|
||||||
actions,
|
linkColumnStructure,
|
||||||
|
flattenColumns,
|
||||||
|
assignColumnAccessor,
|
||||||
|
accessRowsForColumn,
|
||||||
|
makeHeaderGroups,
|
||||||
|
decorateColumn,
|
||||||
|
dedupeBy,
|
||||||
|
} from '../utils'
|
||||||
|
|
||||||
|
import {
|
||||||
|
useGetLatest,
|
||||||
reduceHooks,
|
reduceHooks,
|
||||||
|
actions,
|
||||||
loopHooks,
|
loopHooks,
|
||||||
makePropGetter,
|
makePropGetter,
|
||||||
makeRenderer,
|
makeRenderer,
|
||||||
decorateColumnTree,
|
} from '../publicUtils'
|
||||||
makeHeaderGroups,
|
|
||||||
flattenBy,
|
|
||||||
useGetLatest,
|
|
||||||
useConsumeHookGetter,
|
|
||||||
} from '../utils'
|
|
||||||
|
|
||||||
import makeDefaultPluginHooks from '../makeDefaultPluginHooks'
|
import makeDefaultPluginHooks from '../makeDefaultPluginHooks'
|
||||||
|
|
||||||
@ -73,15 +79,15 @@ export const useTable = (props, ...plugins) => {
|
|||||||
plugin(getInstance().hooks)
|
plugin(getInstance().hooks)
|
||||||
})
|
})
|
||||||
|
|
||||||
const getUseOptionsHooks = useConsumeHookGetter(
|
// Consume all hooks and make a getter for them
|
||||||
getInstance().hooks,
|
const getHooks = useGetLatest(getInstance().hooks)
|
||||||
'useOptions'
|
getInstance().getHooks = getHooks
|
||||||
)
|
delete getInstance().hooks
|
||||||
|
|
||||||
// Allow useOptions hooks to modify the options coming into the table
|
// Allow useOptions hooks to modify the options coming into the table
|
||||||
Object.assign(
|
Object.assign(
|
||||||
getInstance(),
|
getInstance(),
|
||||||
reduceHooks(getUseOptionsHooks(), applyDefaults(props))
|
reduceHooks(getHooks().useOptions, applyDefaults(props))
|
||||||
)
|
)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -95,12 +101,6 @@ export const useTable = (props, ...plugins) => {
|
|||||||
useControlledState,
|
useControlledState,
|
||||||
} = getInstance()
|
} = getInstance()
|
||||||
|
|
||||||
// Snapshot hook and disallow more from being added
|
|
||||||
const getStateReducers = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
|
||||||
'stateReducers'
|
|
||||||
)
|
|
||||||
|
|
||||||
// Setup user reducer ref
|
// Setup user reducer ref
|
||||||
const getStateReducer = useGetLatest(stateReducer)
|
const getStateReducer = useGetLatest(stateReducer)
|
||||||
|
|
||||||
@ -115,7 +115,7 @@ export const useTable = (props, ...plugins) => {
|
|||||||
|
|
||||||
// Reduce the state from all plugin reducers
|
// Reduce the state from all plugin reducers
|
||||||
return [
|
return [
|
||||||
...getStateReducers(),
|
...getHooks().stateReducers,
|
||||||
// Allow the user to add their own state reducer(s)
|
// Allow the user to add their own state reducer(s)
|
||||||
...(Array.isArray(getStateReducer())
|
...(Array.isArray(getStateReducer())
|
||||||
? getStateReducer()
|
? getStateReducer()
|
||||||
@ -125,7 +125,7 @@ export const useTable = (props, ...plugins) => {
|
|||||||
state
|
state
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
[getStateReducers, getStateReducer, getInstance]
|
[getHooks, getStateReducer, getInstance]
|
||||||
)
|
)
|
||||||
|
|
||||||
// Start the reducer
|
// Start the reducer
|
||||||
@ -133,15 +133,9 @@ export const useTable = (props, ...plugins) => {
|
|||||||
reducer(initialState, { type: actions.init })
|
reducer(initialState, { type: actions.init })
|
||||||
)
|
)
|
||||||
|
|
||||||
// Snapshot hook and disallow more from being added
|
|
||||||
const getUseControlledStateHooks = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
|
||||||
'useControlledState'
|
|
||||||
)
|
|
||||||
|
|
||||||
// Allow the user to control the final state with hooks
|
// Allow the user to control the final state with hooks
|
||||||
const state = reduceHooks(
|
const state = reduceHooks(
|
||||||
[...getUseControlledStateHooks(), useControlledState],
|
[...getHooks().useControlledState, useControlledState],
|
||||||
reducerState,
|
reducerState,
|
||||||
{ instance: getInstance() }
|
{ instance: getInstance() }
|
||||||
)
|
)
|
||||||
@ -151,170 +145,182 @@ export const useTable = (props, ...plugins) => {
|
|||||||
dispatch,
|
dispatch,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Snapshot hook and disallow more from being added
|
|
||||||
const getColumnsHooks = useConsumeHookGetter(getInstance().hooks, 'columns')
|
|
||||||
|
|
||||||
// Snapshot hook and disallow more from being added
|
|
||||||
const getColumnsDepsHooks = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
|
||||||
'columnsDeps'
|
|
||||||
)
|
|
||||||
|
|
||||||
// Decorate All the columns
|
// Decorate All the columns
|
||||||
let columns = React.useMemo(
|
const columns = React.useMemo(
|
||||||
() =>
|
() =>
|
||||||
decorateColumnTree(
|
linkColumnStructure(
|
||||||
reduceHooks(getColumnsHooks(), userColumns, {
|
reduceHooks(getHooks().columns, userColumns, {
|
||||||
instance: getInstance(),
|
instance: getInstance(),
|
||||||
}),
|
})
|
||||||
defaultColumn
|
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
defaultColumn,
|
getHooks,
|
||||||
getColumnsHooks,
|
|
||||||
getInstance,
|
getInstance,
|
||||||
userColumns,
|
userColumns,
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
...reduceHooks(getColumnsDepsHooks(), [], { instance: getInstance() }),
|
...reduceHooks(getHooks().columnsDeps, [], { instance: getInstance() }),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
getInstance().columns = columns
|
getInstance().columns = columns
|
||||||
|
|
||||||
// Get the flat list of all columns and allow hooks to decorate
|
// Get the flat list of all columns and allow hooks to decorate
|
||||||
// those columns (and trigger this memoization via deps)
|
// those columns (and trigger this memoization via deps)
|
||||||
let flatColumns = React.useMemo(() => flattenBy(columns, 'columns'), [
|
let allColumns = React.useMemo(
|
||||||
columns,
|
() =>
|
||||||
])
|
reduceHooks(getHooks().allColumns, flattenColumns(columns), {
|
||||||
|
instance: getInstance(),
|
||||||
|
}).map(assignColumnAccessor),
|
||||||
|
[
|
||||||
|
columns,
|
||||||
|
getHooks,
|
||||||
|
getInstance,
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
...reduceHooks(getHooks().allColumnsDeps, [], {
|
||||||
|
instance: getInstance(),
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
getInstance().allColumns = allColumns
|
||||||
|
|
||||||
getInstance().flatColumns = flatColumns
|
// Access the row model using initial columns
|
||||||
|
const coreDataModel = React.useMemo(() => {
|
||||||
// Access the row model
|
let rows = []
|
||||||
const [rows, flatRows] = React.useMemo(() => {
|
|
||||||
let flatRows = []
|
let flatRows = []
|
||||||
|
const rowsById = {}
|
||||||
|
|
||||||
// Access the row's data
|
const allColumnsQueue = [...allColumns]
|
||||||
const accessRow = (originalRow, i, depth = 0, parent) => {
|
|
||||||
// Keep the original reference around
|
|
||||||
const original = originalRow
|
|
||||||
|
|
||||||
const id = getRowId(originalRow, i, parent)
|
while (allColumnsQueue.length) {
|
||||||
|
const column = allColumnsQueue.shift()
|
||||||
const row = {
|
accessRowsForColumn({
|
||||||
id,
|
data,
|
||||||
original,
|
rows,
|
||||||
index: i,
|
flatRows,
|
||||||
depth,
|
rowsById,
|
||||||
cells: [{}], // This is a dummy cell
|
column,
|
||||||
}
|
getRowId,
|
||||||
|
getSubRows,
|
||||||
flatRows.push(row)
|
accessValueHooks: getHooks().accessValue,
|
||||||
|
getInstance,
|
||||||
// Process any subRows
|
|
||||||
let subRows = getSubRows(originalRow, i)
|
|
||||||
|
|
||||||
if (subRows) {
|
|
||||||
row.subRows = subRows.map((d, i) => accessRow(d, i, depth + 1, row))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Override common array functions (and the dummy cell's getCellProps function)
|
|
||||||
// to show an error if it is accessed without calling prepareRow
|
|
||||||
const unpreparedAccessWarning = () => {
|
|
||||||
throw new Error(
|
|
||||||
'React-Table: You have not called prepareRow(row) one or more rows you are attempting to render.'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
row.cells.map = unpreparedAccessWarning
|
|
||||||
row.cells.filter = unpreparedAccessWarning
|
|
||||||
row.cells.forEach = unpreparedAccessWarning
|
|
||||||
row.cells[0].getCellProps = unpreparedAccessWarning
|
|
||||||
|
|
||||||
// Create the cells and values
|
|
||||||
row.values = {}
|
|
||||||
flatColumns.forEach(({ id, accessor }) => {
|
|
||||||
row.values[id] = accessor
|
|
||||||
? accessor(originalRow, i, { subRows, depth, data })
|
|
||||||
: undefined
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return row
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use the resolved data
|
return { rows, flatRows, rowsById }
|
||||||
const accessedData = data.map((d, i) => accessRow(d, i))
|
}, [allColumns, data, getRowId, getSubRows, getHooks, getInstance])
|
||||||
|
|
||||||
return [accessedData, flatRows]
|
// Allow materialized columns to also access data
|
||||||
}, [data, flatColumns, getRowId, getSubRows])
|
const [rows, flatRows, rowsById, materializedColumns] = React.useMemo(() => {
|
||||||
|
const { rows, flatRows, rowsById } = coreDataModel
|
||||||
|
const materializedColumns = reduceHooks(
|
||||||
|
getHooks().materializedColumns,
|
||||||
|
[],
|
||||||
|
{
|
||||||
|
instance: getInstance(),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
getInstance().rows = rows
|
materializedColumns.forEach(d => assignColumnAccessor(d))
|
||||||
getInstance().flatRows = flatRows
|
|
||||||
|
|
||||||
// Snapshot hook and disallow more from being added
|
const materializedColumnsQueue = [...materializedColumns]
|
||||||
const flatColumnsHooks = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
while (materializedColumnsQueue.length) {
|
||||||
'flatColumns'
|
const column = materializedColumnsQueue.shift()
|
||||||
)
|
accessRowsForColumn({
|
||||||
|
data,
|
||||||
// Snapshot hook and disallow more from being added
|
rows,
|
||||||
const flatColumnsDepsHooks = useConsumeHookGetter(
|
flatRows,
|
||||||
getInstance().hooks,
|
rowsById,
|
||||||
'flatColumnsDeps'
|
column,
|
||||||
|
getRowId,
|
||||||
|
getSubRows,
|
||||||
|
accessValueHooks: getHooks().accessValue,
|
||||||
|
getInstance,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return [rows, flatRows, rowsById, materializedColumns]
|
||||||
|
}, [
|
||||||
|
coreDataModel,
|
||||||
|
getHooks,
|
||||||
|
getInstance,
|
||||||
|
data,
|
||||||
|
getRowId,
|
||||||
|
getSubRows,
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
...reduceHooks(getHooks().materializedColumnsDeps, [], {
|
||||||
|
instance: getInstance(),
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
Object.assign(getInstance(), {
|
||||||
|
rows,
|
||||||
|
flatRows,
|
||||||
|
rowsById,
|
||||||
|
materializedColumns,
|
||||||
|
})
|
||||||
|
|
||||||
|
loopHooks(getHooks().useInstanceAfterData, getInstance())
|
||||||
|
|
||||||
|
// Combine new materialized columns with all columns (dedupe prefers later columns)
|
||||||
|
allColumns = React.useMemo(
|
||||||
|
() => dedupeBy([...allColumns, ...materializedColumns], d => d.id),
|
||||||
|
[allColumns, materializedColumns]
|
||||||
)
|
)
|
||||||
|
getInstance().allColumns = allColumns
|
||||||
|
|
||||||
// Get the flat list of all columns AFTER the rows
|
// Get the flat list of all columns AFTER the rows
|
||||||
// have been access, and allow hooks to decorate
|
// have been access, and allow hooks to decorate
|
||||||
// those columns (and trigger this memoization via deps)
|
// those columns (and trigger this memoization via deps)
|
||||||
flatColumns = React.useMemo(
|
let visibleColumns = React.useMemo(
|
||||||
() =>
|
() =>
|
||||||
reduceHooks(flatColumnsHooks(), flatColumns, { instance: getInstance() }),
|
reduceHooks(getHooks().visibleColumns, allColumns, {
|
||||||
|
instance: getInstance(),
|
||||||
|
}).map(d => decorateColumn(d, defaultColumn)),
|
||||||
[
|
[
|
||||||
flatColumns,
|
getHooks,
|
||||||
flatColumnsHooks,
|
allColumns,
|
||||||
getInstance,
|
getInstance,
|
||||||
|
defaultColumn,
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
...reduceHooks(flatColumnsDepsHooks(), [], { instance: getInstance() }),
|
...reduceHooks(getHooks().visibleColumnsDeps, [], {
|
||||||
|
instance: getInstance(),
|
||||||
|
}),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
getInstance().flatColumns = flatColumns
|
// Combine new visible columns with all columns (dedupe prefers later columns)
|
||||||
|
allColumns = React.useMemo(
|
||||||
// Snapshot hook and disallow more from being added
|
() => dedupeBy([...allColumns, ...visibleColumns], d => d.id),
|
||||||
const getHeaderGroups = useConsumeHookGetter(
|
[allColumns, visibleColumns]
|
||||||
getInstance().hooks,
|
|
||||||
'headerGroups'
|
|
||||||
)
|
|
||||||
|
|
||||||
// Snapshot hook and disallow more from being added
|
|
||||||
const getHeaderGroupsDeps = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
|
||||||
'headerGroupsDeps'
|
|
||||||
)
|
)
|
||||||
|
getInstance().allColumns = allColumns
|
||||||
|
|
||||||
// Make the headerGroups
|
// Make the headerGroups
|
||||||
const headerGroups = React.useMemo(
|
const headerGroups = React.useMemo(
|
||||||
() =>
|
() =>
|
||||||
reduceHooks(
|
reduceHooks(
|
||||||
getHeaderGroups(),
|
getHooks().headerGroups,
|
||||||
makeHeaderGroups(flatColumns, defaultColumn),
|
makeHeaderGroups(visibleColumns, defaultColumn),
|
||||||
getInstance()
|
getInstance()
|
||||||
),
|
),
|
||||||
[
|
[
|
||||||
|
getHooks,
|
||||||
|
visibleColumns,
|
||||||
defaultColumn,
|
defaultColumn,
|
||||||
flatColumns,
|
|
||||||
getHeaderGroups,
|
|
||||||
getInstance,
|
getInstance,
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
...reduceHooks(getHeaderGroupsDeps(), [], { instance: getInstance() }),
|
...reduceHooks(getHooks().headerGroupsDeps, [], {
|
||||||
|
instance: getInstance(),
|
||||||
|
}),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
getInstance().headerGroups = headerGroups
|
getInstance().headerGroups = headerGroups
|
||||||
|
|
||||||
|
// Get the first level of headers
|
||||||
const headers = React.useMemo(
|
const headers = React.useMemo(
|
||||||
() => (headerGroups.length ? headerGroups[0].headers : []),
|
() => (headerGroups.length ? headerGroups[0].headers : []),
|
||||||
[headerGroups]
|
[headerGroups]
|
||||||
)
|
)
|
||||||
|
|
||||||
getInstance().headers = headers
|
getInstance().headers = headers
|
||||||
|
|
||||||
// Provide a flat header list for utilities
|
// Provide a flat header list for utilities
|
||||||
@ -323,13 +329,21 @@ export const useTable = (props, ...plugins) => {
|
|||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
|
||||||
// Snapshot hook and disallow more from being added
|
loopHooks(getHooks().useInstanceBeforeDimensions, getInstance())
|
||||||
const getUseInstanceBeforeDimensions = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
|
||||||
'useInstanceBeforeDimensions'
|
|
||||||
)
|
|
||||||
|
|
||||||
loopHooks(getUseInstanceBeforeDimensions(), getInstance())
|
// Filter columns down to visible ones
|
||||||
|
const visibleColumnsDep = visibleColumns
|
||||||
|
.filter(d => d.isVisible)
|
||||||
|
.map(d => d.id)
|
||||||
|
.sort()
|
||||||
|
.join('_')
|
||||||
|
|
||||||
|
visibleColumns = React.useMemo(
|
||||||
|
() => visibleColumns.filter(d => d.isVisible),
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
[visibleColumns, visibleColumnsDep]
|
||||||
|
)
|
||||||
|
getInstance().visibleColumns = visibleColumns
|
||||||
|
|
||||||
// Header Visibility is needed by this point
|
// Header Visibility is needed by this point
|
||||||
const [
|
const [
|
||||||
@ -342,59 +356,29 @@ export const useTable = (props, ...plugins) => {
|
|||||||
getInstance().totalColumnsWidth = totalColumnsWidth
|
getInstance().totalColumnsWidth = totalColumnsWidth
|
||||||
getInstance().totalColumnsMaxWidth = totalColumnsMaxWidth
|
getInstance().totalColumnsMaxWidth = totalColumnsMaxWidth
|
||||||
|
|
||||||
// Snapshot hook and disallow more from being added
|
loopHooks(getHooks().useInstance, getInstance())
|
||||||
const getUseInstance = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
|
||||||
'useInstance'
|
|
||||||
)
|
|
||||||
|
|
||||||
loopHooks(getUseInstance(), getInstance())
|
|
||||||
|
|
||||||
// Snapshot hook and disallow more from being added
|
|
||||||
const getHeaderPropsHooks = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
|
||||||
'getHeaderProps'
|
|
||||||
)
|
|
||||||
|
|
||||||
// Snapshot hook and disallow more from being added
|
|
||||||
const getFooterPropsHooks = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
|
||||||
'getFooterProps'
|
|
||||||
)
|
|
||||||
|
|
||||||
// Each materialized header needs to be assigned a render function and other
|
// Each materialized header needs to be assigned a render function and other
|
||||||
// prop getter properties here.
|
// prop getter properties here.
|
||||||
;[...getInstance().flatHeaders, ...getInstance().flatColumns].forEach(
|
;[...getInstance().flatHeaders, ...getInstance().allColumns].forEach(
|
||||||
column => {
|
column => {
|
||||||
// Give columns/headers rendering power
|
// Give columns/headers rendering power
|
||||||
column.render = makeRenderer(getInstance(), column)
|
column.render = makeRenderer(getInstance(), column)
|
||||||
|
|
||||||
// Give columns/headers a default getHeaderProps
|
// Give columns/headers a default getHeaderProps
|
||||||
column.getHeaderProps = makePropGetter(getHeaderPropsHooks(), {
|
column.getHeaderProps = makePropGetter(getHooks().getHeaderProps, {
|
||||||
instance: getInstance(),
|
instance: getInstance(),
|
||||||
column,
|
column,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Give columns/headers a default getFooterProps
|
// Give columns/headers a default getFooterProps
|
||||||
column.getFooterProps = makePropGetter(getFooterPropsHooks(), {
|
column.getFooterProps = makePropGetter(getHooks().getFooterProps, {
|
||||||
instance: getInstance(),
|
instance: getInstance(),
|
||||||
column,
|
column,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Snapshot hook and disallow more from being added
|
|
||||||
const getHeaderGroupPropsHooks = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
|
||||||
'getHeaderGroupProps'
|
|
||||||
)
|
|
||||||
|
|
||||||
// Snapshot hook and disallow more from being added
|
|
||||||
const getFooterGroupPropsHooks = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
|
||||||
'getFooterGroupProps'
|
|
||||||
)
|
|
||||||
|
|
||||||
getInstance().headerGroups = getInstance().headerGroups.filter(
|
getInstance().headerGroups = getInstance().headerGroups.filter(
|
||||||
(headerGroup, i) => {
|
(headerGroup, i) => {
|
||||||
// Filter out any headers and headerGroups that don't have visible columns
|
// Filter out any headers and headerGroups that don't have visible columns
|
||||||
@ -415,12 +399,12 @@ export const useTable = (props, ...plugins) => {
|
|||||||
// Give headerGroups getRowProps
|
// Give headerGroups getRowProps
|
||||||
if (headerGroup.headers.length) {
|
if (headerGroup.headers.length) {
|
||||||
headerGroup.getHeaderGroupProps = makePropGetter(
|
headerGroup.getHeaderGroupProps = makePropGetter(
|
||||||
getHeaderGroupPropsHooks(),
|
getHooks().getHeaderGroupProps,
|
||||||
{ instance: getInstance(), headerGroup, index: i }
|
{ instance: getInstance(), headerGroup, index: i }
|
||||||
)
|
)
|
||||||
|
|
||||||
headerGroup.getFooterGroupProps = makePropGetter(
|
headerGroup.getFooterGroupProps = makePropGetter(
|
||||||
getFooterGroupPropsHooks(),
|
getHooks().getFooterGroupProps,
|
||||||
{ instance: getInstance(), headerGroup, index: i }
|
{ instance: getInstance(), headerGroup, index: i }
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -433,48 +417,18 @@ export const useTable = (props, ...plugins) => {
|
|||||||
|
|
||||||
getInstance().footerGroups = [...getInstance().headerGroups].reverse()
|
getInstance().footerGroups = [...getInstance().headerGroups].reverse()
|
||||||
|
|
||||||
// Run the rows (this could be a dangerous hook with a ton of data)
|
|
||||||
|
|
||||||
// Snapshot hook and disallow more from being added
|
|
||||||
const getUseRowsHooks = useConsumeHookGetter(getInstance().hooks, 'useRows')
|
|
||||||
|
|
||||||
getInstance().rows = reduceHooks(getUseRowsHooks(), getInstance().rows, {
|
|
||||||
instance: getInstance(),
|
|
||||||
})
|
|
||||||
|
|
||||||
// The prepareRow function is absolutely necessary and MUST be called on
|
// The prepareRow function is absolutely necessary and MUST be called on
|
||||||
// any rows the user wishes to be displayed.
|
// any rows the user wishes to be displayed.
|
||||||
|
|
||||||
// Snapshot hook and disallow more from being added
|
|
||||||
const getPrepareRowHooks = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
|
||||||
'prepareRow'
|
|
||||||
)
|
|
||||||
|
|
||||||
// Snapshot hook and disallow more from being added
|
|
||||||
const getRowPropsHooks = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
|
||||||
'getRowProps'
|
|
||||||
)
|
|
||||||
|
|
||||||
// Snapshot hook and disallow more from being added
|
|
||||||
const getCellPropsHooks = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
|
||||||
'getCellProps'
|
|
||||||
)
|
|
||||||
|
|
||||||
// Snapshot hook and disallow more from being added
|
|
||||||
const cellsHooks = useConsumeHookGetter(getInstance().hooks, 'cells')
|
|
||||||
|
|
||||||
getInstance().prepareRow = React.useCallback(
|
getInstance().prepareRow = React.useCallback(
|
||||||
row => {
|
row => {
|
||||||
row.getRowProps = makePropGetter(getRowPropsHooks(), {
|
row.getRowProps = makePropGetter(getHooks().getRowProps, {
|
||||||
instance: getInstance(),
|
instance: getInstance(),
|
||||||
row,
|
row,
|
||||||
})
|
})
|
||||||
|
|
||||||
// Build the visible cells for each row
|
// Build the visible cells for each row
|
||||||
row.allCells = flatColumns.map(column => {
|
row.allCells = allColumns.map(column => {
|
||||||
const cell = {
|
const cell = {
|
||||||
column,
|
column,
|
||||||
row,
|
row,
|
||||||
@ -482,7 +436,7 @@ export const useTable = (props, ...plugins) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Give each cell a getCellProps base
|
// Give each cell a getCellProps base
|
||||||
cell.getCellProps = makePropGetter(getCellPropsHooks(), {
|
cell.getCellProps = makePropGetter(getHooks().getCellProps, {
|
||||||
instance: getInstance(),
|
instance: getInstance(),
|
||||||
cell,
|
cell,
|
||||||
})
|
})
|
||||||
@ -496,50 +450,28 @@ export const useTable = (props, ...plugins) => {
|
|||||||
return cell
|
return cell
|
||||||
})
|
})
|
||||||
|
|
||||||
row.cells = reduceHooks(cellsHooks(), row.allCells, {
|
row.cells = visibleColumns.map(column =>
|
||||||
instance: getInstance(),
|
row.allCells.find(cell => cell.column.id === column.id)
|
||||||
})
|
)
|
||||||
|
|
||||||
// need to apply any row specific hooks (useExpanded requires this)
|
// need to apply any row specific hooks (useExpanded requires this)
|
||||||
loopHooks(getPrepareRowHooks(), row, { instance: getInstance() })
|
loopHooks(getHooks().prepareRow, row, { instance: getInstance() })
|
||||||
},
|
},
|
||||||
[
|
[getHooks, getInstance, allColumns, visibleColumns]
|
||||||
getRowPropsHooks,
|
|
||||||
getInstance,
|
|
||||||
flatColumns,
|
|
||||||
cellsHooks,
|
|
||||||
getPrepareRowHooks,
|
|
||||||
getCellPropsHooks,
|
|
||||||
]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Snapshot hook and disallow more from being added
|
getInstance().getTableProps = makePropGetter(getHooks().getTableProps, {
|
||||||
const getTablePropsHooks = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
|
||||||
'getTableProps'
|
|
||||||
)
|
|
||||||
|
|
||||||
getInstance().getTableProps = makePropGetter(getTablePropsHooks(), {
|
|
||||||
instance: getInstance(),
|
instance: getInstance(),
|
||||||
})
|
})
|
||||||
|
|
||||||
// Snapshot hook and disallow more from being added
|
getInstance().getTableBodyProps = makePropGetter(
|
||||||
const getTableBodyPropsHooks = useConsumeHookGetter(
|
getHooks().getTableBodyProps,
|
||||||
getInstance().hooks,
|
{
|
||||||
'getTableBodyProps'
|
instance: getInstance(),
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
getInstance().getTableBodyProps = makePropGetter(getTableBodyPropsHooks(), {
|
loopHooks(getHooks().useFinalInstance, getInstance())
|
||||||
instance: getInstance(),
|
|
||||||
})
|
|
||||||
|
|
||||||
// Snapshot hook and disallow more from being added
|
|
||||||
const getUseFinalInstanceHooks = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
|
||||||
'useFinalInstance'
|
|
||||||
)
|
|
||||||
|
|
||||||
loopHooks(getUseFinalInstanceHooks(), getInstance())
|
|
||||||
|
|
||||||
return getInstance()
|
return getInstance()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ export { useGlobalFilter } from './plugin-hooks/useGlobalFilter'
|
|||||||
export { useGroupBy } from './plugin-hooks/useGroupBy'
|
export { useGroupBy } from './plugin-hooks/useGroupBy'
|
||||||
export { useSortBy } from './plugin-hooks/useSortBy'
|
export { useSortBy } from './plugin-hooks/useSortBy'
|
||||||
export { usePagination } from './plugin-hooks/usePagination'
|
export { usePagination } from './plugin-hooks/usePagination'
|
||||||
|
export { usePivotColumns as _UNSTABLE_usePivoteColumns } from './plugin-hooks/usePivotColumns'
|
||||||
export { useRowSelect } from './plugin-hooks/useRowSelect'
|
export { useRowSelect } from './plugin-hooks/useRowSelect'
|
||||||
export { useRowState } from './plugin-hooks/useRowState'
|
export { useRowState } from './plugin-hooks/useRowState'
|
||||||
export { useColumnOrder } from './plugin-hooks/useColumnOrder'
|
export { useColumnOrder } from './plugin-hooks/useColumnOrder'
|
||||||
|
|||||||
@ -1,8 +1,17 @@
|
|||||||
const defaultCells = cell => cell.filter(d => d.column.isVisible)
|
const defaultGetTableProps = props => ({
|
||||||
|
role: 'table',
|
||||||
|
...props,
|
||||||
|
})
|
||||||
|
|
||||||
|
const defaultGetTableBodyProps = props => ({
|
||||||
|
role: 'rowgroup',
|
||||||
|
...props,
|
||||||
|
})
|
||||||
|
|
||||||
const defaultGetHeaderProps = (props, { column }) => ({
|
const defaultGetHeaderProps = (props, { column }) => ({
|
||||||
key: `header_${column.id}`,
|
key: `header_${column.id}`,
|
||||||
colSpan: column.totalVisibleHeaderCount,
|
colSpan: column.totalVisibleHeaderCount,
|
||||||
|
role: 'columnheader',
|
||||||
...props,
|
...props,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -14,6 +23,7 @@ const defaultGetFooterProps = (props, { column }) => ({
|
|||||||
|
|
||||||
const defaultGetHeaderGroupProps = (props, { index }) => ({
|
const defaultGetHeaderGroupProps = (props, { index }) => ({
|
||||||
key: `headerGroup_${index}`,
|
key: `headerGroup_${index}`,
|
||||||
|
role: 'row',
|
||||||
...props,
|
...props,
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -24,12 +34,14 @@ const defaultGetFooterGroupProps = (props, { index }) => ({
|
|||||||
|
|
||||||
const defaultGetRowProps = (props, { row }) => ({
|
const defaultGetRowProps = (props, { row }) => ({
|
||||||
key: `row_${row.id}`,
|
key: `row_${row.id}`,
|
||||||
|
role: 'row',
|
||||||
...props,
|
...props,
|
||||||
})
|
})
|
||||||
|
|
||||||
const defaultGetCellProps = (props, { cell }) => ({
|
const defaultGetCellProps = (props, { cell }) => ({
|
||||||
...props,
|
|
||||||
key: `cell_${cell.row.id}_${cell.column.id}`,
|
key: `cell_${cell.row.id}_${cell.column.id}`,
|
||||||
|
role: 'cell',
|
||||||
|
...props,
|
||||||
})
|
})
|
||||||
|
|
||||||
export default function makeDefaultPluginHooks() {
|
export default function makeDefaultPluginHooks() {
|
||||||
@ -39,17 +51,21 @@ export default function makeDefaultPluginHooks() {
|
|||||||
useControlledState: [],
|
useControlledState: [],
|
||||||
columns: [],
|
columns: [],
|
||||||
columnsDeps: [],
|
columnsDeps: [],
|
||||||
flatColumns: [],
|
allColumns: [],
|
||||||
flatColumnsDeps: [],
|
allColumnsDeps: [],
|
||||||
|
accessValue: [],
|
||||||
|
materializedColumns: [],
|
||||||
|
materializedColumnsDeps: [],
|
||||||
|
useInstanceAfterData: [],
|
||||||
|
visibleColumns: [],
|
||||||
|
visibleColumnsDeps: [],
|
||||||
headerGroups: [],
|
headerGroups: [],
|
||||||
headerGroupsDeps: [],
|
headerGroupsDeps: [],
|
||||||
useInstanceBeforeDimensions: [],
|
useInstanceBeforeDimensions: [],
|
||||||
useInstance: [],
|
useInstance: [],
|
||||||
useRows: [],
|
|
||||||
cells: [defaultCells],
|
|
||||||
prepareRow: [],
|
prepareRow: [],
|
||||||
getTableProps: [],
|
getTableProps: [defaultGetTableProps],
|
||||||
getTableBodyProps: [],
|
getTableBodyProps: [defaultGetTableBodyProps],
|
||||||
getHeaderGroupProps: [defaultGetHeaderGroupProps],
|
getHeaderGroupProps: [defaultGetHeaderGroupProps],
|
||||||
getFooterGroupProps: [defaultGetFooterGroupProps],
|
getFooterGroupProps: [defaultGetFooterGroupProps],
|
||||||
getHeaderProps: [defaultGetHeaderProps],
|
getHeaderProps: [defaultGetHeaderProps],
|
||||||
|
|||||||
@ -1,205 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`renders a table 1`] = `
|
|
||||||
<DocumentFragment>
|
|
||||||
<div
|
|
||||||
class="table"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
class="row"
|
|
||||||
style="position: relative; width: 1400px;"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="cell header"
|
|
||||||
colspan="2"
|
|
||||||
style="position: absolute; top: 0px; left: 0px; width: 550px;"
|
|
||||||
>
|
|
||||||
Name
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell header"
|
|
||||||
colspan="4"
|
|
||||||
style="position: absolute; top: 0px; left: 550px; width: 850px;"
|
|
||||||
>
|
|
||||||
Info
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="row"
|
|
||||||
style="position: relative; width: 1400px;"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="cell header"
|
|
||||||
colspan="1"
|
|
||||||
style="position: absolute; top: 0px; left: 0px; width: 250px;"
|
|
||||||
>
|
|
||||||
First Name
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell header"
|
|
||||||
colspan="1"
|
|
||||||
style="position: absolute; top: 0px; left: 250px; width: 300px;"
|
|
||||||
>
|
|
||||||
Last Name
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell header"
|
|
||||||
colspan="1"
|
|
||||||
style="position: absolute; top: 0px; left: 550px; width: 300px;"
|
|
||||||
>
|
|
||||||
Age
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell header"
|
|
||||||
colspan="1"
|
|
||||||
style="position: absolute; top: 0px; left: 850px; width: 150px;"
|
|
||||||
>
|
|
||||||
Visits
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell header"
|
|
||||||
colspan="1"
|
|
||||||
style="position: absolute; top: 0px; left: 1000px; width: 200px;"
|
|
||||||
>
|
|
||||||
Status
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell header"
|
|
||||||
colspan="1"
|
|
||||||
style="position: absolute; top: 0px; left: 1200px; width: 200px;"
|
|
||||||
>
|
|
||||||
Profile Progress
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style="position: relative; width: 1400px;"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="row"
|
|
||||||
style="position: relative; width: 1400px;"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="position: absolute; top: 0px; left: 0px; width: 250px;"
|
|
||||||
>
|
|
||||||
firstName: tanner
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="position: absolute; top: 0px; left: 250px; width: 300px;"
|
|
||||||
>
|
|
||||||
lastName: linsley
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="position: absolute; top: 0px; left: 550px; width: 300px;"
|
|
||||||
>
|
|
||||||
age: 29
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="position: absolute; top: 0px; left: 850px; width: 150px;"
|
|
||||||
>
|
|
||||||
visits: 100
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="position: absolute; top: 0px; left: 1000px; width: 200px;"
|
|
||||||
>
|
|
||||||
status: In Relationship
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="position: absolute; top: 0px; left: 1200px; width: 200px;"
|
|
||||||
>
|
|
||||||
progress: 50
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="row"
|
|
||||||
style="position: relative; width: 1400px;"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="position: absolute; top: 0px; left: 0px; width: 250px;"
|
|
||||||
>
|
|
||||||
firstName: derek
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="position: absolute; top: 0px; left: 250px; width: 300px;"
|
|
||||||
>
|
|
||||||
lastName: perkins
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="position: absolute; top: 0px; left: 550px; width: 300px;"
|
|
||||||
>
|
|
||||||
age: 30
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="position: absolute; top: 0px; left: 850px; width: 150px;"
|
|
||||||
>
|
|
||||||
visits: 40
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="position: absolute; top: 0px; left: 1000px; width: 200px;"
|
|
||||||
>
|
|
||||||
status: Single
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="position: absolute; top: 0px; left: 1200px; width: 200px;"
|
|
||||||
>
|
|
||||||
progress: 80
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="row"
|
|
||||||
style="position: relative; width: 1400px;"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="position: absolute; top: 0px; left: 0px; width: 250px;"
|
|
||||||
>
|
|
||||||
firstName: joe
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="position: absolute; top: 0px; left: 250px; width: 300px;"
|
|
||||||
>
|
|
||||||
lastName: bergevin
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="position: absolute; top: 0px; left: 550px; width: 300px;"
|
|
||||||
>
|
|
||||||
age: 45
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="position: absolute; top: 0px; left: 850px; width: 150px;"
|
|
||||||
>
|
|
||||||
visits: 20
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="position: absolute; top: 0px; left: 1000px; width: 200px;"
|
|
||||||
>
|
|
||||||
status: Complicated
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="position: absolute; top: 0px; left: 1200px; width: 200px;"
|
|
||||||
>
|
|
||||||
progress: 10
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DocumentFragment>
|
|
||||||
`;
|
|
||||||
@ -1,203 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`renders a table 1`] = `
|
|
||||||
<DocumentFragment>
|
|
||||||
<div
|
|
||||||
class="table"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
class="row"
|
|
||||||
style="display: flex; width: 1400px;"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="cell header"
|
|
||||||
colspan="2"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 550px;"
|
|
||||||
>
|
|
||||||
Name
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell header"
|
|
||||||
colspan="4"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 850px;"
|
|
||||||
>
|
|
||||||
Info
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="row"
|
|
||||||
style="display: flex; width: 1400px;"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="cell header"
|
|
||||||
colspan="1"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 250px;"
|
|
||||||
>
|
|
||||||
First Name
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell header"
|
|
||||||
colspan="1"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 300px;"
|
|
||||||
>
|
|
||||||
Last Name
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell header"
|
|
||||||
colspan="1"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 300px;"
|
|
||||||
>
|
|
||||||
Age
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell header"
|
|
||||||
colspan="1"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 150px;"
|
|
||||||
>
|
|
||||||
Visits
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell header"
|
|
||||||
colspan="1"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 200px;"
|
|
||||||
>
|
|
||||||
Status
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell header"
|
|
||||||
colspan="1"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 200px;"
|
|
||||||
>
|
|
||||||
Profile Progress
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<div
|
|
||||||
class="row"
|
|
||||||
style="display: flex; width: 1400px;"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 250px;"
|
|
||||||
>
|
|
||||||
firstName: tanner
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 300px;"
|
|
||||||
>
|
|
||||||
lastName: linsley
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 300px;"
|
|
||||||
>
|
|
||||||
age: 29
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 150px;"
|
|
||||||
>
|
|
||||||
visits: 100
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 200px;"
|
|
||||||
>
|
|
||||||
status: In Relationship
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 200px;"
|
|
||||||
>
|
|
||||||
progress: 50
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="row"
|
|
||||||
style="display: flex; width: 1400px;"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 250px;"
|
|
||||||
>
|
|
||||||
firstName: derek
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 300px;"
|
|
||||||
>
|
|
||||||
lastName: perkins
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 300px;"
|
|
||||||
>
|
|
||||||
age: 30
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 150px;"
|
|
||||||
>
|
|
||||||
visits: 40
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 200px;"
|
|
||||||
>
|
|
||||||
status: Single
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 200px;"
|
|
||||||
>
|
|
||||||
progress: 80
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="row"
|
|
||||||
style="display: flex; width: 1400px;"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 250px;"
|
|
||||||
>
|
|
||||||
firstName: joe
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 300px;"
|
|
||||||
>
|
|
||||||
lastName: bergevin
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 300px;"
|
|
||||||
>
|
|
||||||
age: 45
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 150px;"
|
|
||||||
>
|
|
||||||
visits: 20
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 200px;"
|
|
||||||
>
|
|
||||||
status: Complicated
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cell"
|
|
||||||
style="display: inline-block; box-sizing: border-box; width: 200px;"
|
|
||||||
>
|
|
||||||
progress: 10
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DocumentFragment>
|
|
||||||
`;
|
|
||||||
@ -1,911 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`renders an expandable table 1`] = `
|
|
||||||
Snapshot Diff:
|
|
||||||
- First value
|
|
||||||
+ Second value
|
|
||||||
|
|
||||||
@@ -1,10 +1,12 @@
|
|
||||||
<DocumentFragment>
|
|
||||||
<pre>
|
|
||||||
<code>
|
|
||||||
{
|
|
||||||
- "expanded": {}
|
|
||||||
+ "expanded": {
|
|
||||||
+ "0": true
|
|
||||||
+ }
|
|
||||||
}
|
|
||||||
</code>
|
|
||||||
</pre>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
@@ -64,10 +66,37 @@
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<span
|
|
||||||
style="cursor: pointer; padding-left: 0rem;"
|
|
||||||
+ >
|
|
||||||
+ 👇
|
|
||||||
+ </span>
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ tanner
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ linsley
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 29
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 100
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ In Relationship
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 50
|
|
||||||
+ </td>
|
|
||||||
+ </tr>
|
|
||||||
+ <tr>
|
|
||||||
+ <td>
|
|
||||||
+ <span
|
|
||||||
+ style="cursor: pointer; padding-left: 2rem;"
|
|
||||||
>
|
|
||||||
👉
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -85,10 +114,91 @@
|
|
||||||
<td>
|
|
||||||
In Relationship
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
50
|
|
||||||
+ </td>
|
|
||||||
+ </tr>
|
|
||||||
+ <tr>
|
|
||||||
+ <td>
|
|
||||||
+ <span
|
|
||||||
+ style="cursor: pointer; padding-left: 2rem;"
|
|
||||||
+ >
|
|
||||||
+ 👉
|
|
||||||
+ </span>
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ derek
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ perkins
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 40
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 40
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ Single
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 80
|
|
||||||
+ </td>
|
|
||||||
+ </tr>
|
|
||||||
+ <tr>
|
|
||||||
+ <td>
|
|
||||||
+ <span
|
|
||||||
+ style="cursor: pointer; padding-left: 2rem;"
|
|
||||||
+ >
|
|
||||||
+ 👉
|
|
||||||
+ </span>
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ joe
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ bergevin
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 45
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 20
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ Complicated
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 10
|
|
||||||
+ </td>
|
|
||||||
+ </tr>
|
|
||||||
+ <tr>
|
|
||||||
+ <td>
|
|
||||||
+ <span
|
|
||||||
+ style="cursor: pointer; padding-left: 2rem;"
|
|
||||||
+ >
|
|
||||||
+ 👉
|
|
||||||
+ </span>
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ jaylen
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ linsley
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 26
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 99
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ In Relationship
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 70
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<span
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`renders an expandable table 2`] = `
|
|
||||||
Snapshot Diff:
|
|
||||||
- First value
|
|
||||||
+ Second value
|
|
||||||
|
|
||||||
@@ -1,11 +1,12 @@
|
|
||||||
<DocumentFragment>
|
|
||||||
<pre>
|
|
||||||
<code>
|
|
||||||
{
|
|
||||||
"expanded": {
|
|
||||||
- "0": true
|
|
||||||
+ "0": true,
|
|
||||||
+ "0.0": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</code>
|
|
||||||
</pre>
|
|
||||||
<table>
|
|
||||||
@@ -94,10 +95,37 @@
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<span
|
|
||||||
style="cursor: pointer; padding-left: 2rem;"
|
|
||||||
>
|
|
||||||
+ 👇
|
|
||||||
+ </span>
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ tanner
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ linsley
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 29
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 100
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ In Relationship
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 50
|
|
||||||
+ </td>
|
|
||||||
+ </tr>
|
|
||||||
+ <tr>
|
|
||||||
+ <td>
|
|
||||||
+ <span
|
|
||||||
+ style="cursor: pointer; padding-left: 4rem;"
|
|
||||||
+ >
|
|
||||||
👉
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
tanner
|
|
||||||
@@ -114,10 +142,91 @@
|
|
||||||
<td>
|
|
||||||
In Relationship
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
50
|
|
||||||
+ </td>
|
|
||||||
+ </tr>
|
|
||||||
+ <tr>
|
|
||||||
+ <td>
|
|
||||||
+ <span
|
|
||||||
+ style="cursor: pointer; padding-left: 4rem;"
|
|
||||||
+ >
|
|
||||||
+ 👉
|
|
||||||
+ </span>
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ derek
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ perkins
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 40
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 40
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ Single
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 80
|
|
||||||
+ </td>
|
|
||||||
+ </tr>
|
|
||||||
+ <tr>
|
|
||||||
+ <td>
|
|
||||||
+ <span
|
|
||||||
+ style="cursor: pointer; padding-left: 4rem;"
|
|
||||||
+ >
|
|
||||||
+ 👉
|
|
||||||
+ </span>
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ joe
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ bergevin
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 45
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 20
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ Complicated
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 10
|
|
||||||
+ </td>
|
|
||||||
+ </tr>
|
|
||||||
+ <tr>
|
|
||||||
+ <td>
|
|
||||||
+ <span
|
|
||||||
+ style="cursor: pointer; padding-left: 4rem;"
|
|
||||||
+ >
|
|
||||||
+ 👉
|
|
||||||
+ </span>
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ jaylen
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ linsley
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 26
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 99
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ In Relationship
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 70
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<span
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`renders an expandable table 3`] = `
|
|
||||||
Snapshot Diff:
|
|
||||||
- First value
|
|
||||||
+ Second value
|
|
||||||
|
|
||||||
@@ -2,11 +2,12 @@
|
|
||||||
<pre>
|
|
||||||
<code>
|
|
||||||
{
|
|
||||||
"expanded": {
|
|
||||||
"0": true,
|
|
||||||
- "0.0": true
|
|
||||||
+ "0.0": true,
|
|
||||||
+ "0.0.0": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</code>
|
|
||||||
</pre>
|
|
||||||
<table>
|
|
||||||
@@ -122,10 +123,37 @@
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<span
|
|
||||||
style="cursor: pointer; padding-left: 4rem;"
|
|
||||||
>
|
|
||||||
+ 👇
|
|
||||||
+ </span>
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ tanner
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ linsley
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 29
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 100
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ In Relationship
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 50
|
|
||||||
+ </td>
|
|
||||||
+ </tr>
|
|
||||||
+ <tr>
|
|
||||||
+ <td>
|
|
||||||
+ <span
|
|
||||||
+ style="cursor: pointer; padding-left: 6rem;"
|
|
||||||
+ >
|
|
||||||
👉
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
tanner
|
|
||||||
@@ -142,10 +170,91 @@
|
|
||||||
<td>
|
|
||||||
In Relationship
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
50
|
|
||||||
+ </td>
|
|
||||||
+ </tr>
|
|
||||||
+ <tr>
|
|
||||||
+ <td>
|
|
||||||
+ <span
|
|
||||||
+ style="cursor: pointer; padding-left: 6rem;"
|
|
||||||
+ >
|
|
||||||
+ 👉
|
|
||||||
+ </span>
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ derek
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ perkins
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 40
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 40
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ Single
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 80
|
|
||||||
+ </td>
|
|
||||||
+ </tr>
|
|
||||||
+ <tr>
|
|
||||||
+ <td>
|
|
||||||
+ <span
|
|
||||||
+ style="cursor: pointer; padding-left: 6rem;"
|
|
||||||
+ >
|
|
||||||
+ 👉
|
|
||||||
+ </span>
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ joe
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ bergevin
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 45
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 20
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ Complicated
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 10
|
|
||||||
+ </td>
|
|
||||||
+ </tr>
|
|
||||||
+ <tr>
|
|
||||||
+ <td>
|
|
||||||
+ <span
|
|
||||||
+ style="cursor: pointer; padding-left: 6rem;"
|
|
||||||
+ >
|
|
||||||
+ 👉
|
|
||||||
+ </span>
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ jaylen
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ linsley
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 26
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 99
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ In Relationship
|
|
||||||
+ </td>
|
|
||||||
+ <td>
|
|
||||||
+ 70
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<span
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`renders an expandable table 4`] = `
|
|
||||||
Snapshot Diff:
|
|
||||||
- First value
|
|
||||||
+ Second value
|
|
||||||
|
|
||||||
@@ -3,11 +3,12 @@
|
|
||||||
<code>
|
|
||||||
{
|
|
||||||
"expanded": {
|
|
||||||
"0": true,
|
|
||||||
"0.0": true,
|
|
||||||
- "0.0.0": true
|
|
||||||
+ "0.0.0": true,
|
|
||||||
+ "0.0.0.0": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</code>
|
|
||||||
</pre>
|
|
||||||
<table>
|
|
||||||
@@ -150,11 +151,11 @@
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<span
|
|
||||||
style="cursor: pointer; padding-left: 6rem;"
|
|
||||||
>
|
|
||||||
- 👉
|
|
||||||
+ 👇
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
tanner
|
|
||||||
</td>
|
|
||||||
@@ -170,10 +171,30 @@
|
|
||||||
<td>
|
|
||||||
In Relationship
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
50
|
|
||||||
+ </td>
|
|
||||||
+ </tr>
|
|
||||||
+ <tr>
|
|
||||||
+ <td
|
|
||||||
+ colspan="7"
|
|
||||||
+ >
|
|
||||||
+ <pre>
|
|
||||||
+ <code>
|
|
||||||
+ {
|
|
||||||
+ "values": {
|
|
||||||
+ "firstName": "tanner",
|
|
||||||
+ "lastName": "linsley",
|
|
||||||
+ "age": 29,
|
|
||||||
+ "visits": 100,
|
|
||||||
+ "status": "In Relationship",
|
|
||||||
+ "progress": 50
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ </code>
|
|
||||||
+ </pre>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<span
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`renders an expandable table 5`] = `
|
|
||||||
Snapshot Diff:
|
|
||||||
- First value
|
|
||||||
+ Second value
|
|
||||||
|
|
||||||
@@ -1,15 +1,10 @@
|
|
||||||
<DocumentFragment>
|
|
||||||
<pre>
|
|
||||||
<code>
|
|
||||||
{
|
|
||||||
- "expanded": {
|
|
||||||
- "0": true,
|
|
||||||
- "0.0": true,
|
|
||||||
- "0.0.0": true,
|
|
||||||
- "0.0.0.0": true
|
|
||||||
- }
|
|
||||||
+ "expanded": {}
|
|
||||||
}
|
|
||||||
</code>
|
|
||||||
</pre>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
@@ -70,92 +65,11 @@
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<span
|
|
||||||
style="cursor: pointer; padding-left: 0rem;"
|
|
||||||
>
|
|
||||||
- 👇
|
|
||||||
- </span>
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- tanner
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- linsley
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 29
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 100
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- In Relationship
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 50
|
|
||||||
- </td>
|
|
||||||
- </tr>
|
|
||||||
- <tr>
|
|
||||||
- <td>
|
|
||||||
- <span
|
|
||||||
- style="cursor: pointer; padding-left: 2rem;"
|
|
||||||
- >
|
|
||||||
- 👇
|
|
||||||
- </span>
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- tanner
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- linsley
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 29
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 100
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- In Relationship
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 50
|
|
||||||
- </td>
|
|
||||||
- </tr>
|
|
||||||
- <tr>
|
|
||||||
- <td>
|
|
||||||
- <span
|
|
||||||
- style="cursor: pointer; padding-left: 4rem;"
|
|
||||||
- >
|
|
||||||
- 👇
|
|
||||||
- </span>
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- tanner
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- linsley
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 29
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 100
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- In Relationship
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 50
|
|
||||||
- </td>
|
|
||||||
- </tr>
|
|
||||||
- <tr>
|
|
||||||
- <td>
|
|
||||||
- <span
|
|
||||||
- style="cursor: pointer; padding-left: 6rem;"
|
|
||||||
- >
|
|
||||||
- 👇
|
|
||||||
+ 👉
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
tanner
|
|
||||||
</td>
|
|
||||||
@@ -171,273 +85,10 @@
|
|
||||||
<td>
|
|
||||||
In Relationship
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
50
|
|
||||||
- </td>
|
|
||||||
- </tr>
|
|
||||||
- <tr>
|
|
||||||
- <td
|
|
||||||
- colspan="7"
|
|
||||||
- >
|
|
||||||
- <pre>
|
|
||||||
- <code>
|
|
||||||
- {
|
|
||||||
- "values": {
|
|
||||||
- "firstName": "tanner",
|
|
||||||
- "lastName": "linsley",
|
|
||||||
- "age": 29,
|
|
||||||
- "visits": 100,
|
|
||||||
- "status": "In Relationship",
|
|
||||||
- "progress": 50
|
|
||||||
- }
|
|
||||||
- }
|
|
||||||
- </code>
|
|
||||||
- </pre>
|
|
||||||
- </td>
|
|
||||||
- </tr>
|
|
||||||
- <tr>
|
|
||||||
- <td>
|
|
||||||
- <span
|
|
||||||
- style="cursor: pointer; padding-left: 6rem;"
|
|
||||||
- >
|
|
||||||
- 👉
|
|
||||||
- </span>
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- derek
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- perkins
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 40
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 40
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- Single
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 80
|
|
||||||
- </td>
|
|
||||||
- </tr>
|
|
||||||
- <tr>
|
|
||||||
- <td>
|
|
||||||
- <span
|
|
||||||
- style="cursor: pointer; padding-left: 6rem;"
|
|
||||||
- >
|
|
||||||
- 👉
|
|
||||||
- </span>
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- joe
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- bergevin
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 45
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 20
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- Complicated
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 10
|
|
||||||
- </td>
|
|
||||||
- </tr>
|
|
||||||
- <tr>
|
|
||||||
- <td>
|
|
||||||
- <span
|
|
||||||
- style="cursor: pointer; padding-left: 6rem;"
|
|
||||||
- >
|
|
||||||
- 👉
|
|
||||||
- </span>
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- jaylen
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- linsley
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 26
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 99
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- In Relationship
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 70
|
|
||||||
- </td>
|
|
||||||
- </tr>
|
|
||||||
- <tr>
|
|
||||||
- <td>
|
|
||||||
- <span
|
|
||||||
- style="cursor: pointer; padding-left: 4rem;"
|
|
||||||
- >
|
|
||||||
- 👉
|
|
||||||
- </span>
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- derek
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- perkins
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 40
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 40
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- Single
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 80
|
|
||||||
- </td>
|
|
||||||
- </tr>
|
|
||||||
- <tr>
|
|
||||||
- <td>
|
|
||||||
- <span
|
|
||||||
- style="cursor: pointer; padding-left: 4rem;"
|
|
||||||
- >
|
|
||||||
- 👉
|
|
||||||
- </span>
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- joe
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- bergevin
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 45
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 20
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- Complicated
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 10
|
|
||||||
- </td>
|
|
||||||
- </tr>
|
|
||||||
- <tr>
|
|
||||||
- <td>
|
|
||||||
- <span
|
|
||||||
- style="cursor: pointer; padding-left: 4rem;"
|
|
||||||
- >
|
|
||||||
- 👉
|
|
||||||
- </span>
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- jaylen
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- linsley
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 26
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 99
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- In Relationship
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 70
|
|
||||||
- </td>
|
|
||||||
- </tr>
|
|
||||||
- <tr>
|
|
||||||
- <td>
|
|
||||||
- <span
|
|
||||||
- style="cursor: pointer; padding-left: 2rem;"
|
|
||||||
- >
|
|
||||||
- 👉
|
|
||||||
- </span>
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- derek
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- perkins
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 40
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 40
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- Single
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 80
|
|
||||||
- </td>
|
|
||||||
- </tr>
|
|
||||||
- <tr>
|
|
||||||
- <td>
|
|
||||||
- <span
|
|
||||||
- style="cursor: pointer; padding-left: 2rem;"
|
|
||||||
- >
|
|
||||||
- 👉
|
|
||||||
- </span>
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- joe
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- bergevin
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 45
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 20
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- Complicated
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 10
|
|
||||||
- </td>
|
|
||||||
- </tr>
|
|
||||||
- <tr>
|
|
||||||
- <td>
|
|
||||||
- <span
|
|
||||||
- style="cursor: pointer; padding-left: 2rem;"
|
|
||||||
- >
|
|
||||||
- 👉
|
|
||||||
- </span>
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- jaylen
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- linsley
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 26
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 99
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- In Relationship
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 70
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<span
|
|
||||||
`;
|
|
||||||
@ -1,766 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`renders a filterable table 1`] = `
|
|
||||||
<DocumentFragment>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
colspan="2"
|
|
||||||
>
|
|
||||||
Name
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="4"
|
|
||||||
>
|
|
||||||
Info
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
First Name
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Last Name
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Age
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Visits
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Status
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Profile Progress
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
colspan="6"
|
|
||||||
style="text-align: left;"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
<input
|
|
||||||
placeholder="Global search..."
|
|
||||||
style="font-size: 1.1rem; border: 0px;"
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
firstName: tanner
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
lastName: linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
age: 29
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
visits: 100
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
status: In Relationship
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
progress: 50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
firstName: derek
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
lastName: perkins
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
age: 40
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
visits: 40
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
status: Single
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
progress: 80
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
firstName: joe
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
lastName: bergevin
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
age: 45
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
visits: 20
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
status: Complicated
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
progress: 10
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
firstName: jaylen
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
lastName: linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
age: 26
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
visits: 99
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
status: In Relationship
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
progress: 70
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</DocumentFragment>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`renders a filterable table 2`] = `
|
|
||||||
<DocumentFragment>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
colspan="2"
|
|
||||||
>
|
|
||||||
Name
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="4"
|
|
||||||
>
|
|
||||||
Info
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
First Name
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Last Name
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value="l"
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Age
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Visits
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Status
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Profile Progress
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
colspan="6"
|
|
||||||
style="text-align: left;"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
<input
|
|
||||||
placeholder="Global search..."
|
|
||||||
style="font-size: 1.1rem; border: 0px;"
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
firstName: tanner
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
lastName: linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
age: 29
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
visits: 100
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
status: In Relationship
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
progress: 50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
firstName: jaylen
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
lastName: linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
age: 26
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
visits: 99
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
status: In Relationship
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
progress: 70
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</DocumentFragment>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`renders a filterable table 3`] = `
|
|
||||||
<DocumentFragment>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
colspan="2"
|
|
||||||
>
|
|
||||||
Name
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="4"
|
|
||||||
>
|
|
||||||
Info
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
First Name
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Last Name
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value="er"
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Age
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Visits
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Status
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Profile Progress
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
colspan="6"
|
|
||||||
style="text-align: left;"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
<input
|
|
||||||
placeholder="Global search..."
|
|
||||||
style="font-size: 1.1rem; border: 0px;"
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
firstName: derek
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
lastName: perkins
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
age: 40
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
visits: 40
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
status: Single
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
progress: 80
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
firstName: joe
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
lastName: bergevin
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
age: 45
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
visits: 20
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
status: Complicated
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
progress: 10
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</DocumentFragment>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`renders a filterable table 4`] = `
|
|
||||||
<DocumentFragment>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
colspan="2"
|
|
||||||
>
|
|
||||||
Name
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="4"
|
|
||||||
>
|
|
||||||
Info
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
First Name
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Last Name
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Age
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Visits
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Status
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Profile Progress
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
colspan="6"
|
|
||||||
style="text-align: left;"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
<input
|
|
||||||
placeholder="Global search..."
|
|
||||||
style="font-size: 1.1rem; border: 0px;"
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
firstName: tanner
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
lastName: linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
age: 29
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
visits: 100
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
status: In Relationship
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
progress: 50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
firstName: derek
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
lastName: perkins
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
age: 40
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
visits: 40
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
status: Single
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
progress: 80
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
firstName: joe
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
lastName: bergevin
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
age: 45
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
visits: 20
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
status: Complicated
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
progress: 10
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
firstName: jaylen
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
lastName: linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
age: 26
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
visits: 99
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
status: In Relationship
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
progress: 70
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</DocumentFragment>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`renders a filterable table 5`] = `
|
|
||||||
<DocumentFragment>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
colspan="2"
|
|
||||||
>
|
|
||||||
Name
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="4"
|
|
||||||
>
|
|
||||||
Info
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
First Name
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Last Name
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Age
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Visits
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Status
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
Profile Progress
|
|
||||||
<input
|
|
||||||
placeholder="Search..."
|
|
||||||
value=""
|
|
||||||
/>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
colspan="6"
|
|
||||||
style="text-align: left;"
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
<input
|
|
||||||
placeholder="Global search..."
|
|
||||||
style="font-size: 1.1rem; border: 0px;"
|
|
||||||
value="li"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
firstName: tanner
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
lastName: linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
age: 29
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
visits: 100
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
status: In Relationship
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
progress: 50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
firstName: joe
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
lastName: bergevin
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
age: 45
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
visits: 20
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
status: Complicated
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
progress: 10
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
firstName: jaylen
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
lastName: linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
age: 26
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
visits: 99
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
status: In Relationship
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
progress: 70
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</DocumentFragment>
|
|
||||||
`;
|
|
||||||
@ -1,294 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`renders a groupable table 1`] = `
|
|
||||||
Snapshot Diff:
|
|
||||||
- First value
|
|
||||||
+ Second value
|
|
||||||
|
|
||||||
@@ -19,24 +19,24 @@
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
style="cursor: pointer;"
|
|
||||||
title="Toggle GroupBy"
|
|
||||||
>
|
|
||||||
- 👊
|
|
||||||
+ 🛑
|
|
||||||
</span>
|
|
||||||
- First Name
|
|
||||||
+ Last Name
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
style="cursor: pointer;"
|
|
||||||
title="Toggle GroupBy"
|
|
||||||
>
|
|
||||||
👊
|
|
||||||
</span>
|
|
||||||
- Last Name
|
|
||||||
+ First Name
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
@@ -83,86 +83,81 @@
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- firstName: tanner
|
|
||||||
+ <span
|
|
||||||
+ style="cursor: pointer;"
|
|
||||||
+ >
|
|
||||||
+ 👉
|
|
||||||
+ </span>
|
|
||||||
+ lastName: linsley (2)
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- lastName: linsley
|
|
||||||
+ 2 Names
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- age: 29
|
|
||||||
+ 27.5 (avg)
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- visits: 100
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- status: In Relationship
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- progress: 50
|
|
||||||
- </td>
|
|
||||||
- </tr>
|
|
||||||
- <tr>
|
|
||||||
- <td>
|
|
||||||
- firstName: derek
|
|
||||||
+ 199 (total)
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- lastName: perkins
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- age: 40
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- visits: 40
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- status: Single
|
|
||||||
+ status: null
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- progress: 80
|
|
||||||
+ 60 (med)
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- firstName: joe
|
|
||||||
+ <span
|
|
||||||
+ style="cursor: pointer;"
|
|
||||||
+ >
|
|
||||||
+ 👉
|
|
||||||
+ </span>
|
|
||||||
+ lastName: perkins (1)
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- lastName: bergevin
|
|
||||||
+ 1 Names
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- age: 45
|
|
||||||
+ 40 (avg)
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- visits: 20
|
|
||||||
+ 40 (total)
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- status: Complicated
|
|
||||||
+ status: null
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- progress: 10
|
|
||||||
+ 80 (med)
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- firstName: jaylen
|
|
||||||
+ <span
|
|
||||||
+ style="cursor: pointer;"
|
|
||||||
+ >
|
|
||||||
+ 👉
|
|
||||||
+ </span>
|
|
||||||
+ lastName: bergevin (1)
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- lastName: linsley
|
|
||||||
+ 1 Names
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- age: 26
|
|
||||||
+ 45 (avg)
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- visits: 99
|
|
||||||
+ 20 (total)
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- status: In Relationship
|
|
||||||
+ status: null
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- progress: 70
|
|
||||||
+ 10 (med)
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</DocumentFragment>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`renders a groupable table 2`] = `
|
|
||||||
Snapshot Diff:
|
|
||||||
- First value
|
|
||||||
+ Second value
|
|
||||||
|
|
||||||
@@ -1,16 +1,26 @@
|
|
||||||
<DocumentFragment>
|
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
- colspan="2"
|
|
||||||
+ colspan="1"
|
|
||||||
+ >
|
|
||||||
+ Name
|
|
||||||
+ </th>
|
|
||||||
+ <th
|
|
||||||
+ colspan="1"
|
|
||||||
+ >
|
|
||||||
+ Info
|
|
||||||
+ </th>
|
|
||||||
+ <th
|
|
||||||
+ colspan="1"
|
|
||||||
>
|
|
||||||
Name
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
- colspan="4"
|
|
||||||
+ colspan="3"
|
|
||||||
>
|
|
||||||
Info
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
@@ -30,35 +40,35 @@
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
style="cursor: pointer;"
|
|
||||||
title="Toggle GroupBy"
|
|
||||||
>
|
|
||||||
- 👊
|
|
||||||
+ 🛑
|
|
||||||
</span>
|
|
||||||
- First Name
|
|
||||||
+ Visits
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
style="cursor: pointer;"
|
|
||||||
title="Toggle GroupBy"
|
|
||||||
>
|
|
||||||
👊
|
|
||||||
</span>
|
|
||||||
- Age
|
|
||||||
+ First Name
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
style="cursor: pointer;"
|
|
||||||
title="Toggle GroupBy"
|
|
||||||
>
|
|
||||||
👊
|
|
||||||
</span>
|
|
||||||
- Visits
|
|
||||||
+ Age
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
@@ -90,18 +100,16 @@
|
|
||||||
>
|
|
||||||
👉
|
|
||||||
</span>
|
|
||||||
lastName: linsley (2)
|
|
||||||
</td>
|
|
||||||
+ <td />
|
|
||||||
<td>
|
|
||||||
2 Names
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
27.5 (avg)
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 199 (total)
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
status: null
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -115,20 +123,18 @@
|
|
||||||
>
|
|
||||||
👉
|
|
||||||
</span>
|
|
||||||
lastName: perkins (1)
|
|
||||||
</td>
|
|
||||||
+ <td />
|
|
||||||
<td>
|
|
||||||
1 Names
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
40 (avg)
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- 40 (total)
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
status: null
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
80 (med)
|
|
||||||
</td>
|
|
||||||
@@ -140,18 +146,16 @@
|
|
||||||
>
|
|
||||||
👉
|
|
||||||
</span>
|
|
||||||
lastName: bergevin (1)
|
|
||||||
</td>
|
|
||||||
+ <td />
|
|
||||||
<td>
|
|
||||||
1 Names
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
45 (avg)
|
|
||||||
- </td>
|
|
||||||
- <td>
|
|
||||||
- 20 (total)
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
status: null
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
`;
|
|
||||||
@ -1,498 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`renders a paginated table 1`] = `
|
|
||||||
Snapshot Diff:
|
|
||||||
- First value
|
|
||||||
+ Second value
|
|
||||||
|
|
||||||
@@ -47,11 +47,11 @@
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 21
|
|
||||||
+ tanner 31
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -67,11 +67,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 22
|
|
||||||
+ tanner 32
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -87,11 +87,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 23
|
|
||||||
+ tanner 33
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -107,11 +107,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 24
|
|
||||||
+ tanner 34
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -127,11 +127,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 25
|
|
||||||
+ tanner 35
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -147,11 +147,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 26
|
|
||||||
+ tanner 36
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -167,11 +167,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 27
|
|
||||||
+ tanner 37
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -187,11 +187,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 28
|
|
||||||
+ tanner 38
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -207,11 +207,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 29
|
|
||||||
+ tanner 39
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -227,11 +227,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 30
|
|
||||||
+ tanner 40
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -269,20 +269,20 @@
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<span>
|
|
||||||
Page
|
|
||||||
<strong>
|
|
||||||
- 3 of 10
|
|
||||||
+ 4 of 10
|
|
||||||
</strong>
|
|
||||||
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
| Go to page:
|
|
||||||
<input
|
|
||||||
style="width: 100px;"
|
|
||||||
type="number"
|
|
||||||
- value="3"
|
|
||||||
+ value="4"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<select>
|
|
||||||
<option
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`renders a paginated table 2`] = `
|
|
||||||
Snapshot Diff:
|
|
||||||
- First value
|
|
||||||
+ Second value
|
|
||||||
|
|
||||||
@@ -47,11 +47,11 @@
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 31
|
|
||||||
+ tanner 41
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -67,11 +67,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 32
|
|
||||||
+ tanner 42
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -87,11 +87,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 33
|
|
||||||
+ tanner 43
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -107,11 +107,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 34
|
|
||||||
+ tanner 44
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -127,11 +127,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 35
|
|
||||||
+ tanner 45
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -147,11 +147,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 36
|
|
||||||
+ tanner 46
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -167,11 +167,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 37
|
|
||||||
+ tanner 47
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -187,11 +187,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 38
|
|
||||||
+ tanner 48
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -207,11 +207,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 39
|
|
||||||
+ tanner 49
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -227,11 +227,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 40
|
|
||||||
+ tanner 50
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -269,20 +269,20 @@
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<span>
|
|
||||||
Page
|
|
||||||
<strong>
|
|
||||||
- 4 of 10
|
|
||||||
+ 5 of 10
|
|
||||||
</strong>
|
|
||||||
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
| Go to page:
|
|
||||||
<input
|
|
||||||
style="width: 100px;"
|
|
||||||
type="number"
|
|
||||||
- value="4"
|
|
||||||
+ value="5"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<select>
|
|
||||||
<option
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`renders a paginated table 3`] = `
|
|
||||||
Snapshot Diff:
|
|
||||||
- First value
|
|
||||||
+ Second value
|
|
||||||
|
|
||||||
@@ -47,11 +47,11 @@
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 41
|
|
||||||
+ tanner 91
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -67,11 +67,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 42
|
|
||||||
+ tanner 92
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -87,11 +87,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 43
|
|
||||||
+ tanner 93
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -107,11 +107,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 44
|
|
||||||
+ tanner 94
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -127,11 +127,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 45
|
|
||||||
+ tanner 95
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -147,11 +147,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 46
|
|
||||||
+ tanner 96
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -167,11 +167,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 47
|
|
||||||
+ tanner 97
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -187,11 +187,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 48
|
|
||||||
+ tanner 98
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -207,11 +207,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 49
|
|
||||||
+ tanner 99
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -227,11 +227,11 @@
|
|
||||||
50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- tanner 50
|
|
||||||
+ tanner 100
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
@@ -258,31 +258,35 @@
|
|
||||||
|
|
||||||
<button>
|
|
||||||
<
|
|
||||||
</button>
|
|
||||||
|
|
||||||
- <button>
|
|
||||||
+ <button
|
|
||||||
+ disabled=""
|
|
||||||
+ >
|
|
||||||
>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
- <button>
|
|
||||||
+ <button
|
|
||||||
+ disabled=""
|
|
||||||
+ >
|
|
||||||
>>
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<span>
|
|
||||||
Page
|
|
||||||
<strong>
|
|
||||||
- 5 of 10
|
|
||||||
+ 10 of 10
|
|
||||||
</strong>
|
|
||||||
|
|
||||||
</span>
|
|
||||||
<span>
|
|
||||||
| Go to page:
|
|
||||||
<input
|
|
||||||
style="width: 100px;"
|
|
||||||
type="number"
|
|
||||||
- value="5"
|
|
||||||
+ value="10"
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<select>
|
|
||||||
<option
|
|
||||||
`;
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,194 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`renders a sortable table 1`] = `
|
|
||||||
Snapshot Diff:
|
|
||||||
- First value
|
|
||||||
+ Second value
|
|
||||||
|
|
||||||
@@ -20,11 +20,13 @@
|
|
||||||
colspan="1"
|
|
||||||
style="cursor: pointer;"
|
|
||||||
title="Toggle SortBy"
|
|
||||||
>
|
|
||||||
First Name
|
|
||||||
- <span />
|
|
||||||
+ <span>
|
|
||||||
+ 🔼
|
|
||||||
+ </span>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
style="cursor: pointer;"
|
|
||||||
title="Toggle SortBy"
|
|
||||||
@@ -67,66 +69,66 @@
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- firstName: tanner
|
|
||||||
+ firstName: derek
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- lastName: linsley
|
|
||||||
+ lastName: perkins
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- age: 29
|
|
||||||
+ age: 40
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- visits: 100
|
|
||||||
+ visits: 40
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- status: In Relationship
|
|
||||||
+ status: Single
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- progress: 50
|
|
||||||
+ progress: 80
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- firstName: derek
|
|
||||||
+ firstName: joe
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- lastName: perkins
|
|
||||||
+ lastName: bergevin
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- age: 40
|
|
||||||
+ age: 45
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- visits: 40
|
|
||||||
+ visits: 20
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- status: Single
|
|
||||||
+ status: Complicated
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- progress: 80
|
|
||||||
+ progress: 10
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- firstName: joe
|
|
||||||
+ firstName: tanner
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- lastName: bergevin
|
|
||||||
+ lastName: linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- age: 45
|
|
||||||
+ age: 29
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- visits: 20
|
|
||||||
+ visits: 100
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- status: Complicated
|
|
||||||
+ status: In Relationship
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- progress: 10
|
|
||||||
+ progress: 50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</DocumentFragment>
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`renders a sortable table 2`] = `
|
|
||||||
Snapshot Diff:
|
|
||||||
- First value
|
|
||||||
+ Second value
|
|
||||||
|
|
||||||
@@ -21,11 +21,11 @@
|
|
||||||
style="cursor: pointer;"
|
|
||||||
title="Toggle SortBy"
|
|
||||||
>
|
|
||||||
First Name
|
|
||||||
<span>
|
|
||||||
- 🔼
|
|
||||||
+ 🔽
|
|
||||||
</span>
|
|
||||||
</th>
|
|
||||||
<th
|
|
||||||
colspan="1"
|
|
||||||
style="cursor: pointer;"
|
|
||||||
@@ -69,26 +69,26 @@
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- firstName: derek
|
|
||||||
+ firstName: tanner
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- lastName: perkins
|
|
||||||
+ lastName: linsley
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- age: 40
|
|
||||||
+ age: 29
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- visits: 40
|
|
||||||
+ visits: 100
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- status: Single
|
|
||||||
+ status: In Relationship
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- progress: 80
|
|
||||||
+ progress: 50
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
firstName: joe
|
|
||||||
@@ -109,26 +109,26 @@
|
|
||||||
progress: 10
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
- firstName: tanner
|
|
||||||
+ firstName: derek
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- lastName: linsley
|
|
||||||
+ lastName: perkins
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- age: 29
|
|
||||||
+ age: 40
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- visits: 100
|
|
||||||
+ visits: 40
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- status: In Relationship
|
|
||||||
+ status: Single
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
- progress: 50
|
|
||||||
+ progress: 80
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</DocumentFragment>
|
|
||||||
`;
|
|
||||||
@ -136,7 +136,22 @@ function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test('renders a table', () => {
|
test('renders a table', () => {
|
||||||
const { asFragment } = render(<App />)
|
const rtl = render(<App />)
|
||||||
|
|
||||||
expect(asFragment()).toMatchSnapshot()
|
expect(
|
||||||
|
rtl.getAllByRole('columnheader').every(d => d.style.position === 'absolute')
|
||||||
|
).toBe(true)
|
||||||
|
|
||||||
|
expect(
|
||||||
|
rtl.getAllByRole('columnheader').map(d => [d.style.left, d.style.width])
|
||||||
|
).toStrictEqual([
|
||||||
|
['0px', '550px'],
|
||||||
|
['550px', '850px'],
|
||||||
|
['0px', '250px'],
|
||||||
|
['250px', '300px'],
|
||||||
|
['550px', '300px'],
|
||||||
|
['850px', '150px'],
|
||||||
|
['1000px', '200px'],
|
||||||
|
['1200px', '200px'],
|
||||||
|
])
|
||||||
})
|
})
|
||||||
|
|||||||
@ -136,7 +136,28 @@ function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test('renders a table', () => {
|
test('renders a table', () => {
|
||||||
const { asFragment } = render(<App />)
|
const rtl = render(<App />)
|
||||||
|
|
||||||
expect(asFragment()).toMatchSnapshot()
|
expect(
|
||||||
|
rtl
|
||||||
|
.getAllByRole('columnheader')
|
||||||
|
.every(d => d.style.display === 'inline-block')
|
||||||
|
).toBe(true)
|
||||||
|
|
||||||
|
expect(rtl.getAllByRole('row').every(d => d.style.display === 'flex')).toBe(
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(
|
||||||
|
rtl.getAllByRole('columnheader').map(d => d.style.width)
|
||||||
|
).toStrictEqual([
|
||||||
|
'550px',
|
||||||
|
'850px',
|
||||||
|
'250px',
|
||||||
|
'300px',
|
||||||
|
'300px',
|
||||||
|
'150px',
|
||||||
|
'200px',
|
||||||
|
'200px',
|
||||||
|
])
|
||||||
})
|
})
|
||||||
|
|||||||
186
src/plugin-hooks/tests/useColumnOrder.test.js
Normal file
186
src/plugin-hooks/tests/useColumnOrder.test.js
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { render, fireEvent } from '@testing-library/react'
|
||||||
|
import { useTable } from '../../hooks/useTable'
|
||||||
|
import { useColumnOrder } from '../useColumnOrder'
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
firstName: 'tanner',
|
||||||
|
lastName: 'linsley',
|
||||||
|
age: 29,
|
||||||
|
visits: 100,
|
||||||
|
status: 'In Relationship',
|
||||||
|
progress: 50,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
firstName: 'derek',
|
||||||
|
lastName: 'perkins',
|
||||||
|
age: 40,
|
||||||
|
visits: 40,
|
||||||
|
status: 'Single',
|
||||||
|
progress: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
firstName: 'joe',
|
||||||
|
lastName: 'bergevin',
|
||||||
|
age: 45,
|
||||||
|
visits: 20,
|
||||||
|
status: 'Complicated',
|
||||||
|
progress: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
firstName: 'jaylen',
|
||||||
|
lastName: 'linsley',
|
||||||
|
age: 26,
|
||||||
|
visits: 99,
|
||||||
|
status: 'In Relationship',
|
||||||
|
progress: 70,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
function shuffle(arr, mapping) {
|
||||||
|
if (arr.length !== mapping.length) {
|
||||||
|
throw new Error()
|
||||||
|
}
|
||||||
|
arr = [...arr]
|
||||||
|
mapping = [...mapping]
|
||||||
|
const shuffled = []
|
||||||
|
while (arr.length) {
|
||||||
|
shuffled.push(arr.splice([mapping.shift()], 1)[0])
|
||||||
|
}
|
||||||
|
return shuffled
|
||||||
|
}
|
||||||
|
|
||||||
|
function Table({ columns, data }) {
|
||||||
|
const {
|
||||||
|
getTableProps,
|
||||||
|
getTableBodyProps,
|
||||||
|
headerGroups,
|
||||||
|
rows,
|
||||||
|
visibleColumns,
|
||||||
|
prepareRow,
|
||||||
|
setColumnOrder,
|
||||||
|
state,
|
||||||
|
} = useTable(
|
||||||
|
{
|
||||||
|
columns,
|
||||||
|
data,
|
||||||
|
},
|
||||||
|
useColumnOrder
|
||||||
|
)
|
||||||
|
|
||||||
|
const testColumnOrder = () => {
|
||||||
|
setColumnOrder(
|
||||||
|
shuffle(
|
||||||
|
visibleColumns.map(d => d.id),
|
||||||
|
[1, 4, 2, 0, 3, 5]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<button onClick={() => testColumnOrder({})}>Randomize Columns</button>
|
||||||
|
<table {...getTableProps()}>
|
||||||
|
<thead>
|
||||||
|
{headerGroups.map((headerGroup, i) => (
|
||||||
|
<tr {...headerGroup.getHeaderGroupProps()}>
|
||||||
|
{headerGroup.headers.map(column => (
|
||||||
|
<th {...column.getHeaderProps()}>{column.render('Header')}</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</thead>
|
||||||
|
<tbody {...getTableBodyProps()}>
|
||||||
|
{rows.slice(0, 10).map((row, i) => {
|
||||||
|
prepareRow(row)
|
||||||
|
return (
|
||||||
|
<tr {...row.getRowProps()}>
|
||||||
|
{row.cells.map((cell, i) => {
|
||||||
|
return <td {...cell.getCellProps()}>{cell.render('Cell')}</td>
|
||||||
|
})}
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<pre>
|
||||||
|
<code>{JSON.stringify(state, null, 2)}</code>
|
||||||
|
</pre>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const columns = React.useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
Header: 'Name',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
Header: 'First Name',
|
||||||
|
accessor: 'firstName',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Last Name',
|
||||||
|
accessor: 'lastName',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Info',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
Header: 'Age',
|
||||||
|
accessor: 'age',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Visits',
|
||||||
|
accessor: 'visits',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Status',
|
||||||
|
accessor: 'status',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Profile Progress',
|
||||||
|
accessor: 'progress',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
|
return <Table columns={columns} data={data} />
|
||||||
|
}
|
||||||
|
|
||||||
|
test('renders a column-orderable table', () => {
|
||||||
|
const rtl = render(<App />)
|
||||||
|
|
||||||
|
expect(rtl.getAllByRole('columnheader').map(d => d.textContent)).toEqual([
|
||||||
|
'Name',
|
||||||
|
'Info',
|
||||||
|
'First Name',
|
||||||
|
'Last Name',
|
||||||
|
'Age',
|
||||||
|
'Visits',
|
||||||
|
'Status',
|
||||||
|
'Profile Progress',
|
||||||
|
])
|
||||||
|
|
||||||
|
fireEvent.click(rtl.getByText('Randomize Columns'))
|
||||||
|
|
||||||
|
expect(rtl.getAllByRole('columnheader').map(d => d.textContent)).toEqual([
|
||||||
|
'Name',
|
||||||
|
'Info',
|
||||||
|
'Name',
|
||||||
|
'Info',
|
||||||
|
'Last Name',
|
||||||
|
'Profile Progress',
|
||||||
|
'Visits',
|
||||||
|
'First Name',
|
||||||
|
'Age',
|
||||||
|
'Status',
|
||||||
|
])
|
||||||
|
})
|
||||||
@ -2,47 +2,9 @@ import React from 'react'
|
|||||||
import { render, fireEvent } from '@testing-library/react'
|
import { render, fireEvent } from '@testing-library/react'
|
||||||
import { useTable } from '../../hooks/useTable'
|
import { useTable } from '../../hooks/useTable'
|
||||||
import { useExpanded } from '../useExpanded'
|
import { useExpanded } from '../useExpanded'
|
||||||
|
import makeTestData from '../../../test-utils/makeTestData'
|
||||||
|
|
||||||
const makeData = () => [
|
const data = makeTestData(3, 3, 3)
|
||||||
{
|
|
||||||
firstName: 'tanner',
|
|
||||||
lastName: 'linsley',
|
|
||||||
age: 29,
|
|
||||||
visits: 100,
|
|
||||||
status: 'In Relationship',
|
|
||||||
progress: 50,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
firstName: 'derek',
|
|
||||||
lastName: 'perkins',
|
|
||||||
age: 40,
|
|
||||||
visits: 40,
|
|
||||||
status: 'Single',
|
|
||||||
progress: 80,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
firstName: 'joe',
|
|
||||||
lastName: 'bergevin',
|
|
||||||
age: 45,
|
|
||||||
visits: 20,
|
|
||||||
status: 'Complicated',
|
|
||||||
progress: 10,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
firstName: 'jaylen',
|
|
||||||
lastName: 'linsley',
|
|
||||||
age: 26,
|
|
||||||
visits: 99,
|
|
||||||
status: 'In Relationship',
|
|
||||||
progress: 70,
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const data = makeData()
|
|
||||||
|
|
||||||
data[0].subRows = makeData()
|
|
||||||
data[0].subRows[0].subRows = makeData()
|
|
||||||
data[0].subRows[0].subRows[0].subRows = makeData()
|
|
||||||
|
|
||||||
function Table({ columns: userColumns, data, SubComponent }) {
|
function Table({ columns: userColumns, data, SubComponent }) {
|
||||||
const {
|
const {
|
||||||
@ -51,8 +13,7 @@ function Table({ columns: userColumns, data, SubComponent }) {
|
|||||||
headerGroups,
|
headerGroups,
|
||||||
rows,
|
rows,
|
||||||
prepareRow,
|
prepareRow,
|
||||||
flatColumns,
|
visibleColumns,
|
||||||
state: { expanded },
|
|
||||||
} = useTable(
|
} = useTable(
|
||||||
{
|
{
|
||||||
columns: userColumns,
|
columns: userColumns,
|
||||||
@ -63,9 +24,6 @@ function Table({ columns: userColumns, data, SubComponent }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<pre>
|
|
||||||
<code>{JSON.stringify({ expanded: expanded }, null, 2)}</code>
|
|
||||||
</pre>
|
|
||||||
<table {...getTableProps()}>
|
<table {...getTableProps()}>
|
||||||
<thead>
|
<thead>
|
||||||
{headerGroups.map(headerGroup => (
|
{headerGroups.map(headerGroup => (
|
||||||
@ -91,7 +49,7 @@ function Table({ columns: userColumns, data, SubComponent }) {
|
|||||||
</tr>
|
</tr>
|
||||||
{!row.subRows.length && row.isExpanded ? (
|
{!row.subRows.length && row.isExpanded ? (
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan={flatColumns.length}>
|
<td colSpan={visibleColumns.length}>
|
||||||
{SubComponent({ row })}
|
{SubComponent({ row })}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -119,43 +77,14 @@ function App() {
|
|||||||
}}
|
}}
|
||||||
onClick={() => row.toggleExpanded()}
|
onClick={() => row.toggleExpanded()}
|
||||||
>
|
>
|
||||||
{row.isExpanded ? '👇' : '👉'}
|
{row.isExpanded ? 'Collapse' : 'Expand'} Row {row.id}
|
||||||
</span>
|
</span>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Name',
|
Header: 'First Name',
|
||||||
columns: [
|
accessor: 'name',
|
||||||
{
|
Cell: ({ row: { id } }) => `Row ${id}`,
|
||||||
Header: 'First Name',
|
|
||||||
accessor: 'firstName',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Header: 'Last Name',
|
|
||||||
accessor: 'lastName',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Header: 'Info',
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
Header: 'Age',
|
|
||||||
accessor: 'age',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Header: 'Visits',
|
|
||||||
accessor: 'visits',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Header: 'Status',
|
|
||||||
accessor: 'status',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Header: 'Profile Progress',
|
|
||||||
accessor: 'progress',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[]
|
[]
|
||||||
@ -165,51 +94,32 @@ function App() {
|
|||||||
<Table
|
<Table
|
||||||
columns={columns}
|
columns={columns}
|
||||||
data={data}
|
data={data}
|
||||||
SubComponent={({ row }) => (
|
SubComponent={({ row }) => <span>SubComponent: {row.id}</span>}
|
||||||
<pre>
|
|
||||||
<code>{JSON.stringify({ values: row.values }, null, 2)}</code>
|
|
||||||
</pre>
|
|
||||||
)}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
test('renders an expandable table', () => {
|
test('renders an expandable table', () => {
|
||||||
const { getAllByText, asFragment } = render(<App />)
|
const rtl = render(<App />)
|
||||||
|
|
||||||
let expandButtons = getAllByText('👉')
|
rtl.getByText('Row 0')
|
||||||
|
|
||||||
const before = asFragment()
|
fireEvent.click(rtl.getByText('Expand Row 0'))
|
||||||
|
|
||||||
fireEvent.click(expandButtons[0])
|
rtl.getByText('Row 0.0')
|
||||||
|
rtl.getByText('Row 0.1')
|
||||||
|
rtl.getByText('Row 0.2')
|
||||||
|
|
||||||
const after1 = asFragment()
|
fireEvent.click(rtl.getByText('Expand Row 0.1'))
|
||||||
|
|
||||||
expandButtons = getAllByText('👉')
|
rtl.getByText('Row 0.1.2')
|
||||||
fireEvent.click(expandButtons[0])
|
|
||||||
|
|
||||||
const after2 = asFragment()
|
fireEvent.click(rtl.getByText('Expand Row 0.1.2'))
|
||||||
|
|
||||||
expandButtons = getAllByText('👉')
|
rtl.getByText('SubComponent: 0.1.2')
|
||||||
fireEvent.click(expandButtons[0])
|
|
||||||
|
|
||||||
const after3 = asFragment()
|
fireEvent.click(rtl.getByText('Collapse Row 0'))
|
||||||
|
|
||||||
expandButtons = getAllByText('👉')
|
expect(rtl.queryByText('SubComponent: 0.1.2')).toBe(null)
|
||||||
fireEvent.click(expandButtons[0])
|
rtl.getByText('Expand Row 0')
|
||||||
|
|
||||||
const after4 = asFragment()
|
|
||||||
|
|
||||||
expandButtons = getAllByText('👇')
|
|
||||||
expandButtons.reverse().forEach(button => {
|
|
||||||
fireEvent.click(button)
|
|
||||||
})
|
|
||||||
|
|
||||||
const after5 = asFragment()
|
|
||||||
|
|
||||||
expect(before).toMatchDiffSnapshot(after1)
|
|
||||||
expect(after1).toMatchDiffSnapshot(after2)
|
|
||||||
expect(after2).toMatchDiffSnapshot(after3)
|
|
||||||
expect(after3).toMatchDiffSnapshot(after4)
|
|
||||||
expect(after4).toMatchDiffSnapshot(after5)
|
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { render, fireEvent } from '@testing-library/react'
|
import { render, fireEvent } from '../../../test-utils/react-testing'
|
||||||
import { useTable } from '../../hooks/useTable'
|
import { useTable } from '../../hooks/useTable'
|
||||||
import { useFilters } from '../useFilters'
|
import { useFilters } from '../useFilters'
|
||||||
import { useGlobalFilter } from '../useGlobalFilter'
|
import { useGlobalFilter } from '../useGlobalFilter'
|
||||||
|
|
||||||
const data = [
|
const makeData = () => [
|
||||||
{
|
{
|
||||||
firstName: 'tanner',
|
firstName: 'tanner',
|
||||||
lastName: 'linsley',
|
lastName: 'linsley',
|
||||||
@ -52,79 +52,8 @@ const defaultColumn = {
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
function Table({ columns, data }) {
|
|
||||||
const {
|
|
||||||
getTableProps,
|
|
||||||
getTableBodyProps,
|
|
||||||
headerGroups,
|
|
||||||
rows,
|
|
||||||
prepareRow,
|
|
||||||
flatColumns,
|
|
||||||
state,
|
|
||||||
setGlobalFilter,
|
|
||||||
} = useTable(
|
|
||||||
{
|
|
||||||
columns,
|
|
||||||
data,
|
|
||||||
defaultColumn,
|
|
||||||
},
|
|
||||||
useFilters,
|
|
||||||
useGlobalFilter
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<table {...getTableProps()}>
|
|
||||||
<thead>
|
|
||||||
{headerGroups.map(headerGroup => (
|
|
||||||
<tr {...headerGroup.getHeaderGroupProps()}>
|
|
||||||
{headerGroup.headers.map(column => (
|
|
||||||
<th {...column.getHeaderProps()}>
|
|
||||||
{column.render('Header')}
|
|
||||||
{column.canFilter ? column.render('Filter') : null}
|
|
||||||
</th>
|
|
||||||
))}
|
|
||||||
</tr>
|
|
||||||
))}
|
|
||||||
<tr>
|
|
||||||
<th
|
|
||||||
colSpan={flatColumns.length}
|
|
||||||
style={{
|
|
||||||
textAlign: 'left',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<span>
|
|
||||||
<input
|
|
||||||
value={state.globalFilter || ''}
|
|
||||||
onChange={e => {
|
|
||||||
setGlobalFilter(e.target.value || undefined) // Set undefined to remove the filter entirely
|
|
||||||
}}
|
|
||||||
placeholder={`Global search...`}
|
|
||||||
style={{
|
|
||||||
fontSize: '1.1rem',
|
|
||||||
border: '0',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody {...getTableBodyProps()}>
|
|
||||||
{rows.map(
|
|
||||||
(row, i) =>
|
|
||||||
prepareRow(row) || (
|
|
||||||
<tr {...row.getRowProps()}>
|
|
||||||
{row.cells.map(cell => (
|
|
||||||
<td {...cell.getCellProps()}>{cell.render('Cell')}</td>
|
|
||||||
))}
|
|
||||||
</tr>
|
|
||||||
)
|
|
||||||
)}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
const [data, setData] = React.useState(makeData)
|
||||||
const columns = React.useMemo(
|
const columns = React.useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
@ -165,38 +94,152 @@ function App() {
|
|||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
|
||||||
return <Table columns={columns} data={data} />
|
const {
|
||||||
}
|
getTableProps,
|
||||||
|
getTableBodyProps,
|
||||||
test('renders a filterable table', () => {
|
headerGroups,
|
||||||
const { getAllByPlaceholderText, getByPlaceholderText, asFragment } = render(
|
rows,
|
||||||
<App />
|
prepareRow,
|
||||||
|
visibleColumns,
|
||||||
|
state,
|
||||||
|
setGlobalFilter,
|
||||||
|
} = useTable(
|
||||||
|
{
|
||||||
|
columns,
|
||||||
|
data,
|
||||||
|
defaultColumn,
|
||||||
|
},
|
||||||
|
useFilters,
|
||||||
|
useGlobalFilter
|
||||||
)
|
)
|
||||||
|
|
||||||
const globalFilterInput = getByPlaceholderText('Global search...')
|
const reset = () => setData(makeData())
|
||||||
const filterInputs = getAllByPlaceholderText('Search...')
|
|
||||||
|
|
||||||
const beforeFilter = asFragment()
|
return (
|
||||||
|
<>
|
||||||
|
<button onClick={reset}>Reset Data</button>
|
||||||
|
<table {...getTableProps()}>
|
||||||
|
<thead>
|
||||||
|
{headerGroups.map(headerGroup => (
|
||||||
|
<tr {...headerGroup.getHeaderGroupProps()}>
|
||||||
|
{headerGroup.headers.map(column => (
|
||||||
|
<th {...column.getHeaderProps()}>
|
||||||
|
{column.render('Header')}
|
||||||
|
{column.canFilter ? column.render('Filter') : null}
|
||||||
|
</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
<tr>
|
||||||
|
<th
|
||||||
|
colSpan={visibleColumns.length}
|
||||||
|
style={{
|
||||||
|
textAlign: 'left',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>
|
||||||
|
<input
|
||||||
|
value={state.globalFilter || ''}
|
||||||
|
onChange={e => {
|
||||||
|
setGlobalFilter(e.target.value || undefined) // Set undefined to remove the filter entirely
|
||||||
|
}}
|
||||||
|
placeholder={`Global search...`}
|
||||||
|
style={{
|
||||||
|
fontSize: '1.1rem',
|
||||||
|
border: '0',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody {...getTableBodyProps()}>
|
||||||
|
{rows.map(
|
||||||
|
(row, i) =>
|
||||||
|
prepareRow(row) || (
|
||||||
|
<tr {...row.getRowProps()}>
|
||||||
|
{row.cells.map(cell => (
|
||||||
|
<td {...cell.getCellProps()}>{cell.render('Cell')}</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
test('renders a filterable table', async () => {
|
||||||
|
const rendered = render(<App />)
|
||||||
|
|
||||||
|
const resetButton = rendered.getByText('Reset Data')
|
||||||
|
const globalFilterInput = rendered.getByPlaceholderText('Global search...')
|
||||||
|
const filterInputs = rendered.getAllByPlaceholderText('Search...')
|
||||||
|
|
||||||
fireEvent.change(filterInputs[1], { target: { value: 'l' } })
|
fireEvent.change(filterInputs[1], { target: { value: 'l' } })
|
||||||
|
expect(
|
||||||
const afterFilter1 = asFragment()
|
rendered
|
||||||
|
.queryAllByRole('row')
|
||||||
|
.slice(3)
|
||||||
|
.map(row => Array.from(row.children)[0].textContent)
|
||||||
|
).toEqual(['firstName: tanner', 'firstName: jaylen'])
|
||||||
|
|
||||||
fireEvent.change(filterInputs[1], { target: { value: 'er' } })
|
fireEvent.change(filterInputs[1], { target: { value: 'er' } })
|
||||||
|
expect(
|
||||||
|
rendered
|
||||||
|
.queryAllByRole('row')
|
||||||
|
.slice(3)
|
||||||
|
.map(row => Array.from(row.children)[0].textContent)
|
||||||
|
).toEqual(['firstName: derek', 'firstName: joe'])
|
||||||
|
|
||||||
const afterFilter2 = asFragment()
|
fireEvent.change(filterInputs[2], { target: { value: 'nothing' } })
|
||||||
|
expect(
|
||||||
|
rendered
|
||||||
|
.queryAllByRole('row')
|
||||||
|
.slice(3)
|
||||||
|
.map(row => Array.from(row.children)[0].textContent)
|
||||||
|
).toEqual([])
|
||||||
|
|
||||||
fireEvent.change(filterInputs[1], { target: { value: '' } })
|
fireEvent.change(filterInputs[1], { target: { value: '' } })
|
||||||
|
expect(
|
||||||
|
rendered
|
||||||
|
.queryAllByRole('row')
|
||||||
|
.slice(3)
|
||||||
|
.map(row => Array.from(row.children)[0].textContent)
|
||||||
|
).toEqual([])
|
||||||
|
|
||||||
const afterFilter3 = asFragment()
|
fireEvent.change(filterInputs[2], { target: { value: '' } })
|
||||||
|
expect(
|
||||||
|
rendered
|
||||||
|
.queryAllByRole('row')
|
||||||
|
.slice(3)
|
||||||
|
.map(row => Array.from(row.children)[0].textContent)
|
||||||
|
).toEqual([
|
||||||
|
'firstName: tanner',
|
||||||
|
'firstName: derek',
|
||||||
|
'firstName: joe',
|
||||||
|
'firstName: jaylen',
|
||||||
|
])
|
||||||
|
|
||||||
fireEvent.change(globalFilterInput, { target: { value: 'li' } })
|
fireEvent.change(globalFilterInput, { target: { value: 'li' } })
|
||||||
|
expect(
|
||||||
|
rendered
|
||||||
|
.queryAllByRole('row')
|
||||||
|
.slice(3)
|
||||||
|
.map(row => Array.from(row.children)[0].textContent)
|
||||||
|
).toEqual(['firstName: tanner', 'firstName: joe', 'firstName: jaylen'])
|
||||||
|
|
||||||
const afterFilter4 = asFragment()
|
fireEvent.click(resetButton)
|
||||||
|
expect(
|
||||||
expect(beforeFilter).toMatchSnapshot()
|
rendered
|
||||||
expect(afterFilter1).toMatchSnapshot()
|
.queryAllByRole('row')
|
||||||
expect(afterFilter2).toMatchSnapshot()
|
.slice(3)
|
||||||
expect(afterFilter3).toMatchSnapshot()
|
.map(row => Array.from(row.children)[0].textContent)
|
||||||
expect(afterFilter4).toMatchSnapshot()
|
).toEqual([
|
||||||
|
'firstName: tanner',
|
||||||
|
'firstName: derek',
|
||||||
|
'firstName: joe',
|
||||||
|
'firstName: jaylen',
|
||||||
|
])
|
||||||
})
|
})
|
||||||
|
|||||||
157
src/plugin-hooks/tests/useFlexLayout.test.js
Normal file
157
src/plugin-hooks/tests/useFlexLayout.test.js
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { useTable } from '../../hooks/useTable'
|
||||||
|
import { useFlexLayout } from '../useFlexLayout'
|
||||||
|
import { render } from '../../../test-utils/react-testing'
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
firstName: 'tanner',
|
||||||
|
lastName: 'linsley',
|
||||||
|
age: 29,
|
||||||
|
visits: 100,
|
||||||
|
status: 'In Relationship',
|
||||||
|
progress: 50,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
firstName: 'derek',
|
||||||
|
lastName: 'perkins',
|
||||||
|
age: 30,
|
||||||
|
visits: 40,
|
||||||
|
status: 'Single',
|
||||||
|
progress: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
firstName: 'joe',
|
||||||
|
lastName: 'bergevin',
|
||||||
|
age: 45,
|
||||||
|
visits: 20,
|
||||||
|
status: 'Complicated',
|
||||||
|
progress: 10,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const defaultColumn = {
|
||||||
|
Cell: ({ cell: { value }, column: { id } }) => `${id}: ${value}`,
|
||||||
|
width: 200,
|
||||||
|
minWidth: 100,
|
||||||
|
maxWidth: 300,
|
||||||
|
}
|
||||||
|
|
||||||
|
function Table({ columns, data }) {
|
||||||
|
const {
|
||||||
|
getTableProps,
|
||||||
|
getTableBodyProps,
|
||||||
|
headerGroups,
|
||||||
|
rows,
|
||||||
|
prepareRow,
|
||||||
|
} = useTable(
|
||||||
|
{
|
||||||
|
columns,
|
||||||
|
data,
|
||||||
|
defaultColumn,
|
||||||
|
},
|
||||||
|
useFlexLayout
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div {...getTableProps()} className="table">
|
||||||
|
<div>
|
||||||
|
{headerGroups.map(headerGroup => (
|
||||||
|
<div {...headerGroup.getHeaderGroupProps()} className="row">
|
||||||
|
{headerGroup.headers.map(column => (
|
||||||
|
<div {...column.getHeaderProps()} className="cell header">
|
||||||
|
{column.render('Header')}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div {...getTableBodyProps()}>
|
||||||
|
{rows.map(
|
||||||
|
(row, i) =>
|
||||||
|
prepareRow(row) || (
|
||||||
|
<div {...row.getRowProps()} className="row">
|
||||||
|
{row.cells.map(cell => {
|
||||||
|
return (
|
||||||
|
<div {...cell.getCellProps()} className="cell">
|
||||||
|
{cell.render('Cell')}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const columns = React.useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
Header: 'Name',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
Header: 'First Name',
|
||||||
|
accessor: 'firstName',
|
||||||
|
width: 250,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Last Name',
|
||||||
|
accessor: 'lastName',
|
||||||
|
width: 350,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Info',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
Header: 'Age',
|
||||||
|
accessor: 'age',
|
||||||
|
minWidth: 300,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Visits',
|
||||||
|
accessor: 'visits',
|
||||||
|
maxWidth: 150,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Status',
|
||||||
|
accessor: 'status',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Profile Progress',
|
||||||
|
accessor: 'progress',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
|
return <Table columns={columns} data={data} />
|
||||||
|
}
|
||||||
|
|
||||||
|
test('renders a table', () => {
|
||||||
|
const rendered = render(<App />)
|
||||||
|
|
||||||
|
const [headerRow, firstRow] = rendered.queryAllByRole('row')
|
||||||
|
|
||||||
|
expect(headerRow.getAttribute('style')).toEqual(
|
||||||
|
'display: flex; flex: 1 0 auto; min-width: 800px;'
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(
|
||||||
|
Array.from(firstRow.children).map(d => d.getAttribute('style'))
|
||||||
|
).toEqual([
|
||||||
|
'box-sizing: border-box; flex: 250 0 auto; min-width: 100px; width: 250px;',
|
||||||
|
'box-sizing: border-box; flex: 300 0 auto; min-width: 100px; width: 300px;',
|
||||||
|
'box-sizing: border-box; flex: 300 0 auto; min-width: 300px; width: 300px;',
|
||||||
|
'box-sizing: border-box; flex: 150 0 auto; min-width: 100px; width: 150px;',
|
||||||
|
'box-sizing: border-box; flex: 200 0 auto; min-width: 100px; width: 200px;',
|
||||||
|
'box-sizing: border-box; flex: 200 0 auto; min-width: 100px; width: 200px;',
|
||||||
|
])
|
||||||
|
})
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { render, fireEvent } from '@testing-library/react'
|
import { render, fireEvent } from '../../../test-utils/react-testing'
|
||||||
import { useTable } from '../../hooks/useTable'
|
import { useTable } from '../../hooks/useTable'
|
||||||
import { useGroupBy } from '../useGroupBy'
|
import { useGroupBy } from '../useGroupBy'
|
||||||
import { useExpanded } from '../useExpanded'
|
import { useExpanded } from '../useExpanded'
|
||||||
@ -29,6 +29,14 @@ const data = [
|
|||||||
status: 'Complicated',
|
status: 'Complicated',
|
||||||
progress: 10,
|
progress: 10,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
firstName: 'joe',
|
||||||
|
lastName: 'dirt',
|
||||||
|
age: 20,
|
||||||
|
visits: 5,
|
||||||
|
status: 'Complicated',
|
||||||
|
progress: 97,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
firstName: 'jaylen',
|
firstName: 'jaylen',
|
||||||
lastName: 'linsley',
|
lastName: 'linsley',
|
||||||
@ -82,7 +90,7 @@ function Table({ columns, data }) {
|
|||||||
{column.canGroupBy ? (
|
{column.canGroupBy ? (
|
||||||
// If the column can be grouped, let's add a toggle
|
// If the column can be grouped, let's add a toggle
|
||||||
<span {...column.getGroupByToggleProps()}>
|
<span {...column.getGroupByToggleProps()}>
|
||||||
{column.isGrouped ? '🛑' : '👊'}
|
{column.isGrouped ? 'Ungroup' : 'Group'} {column.id}
|
||||||
</span>
|
</span>
|
||||||
) : null}
|
) : null}
|
||||||
{column.render('Header')}
|
{column.render('Header')}
|
||||||
@ -113,7 +121,7 @@ function Table({ columns, data }) {
|
|||||||
</>
|
</>
|
||||||
) : cell.isAggregated ? (
|
) : cell.isAggregated ? (
|
||||||
cell.render('Aggregated')
|
cell.render('Aggregated')
|
||||||
) : cell.isRepeatedValue ? null : (
|
) : cell.isPlaceholder ? null : (
|
||||||
cell.render('Cell')
|
cell.render('Cell')
|
||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
@ -127,11 +135,14 @@ function Table({ columns, data }) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function roundedMedian(values) {
|
// This is a custom aggregator that
|
||||||
let min = values[0] || ''
|
// takes in an array of leaf values and
|
||||||
let max = values[0] || ''
|
// returns the rounded median
|
||||||
|
function roundedMedian(leafValues) {
|
||||||
|
let min = leafValues[0] || 0
|
||||||
|
let max = leafValues[0] || 0
|
||||||
|
|
||||||
values.forEach(value => {
|
leafValues.forEach(value => {
|
||||||
min = Math.min(min, value)
|
min = Math.min(min, value)
|
||||||
max = Math.max(max, value)
|
max = Math.max(max, value)
|
||||||
})
|
})
|
||||||
@ -148,14 +159,16 @@ function App() {
|
|||||||
{
|
{
|
||||||
Header: 'First Name',
|
Header: 'First Name',
|
||||||
accessor: 'firstName',
|
accessor: 'firstName',
|
||||||
aggregate: ['sum', 'count'],
|
aggregate: 'count',
|
||||||
Aggregated: ({ cell: { value } }) => `${value} Names`,
|
Aggregated: ({ cell: { value } }) =>
|
||||||
|
`First Name Aggregated: ${value} Names`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Last Name',
|
Header: 'Last Name',
|
||||||
accessor: 'lastName',
|
accessor: 'lastName',
|
||||||
aggregate: ['sum', 'uniqueCount'],
|
aggregate: 'uniqueCount',
|
||||||
Aggregated: ({ cell: { value } }) => `${value} Unique Names`,
|
Aggregated: ({ cell: { value } }) =>
|
||||||
|
`Last Name Aggregated: ${value} Unique Names`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -166,23 +179,62 @@ function App() {
|
|||||||
Header: 'Age',
|
Header: 'Age',
|
||||||
accessor: 'age',
|
accessor: 'age',
|
||||||
aggregate: 'average',
|
aggregate: 'average',
|
||||||
Aggregated: ({ cell: { value } }) => `${value} (avg)`,
|
Aggregated: ({ cell: { value } }) =>
|
||||||
|
`Age Aggregated: ${value} (avg)`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Visits',
|
Header: 'Visits',
|
||||||
accessor: 'visits',
|
accessor: 'visits',
|
||||||
aggregate: 'sum',
|
aggregate: 'sum',
|
||||||
Aggregated: ({ cell: { value } }) => `${value} (total)`,
|
Aggregated: ({ cell: { value } }) =>
|
||||||
|
`Visits Aggregated: ${value} (total)`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Min Visits',
|
||||||
|
id: 'minVisits',
|
||||||
|
accessor: 'visits',
|
||||||
|
aggregate: 'min',
|
||||||
|
Aggregated: ({ cell: { value } }) =>
|
||||||
|
`Visits Aggregated: ${value} (min)`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Max Visits',
|
||||||
|
id: 'maxVisits',
|
||||||
|
accessor: 'visits',
|
||||||
|
aggregate: 'max',
|
||||||
|
Aggregated: ({ cell: { value } }) =>
|
||||||
|
`Visits Aggregated: ${value} (max)`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Min/Max Visits',
|
||||||
|
id: 'minMaxVisits',
|
||||||
|
accessor: 'visits',
|
||||||
|
aggregate: 'minMax',
|
||||||
|
Aggregated: ({ cell: { value } }) =>
|
||||||
|
`Visits Aggregated: ${value} (minMax)`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Status',
|
Header: 'Status',
|
||||||
accessor: 'status',
|
accessor: 'status',
|
||||||
|
aggregate: 'unique',
|
||||||
|
Aggregated: ({ cell: { value } }) =>
|
||||||
|
`Visits Aggregated: ${value.join(', ')} (unique)`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Profile Progress',
|
Header: 'Profile Progress (Median)',
|
||||||
accessor: 'progress',
|
accessor: 'progress',
|
||||||
|
id: 'progress',
|
||||||
|
aggregate: 'median',
|
||||||
|
Aggregated: ({ cell: { value } }) =>
|
||||||
|
`Process Aggregated: ${value} (median)`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Profile Progress (Rounded Median)',
|
||||||
|
accessor: 'progress',
|
||||||
|
id: 'progressRounded',
|
||||||
aggregate: roundedMedian,
|
aggregate: roundedMedian,
|
||||||
Aggregated: ({ cell: { value } }) => `${value} (med)`,
|
Aggregated: ({ cell: { value } }) =>
|
||||||
|
`Process Aggregated: ${value} (rounded median)`,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@ -194,20 +246,26 @@ function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test('renders a groupable table', () => {
|
test('renders a groupable table', () => {
|
||||||
const { getAllByText, asFragment } = render(<App />)
|
const rendered = render(<App />)
|
||||||
|
|
||||||
const groupByButtons = getAllByText('👊')
|
fireEvent.click(rendered.getByText('Group lastName'))
|
||||||
|
|
||||||
const beforeGrouping = asFragment()
|
rendered.getByText('lastName: linsley (2)')
|
||||||
|
|
||||||
fireEvent.click(groupByButtons[1])
|
fireEvent.click(rendered.getByText('Group visits'))
|
||||||
|
|
||||||
const afterGrouping1 = asFragment()
|
fireEvent.click(rendered.getByText('Ungroup lastName'))
|
||||||
|
|
||||||
fireEvent.click(groupByButtons[3])
|
rendered.getByText('visits: 100 (1)')
|
||||||
|
|
||||||
const afterGrouping2 = asFragment()
|
fireEvent.click(rendered.getByText('Ungroup visits'))
|
||||||
|
|
||||||
expect(beforeGrouping).toMatchDiffSnapshot(afterGrouping1)
|
fireEvent.click(rendered.getByText('Group firstName'))
|
||||||
expect(afterGrouping1).toMatchDiffSnapshot(afterGrouping2)
|
|
||||||
|
rendered.getByText('firstName: tanner (1)')
|
||||||
|
|
||||||
|
rendered.debugDiff(false)
|
||||||
|
fireEvent.click(rendered.getByText('Group age'))
|
||||||
|
|
||||||
|
rendered.getByText('Last Name Aggregated: 2 Unique Names')
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { render, fireEvent } from '@testing-library/react'
|
import { render, fireEvent } from '../../../test-utils/react-testing'
|
||||||
import { useTable } from '../../hooks/useTable'
|
import { useTable } from '../../hooks/useTable'
|
||||||
import { usePagination } from '../usePagination'
|
import { usePagination } from '../usePagination'
|
||||||
|
|
||||||
const data = [...new Array(100)].map((d, i) => ({
|
const data = [...new Array(1000)].map((d, i) => ({
|
||||||
firstName: 'tanner ' + (i + 1),
|
firstName: `tanner ${i + 1}`,
|
||||||
lastName: 'linsley',
|
lastName: 'linsley',
|
||||||
age: 29,
|
age: 29,
|
||||||
visits: 100,
|
visits: 100,
|
||||||
@ -100,6 +100,7 @@ function Table({ columns, data }) {
|
|||||||
onChange={e => {
|
onChange={e => {
|
||||||
setPageSize(Number(e.target.value))
|
setPageSize(Number(e.target.value))
|
||||||
}}
|
}}
|
||||||
|
data-testid="page-size-select"
|
||||||
>
|
>
|
||||||
{[10, 20, 30, 40, 50].map(pageSize => (
|
{[10, 20, 30, 40, 50].map(pageSize => (
|
||||||
<option key={pageSize} value={pageSize}>
|
<option key={pageSize} value={pageSize}>
|
||||||
@ -157,23 +158,30 @@ function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test('renders a paginated table', () => {
|
test('renders a paginated table', () => {
|
||||||
const { getByText, asFragment } = render(<App />)
|
const rendered = render(<App />)
|
||||||
|
|
||||||
const fragment1 = asFragment()
|
expect(rendered.queryAllByRole('cell')[0].textContent).toEqual('tanner 21')
|
||||||
|
|
||||||
fireEvent.click(getByText('>'))
|
fireEvent.click(rendered.getByText('>'))
|
||||||
|
expect(rendered.queryAllByRole('cell')[0].textContent).toEqual('tanner 31')
|
||||||
|
|
||||||
const fragment2 = asFragment()
|
fireEvent.click(rendered.getByText('>'))
|
||||||
|
expect(rendered.queryAllByRole('cell')[0].textContent).toEqual('tanner 41')
|
||||||
|
|
||||||
fireEvent.click(getByText('>'))
|
fireEvent.click(rendered.getByText('>>'))
|
||||||
|
expect(rendered.queryAllByRole('cell')[0].textContent).toEqual('tanner 991')
|
||||||
|
|
||||||
const fragment3 = asFragment()
|
fireEvent.click(rendered.getByText('<<'))
|
||||||
|
expect(rendered.queryAllByRole('cell')[0].textContent).toEqual('tanner 1')
|
||||||
|
|
||||||
fireEvent.click(getByText('>>'))
|
fireEvent.change(rendered.getByTestId('page-size-select'), {
|
||||||
|
target: { value: 30 },
|
||||||
|
})
|
||||||
|
|
||||||
const fragment4 = asFragment()
|
expect(
|
||||||
|
rendered
|
||||||
expect(fragment1).toMatchDiffSnapshot(fragment2)
|
.queryAllByRole('row')
|
||||||
expect(fragment2).toMatchDiffSnapshot(fragment3)
|
.slice(2)
|
||||||
expect(fragment3).toMatchDiffSnapshot(fragment4)
|
.reverse()[0].children[0].textContent
|
||||||
|
).toEqual('tanner 30')
|
||||||
})
|
})
|
||||||
|
|||||||
234
src/plugin-hooks/tests/useResizeColumns.test.js
Normal file
234
src/plugin-hooks/tests/useResizeColumns.test.js
Normal file
@ -0,0 +1,234 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { render, fireEvent } from '@testing-library/react'
|
||||||
|
import { useTable } from '../../hooks/useTable'
|
||||||
|
import { useBlockLayout } from '../useBlockLayout'
|
||||||
|
import { useResizeColumns } from '../useResizeColumns'
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
firstName: 'tanner',
|
||||||
|
lastName: 'linsley',
|
||||||
|
age: 29,
|
||||||
|
visits: 100,
|
||||||
|
status: 'In Relationship',
|
||||||
|
progress: 50,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
firstName: 'derek',
|
||||||
|
lastName: 'perkins',
|
||||||
|
age: 40,
|
||||||
|
visits: 40,
|
||||||
|
status: 'Single',
|
||||||
|
progress: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
firstName: 'joe',
|
||||||
|
lastName: 'bergevin',
|
||||||
|
age: 45,
|
||||||
|
visits: 20,
|
||||||
|
status: 'Complicated',
|
||||||
|
progress: 10,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
function Table({ columns, data }) {
|
||||||
|
const defaultColumn = React.useMemo(
|
||||||
|
() => ({
|
||||||
|
minWidth: 30,
|
||||||
|
width: 150,
|
||||||
|
maxWidth: 400,
|
||||||
|
}),
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
|
const {
|
||||||
|
getTableProps,
|
||||||
|
getTableBodyProps,
|
||||||
|
headerGroups,
|
||||||
|
rows,
|
||||||
|
prepareRow,
|
||||||
|
} = useTable(
|
||||||
|
{
|
||||||
|
columns,
|
||||||
|
data,
|
||||||
|
defaultColumn,
|
||||||
|
},
|
||||||
|
useBlockLayout,
|
||||||
|
useResizeColumns
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div {...getTableProps()} className="table">
|
||||||
|
<div>
|
||||||
|
{headerGroups.map(headerGroup => (
|
||||||
|
<div {...headerGroup.getHeaderGroupProps()} className="tr">
|
||||||
|
{headerGroup.headers.map(column => (
|
||||||
|
<div {...column.getHeaderProps()} className="th">
|
||||||
|
{column.render('Header')}
|
||||||
|
{/* Use column.getResizerProps to hook up the events correctly */}
|
||||||
|
<div
|
||||||
|
{...column.getResizerProps()}
|
||||||
|
className={`resizer${column.isResizing ? ' isResizing' : ''}`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div {...getTableBodyProps()}>
|
||||||
|
{rows.map((row, i) => {
|
||||||
|
prepareRow(row)
|
||||||
|
return (
|
||||||
|
<div {...row.getRowProps()} className="tr">
|
||||||
|
{row.cells.map(cell => {
|
||||||
|
return (
|
||||||
|
<div {...cell.getCellProps()} className="td">
|
||||||
|
{cell.render('Cell')}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const columns = React.useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
Header: 'Name',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
Header: 'First Name',
|
||||||
|
accessor: 'firstName',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Last Name',
|
||||||
|
accessor: 'lastName',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Info',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
Header: 'Age',
|
||||||
|
accessor: 'age',
|
||||||
|
width: 50,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Visits',
|
||||||
|
accessor: 'visits',
|
||||||
|
width: 60,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Status',
|
||||||
|
accessor: 'status',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Profile Progress',
|
||||||
|
accessor: 'progress',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
|
return <Table columns={columns} data={data} />
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = 20
|
||||||
|
const move = 100
|
||||||
|
const end = 100
|
||||||
|
|
||||||
|
const sizesBefore = [
|
||||||
|
'300px',
|
||||||
|
'410px',
|
||||||
|
'150px',
|
||||||
|
'150px',
|
||||||
|
'50px',
|
||||||
|
'60px',
|
||||||
|
'150px',
|
||||||
|
'150px',
|
||||||
|
]
|
||||||
|
|
||||||
|
const sizesAfter = [
|
||||||
|
'300px',
|
||||||
|
'490px',
|
||||||
|
'150px',
|
||||||
|
'150px',
|
||||||
|
'59.75609756097561px',
|
||||||
|
'71.70731707317073px',
|
||||||
|
'179.26829268292684px',
|
||||||
|
'179.26829268292684px',
|
||||||
|
]
|
||||||
|
|
||||||
|
test('table can be resized by a mouse', () => {
|
||||||
|
const rtl = render(<App />)
|
||||||
|
|
||||||
|
const infoResizer = rtl
|
||||||
|
.getAllByRole('separator')
|
||||||
|
.find(d => d.previousSibling.textContent === 'Info')
|
||||||
|
|
||||||
|
expect(rtl.getAllByRole('columnheader').map(d => d.style.width)).toEqual(
|
||||||
|
sizesBefore
|
||||||
|
)
|
||||||
|
|
||||||
|
fireEvent.mouseDown(infoResizer, { clientX: start })
|
||||||
|
fireEvent.mouseMove(infoResizer, { clientX: move })
|
||||||
|
fireEvent.mouseUp(infoResizer, { clientX: end })
|
||||||
|
|
||||||
|
expect(rtl.getAllByRole('columnheader').map(d => d.style.width)).toEqual(
|
||||||
|
sizesAfter
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('table can be resized by a touch device', () => {
|
||||||
|
const rtl = render(<App />)
|
||||||
|
|
||||||
|
const infoResizer = rtl
|
||||||
|
.getAllByRole('separator')
|
||||||
|
.find(d => d.previousSibling.textContent === 'Info')
|
||||||
|
|
||||||
|
expect(rtl.getAllByRole('columnheader').map(d => d.style.width)).toEqual(
|
||||||
|
sizesBefore
|
||||||
|
)
|
||||||
|
|
||||||
|
fireEvent.touchStart(infoResizer, { touches: [{ clientX: start }] })
|
||||||
|
fireEvent.touchMove(infoResizer, { touches: [{ clientX: move }] })
|
||||||
|
fireEvent.touchEnd(infoResizer, { touches: [{ clientX: end }] })
|
||||||
|
|
||||||
|
expect(rtl.getAllByRole('columnheader').map(d => d.style.width)).toEqual(
|
||||||
|
sizesAfter
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
test('table can not be resized with multiple touches', () => {
|
||||||
|
const rtl = render(<App />)
|
||||||
|
|
||||||
|
const infoResizer = rtl
|
||||||
|
.getAllByRole('separator')
|
||||||
|
.find(d => d.previousSibling.textContent === 'Info')
|
||||||
|
|
||||||
|
expect(rtl.getAllByRole('columnheader').map(d => d.style.width)).toEqual(
|
||||||
|
sizesBefore
|
||||||
|
)
|
||||||
|
|
||||||
|
fireEvent.touchStart(infoResizer, {
|
||||||
|
touches: [{ clientX: start }, { clientX: start }],
|
||||||
|
})
|
||||||
|
fireEvent.touchMove(infoResizer, {
|
||||||
|
touches: [{ clientX: move }, { clientX: move }],
|
||||||
|
})
|
||||||
|
fireEvent.touchEnd(infoResizer, {
|
||||||
|
touches: [{ clientX: end }, { clientX: end }],
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(rtl.getAllByRole('columnheader').map(d => d.style.width)).toEqual(
|
||||||
|
sizesBefore
|
||||||
|
)
|
||||||
|
})
|
||||||
@ -84,7 +84,7 @@ function Table({ columns, data }) {
|
|||||||
useRowSelect,
|
useRowSelect,
|
||||||
useExpanded,
|
useExpanded,
|
||||||
hooks => {
|
hooks => {
|
||||||
hooks.flatColumns.push(columns => [
|
hooks.visibleColumns.push(columns => [
|
||||||
// Let's make a column for selection
|
// Let's make a column for selection
|
||||||
{
|
{
|
||||||
id: 'selection',
|
id: 'selection',
|
||||||
@ -142,12 +142,6 @@ function Table({ columns, data }) {
|
|||||||
)}
|
)}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p>Selected Rows: {Object.keys(selectedRowIds).length}</p>
|
|
||||||
<pre>
|
|
||||||
<code>
|
|
||||||
{JSON.stringify({ selectedRowIds: selectedRowIds }, null, 2)}
|
|
||||||
</code>
|
|
||||||
</pre>
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -170,11 +164,13 @@ function App() {
|
|||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
id: 'selectedStatus',
|
id: 'selectedStatus',
|
||||||
Cell: ({ row }) => (
|
Cell: ({ row }) =>
|
||||||
<div>
|
row.isSelected ? (
|
||||||
Row {row.id} {row.isSelected ? 'Selected' : 'Not Selected'}
|
<div>
|
||||||
</div>
|
<div>Selected</div>
|
||||||
),
|
<div>Row {row.id}</div>
|
||||||
|
</div>
|
||||||
|
) : null,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Header: 'Name',
|
Header: 'Name',
|
||||||
@ -218,43 +214,43 @@ function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test('renders a table with selectable rows', () => {
|
test('renders a table with selectable rows', () => {
|
||||||
const { getByLabelText, getAllByLabelText, asFragment } = render(<App />)
|
const rtl = render(<App />)
|
||||||
|
|
||||||
const fragment1 = asFragment()
|
fireEvent.click(rtl.getByLabelText('Select All'))
|
||||||
|
|
||||||
fireEvent.click(getByLabelText('Select All'))
|
expect(rtl.getAllByText('Selected').length).toBe(24)
|
||||||
|
|
||||||
const fragment2 = asFragment()
|
fireEvent.click(rtl.getAllByLabelText('Select Row')[2])
|
||||||
|
|
||||||
fireEvent.click(getByLabelText('Select All'))
|
expect(rtl.queryAllByText('Selected').length).toBe(23)
|
||||||
|
|
||||||
const fragment3 = asFragment()
|
fireEvent.click(rtl.getByLabelText('Select All'))
|
||||||
|
|
||||||
fireEvent.click(getAllByLabelText('Select Row')[0])
|
expect(rtl.queryAllByText('Selected').length).toBe(24)
|
||||||
fireEvent.click(getAllByLabelText('Select Row')[2])
|
|
||||||
|
|
||||||
const fragment4 = asFragment()
|
fireEvent.click(rtl.getByLabelText('Select All'))
|
||||||
|
|
||||||
fireEvent.click(getAllByLabelText('Select Row')[2])
|
expect(rtl.queryAllByText('Selected').length).toBe(0)
|
||||||
|
|
||||||
const fragment5 = asFragment()
|
fireEvent.click(rtl.getAllByLabelText('Select Row')[0])
|
||||||
|
fireEvent.click(rtl.getAllByLabelText('Select Row')[2])
|
||||||
|
|
||||||
fireEvent.click(getAllByLabelText('Select Row')[3])
|
rtl.getByText('Row 0')
|
||||||
|
rtl.getByText('Row 2')
|
||||||
|
|
||||||
const fragment6 = asFragment()
|
fireEvent.click(rtl.getAllByLabelText('Select Row')[2])
|
||||||
fireEvent.click(getAllByLabelText('Select Row')[4])
|
|
||||||
|
|
||||||
const fragment7 = asFragment()
|
expect(rtl.queryByText('Row 2')).toBeNull()
|
||||||
|
|
||||||
fireEvent.click(getAllByLabelText('Select Row')[4])
|
fireEvent.click(rtl.getAllByLabelText('Select Row')[3])
|
||||||
|
|
||||||
const fragment8 = asFragment()
|
rtl.queryByText('Row 3')
|
||||||
|
|
||||||
expect(fragment1).toMatchDiffSnapshot(fragment2)
|
fireEvent.click(rtl.getAllByLabelText('Select Row')[4])
|
||||||
expect(fragment2).toMatchDiffSnapshot(fragment3)
|
|
||||||
expect(fragment3).toMatchDiffSnapshot(fragment4)
|
rtl.queryByText('Row 4')
|
||||||
expect(fragment4).toMatchDiffSnapshot(fragment5)
|
|
||||||
expect(fragment5).toMatchDiffSnapshot(fragment6)
|
fireEvent.click(rtl.getAllByLabelText('Select Row')[4])
|
||||||
expect(fragment6).toMatchDiffSnapshot(fragment7)
|
|
||||||
expect(fragment7).toMatchDiffSnapshot(fragment8)
|
expect(rtl.queryByText('Row 4')).toBeNull()
|
||||||
})
|
})
|
||||||
|
|||||||
185
src/plugin-hooks/tests/useRowState.test.js
Normal file
185
src/plugin-hooks/tests/useRowState.test.js
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import { render, fireEvent } from '../../../test-utils/react-testing'
|
||||||
|
import { useTable } from '../../hooks/useTable'
|
||||||
|
import { useRowState } from '../useRowState'
|
||||||
|
import { useGlobalFilter } from '../useGlobalFilter'
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{
|
||||||
|
firstName: 'tanner',
|
||||||
|
lastName: 'linsley',
|
||||||
|
age: 29,
|
||||||
|
visits: 100,
|
||||||
|
status: 'In Relationship',
|
||||||
|
progress: 50,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
firstName: 'derek',
|
||||||
|
lastName: 'perkins',
|
||||||
|
age: 40,
|
||||||
|
visits: 40,
|
||||||
|
status: 'Single',
|
||||||
|
progress: 80,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
firstName: 'joe',
|
||||||
|
lastName: 'bergevin',
|
||||||
|
age: 45,
|
||||||
|
visits: 20,
|
||||||
|
status: 'Complicated',
|
||||||
|
progress: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
firstName: 'jaylen',
|
||||||
|
lastName: 'linsley',
|
||||||
|
age: 26,
|
||||||
|
visits: 99,
|
||||||
|
status: 'In Relationship',
|
||||||
|
progress: 70,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
const defaultColumn = {
|
||||||
|
Cell: ({ column, cell, row }) => (
|
||||||
|
<div>
|
||||||
|
Row {row.id} Cell {column.id} Count {cell.state.count}{' '}
|
||||||
|
<button
|
||||||
|
onClick={() => cell.setState(old => ({ ...old, count: old.count + 1 }))}
|
||||||
|
>
|
||||||
|
Row {row.id} Cell {column.id} Toggle
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
function Table({ columns, data }) {
|
||||||
|
const {
|
||||||
|
getTableProps,
|
||||||
|
getTableBodyProps,
|
||||||
|
headerGroups,
|
||||||
|
rows,
|
||||||
|
prepareRow,
|
||||||
|
} = useTable(
|
||||||
|
{
|
||||||
|
columns,
|
||||||
|
data,
|
||||||
|
defaultColumn,
|
||||||
|
initialRowStateAccessor: () => ({ count: 0 }),
|
||||||
|
initialCellStateAccessor: () => ({ count: 0 }),
|
||||||
|
},
|
||||||
|
useRowState,
|
||||||
|
useGlobalFilter
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<table {...getTableProps()}>
|
||||||
|
<thead>
|
||||||
|
{headerGroups.map(headerGroup => (
|
||||||
|
<tr {...headerGroup.getHeaderGroupProps()}>
|
||||||
|
{headerGroup.headers.map(column => (
|
||||||
|
<th {...column.getHeaderProps()}>{column.render('Header')}</th>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</thead>
|
||||||
|
<tbody {...getTableBodyProps()}>
|
||||||
|
{rows.map(
|
||||||
|
(row, i) =>
|
||||||
|
prepareRow(row) || (
|
||||||
|
<tr {...row.getRowProps()}>
|
||||||
|
<td>
|
||||||
|
<pre>Row Count {row.state.count}</pre>
|
||||||
|
<button
|
||||||
|
onClick={() =>
|
||||||
|
row.setState(old => ({
|
||||||
|
...old,
|
||||||
|
count: old.count + 1,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
Row {row.id} Toggle
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
{row.cells.map(cell => (
|
||||||
|
<td {...cell.getCellProps()}>{cell.render('Cell')}</td>
|
||||||
|
))}
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const columns = React.useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
Header: 'Name',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
Header: 'First Name',
|
||||||
|
accessor: 'firstName',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Last Name',
|
||||||
|
accessor: 'lastName',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Info',
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
Header: 'Age',
|
||||||
|
accessor: 'age',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Visits',
|
||||||
|
accessor: 'visits',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Status',
|
||||||
|
accessor: 'status',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Header: 'Profile Progress',
|
||||||
|
accessor: 'progress',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
|
return <Table columns={columns} data={data} />
|
||||||
|
}
|
||||||
|
|
||||||
|
test('renders a filterable table', () => {
|
||||||
|
const rendered = render(<App />)
|
||||||
|
|
||||||
|
fireEvent.click(rendered.getByText('Row 1 Toggle'))
|
||||||
|
fireEvent.click(rendered.getByText('Row 1 Toggle'))
|
||||||
|
|
||||||
|
rendered.getByText('Row Count 2')
|
||||||
|
|
||||||
|
fireEvent.click(rendered.getByText('Row 1 Cell firstName Toggle'))
|
||||||
|
|
||||||
|
rendered.getByText('Row 1 Cell firstName Count 1')
|
||||||
|
|
||||||
|
fireEvent.click(rendered.getByText('Row 2 Cell lastName Toggle'))
|
||||||
|
fireEvent.click(rendered.getByText('Row 2 Cell lastName Toggle'))
|
||||||
|
|
||||||
|
rendered.getByText('Row 2 Cell lastName Count 2')
|
||||||
|
|
||||||
|
fireEvent.click(rendered.getByText('Row 3 Cell age Toggle'))
|
||||||
|
fireEvent.click(rendered.getByText('Row 3 Cell age Toggle'))
|
||||||
|
fireEvent.click(rendered.getByText('Row 3 Cell age Toggle'))
|
||||||
|
|
||||||
|
rendered.getByText('Row 3 Cell age Count 3')
|
||||||
|
|
||||||
|
fireEvent.click(rendered.getByText('Row 1 Toggle'))
|
||||||
|
fireEvent.click(rendered.getByText('Row 1 Toggle'))
|
||||||
|
|
||||||
|
rendered.getByText('Row Count 4')
|
||||||
|
})
|
||||||
@ -1,5 +1,5 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { render, fireEvent } from '@testing-library/react'
|
import { render, fireEvent } from '../../../test-utils/react-testing'
|
||||||
import { useTable } from '../../hooks/useTable'
|
import { useTable } from '../../hooks/useTable'
|
||||||
import { useSortBy } from '../useSortBy'
|
import { useSortBy } from '../useSortBy'
|
||||||
|
|
||||||
@ -10,7 +10,7 @@ const data = [
|
|||||||
age: 29,
|
age: 29,
|
||||||
visits: 100,
|
visits: 100,
|
||||||
status: 'In Relationship',
|
status: 'In Relationship',
|
||||||
progress: 50,
|
progress: 80,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
firstName: 'derek',
|
firstName: 'derek',
|
||||||
@ -61,9 +61,9 @@ function Table({ columns, data }) {
|
|||||||
<th {...column.getHeaderProps(column.getSortByToggleProps())}>
|
<th {...column.getHeaderProps(column.getSortByToggleProps())}>
|
||||||
{column.render('Header')}
|
{column.render('Header')}
|
||||||
{/* Add a sort direction indicator */}
|
{/* Add a sort direction indicator */}
|
||||||
<span>
|
{column.isSorted
|
||||||
{column.isSorted ? (column.isSortedDesc ? ' 🔽' : ' 🔼') : ''}
|
? (column.isSortedDesc ? ' 🔽' : ' 🔼') + column.sortedIndex
|
||||||
</span>
|
: ''}
|
||||||
</th>
|
</th>
|
||||||
))}
|
))}
|
||||||
</tr>
|
</tr>
|
||||||
@ -130,18 +130,42 @@ function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test('renders a sortable table', () => {
|
test('renders a sortable table', () => {
|
||||||
const { getByText, asFragment } = render(<App />)
|
const rendered = render(<App />)
|
||||||
|
|
||||||
const beforeSort = asFragment()
|
fireEvent.click(rendered.getByText('First Name'))
|
||||||
|
rendered.getByText('First Name 🔼0')
|
||||||
|
expect(
|
||||||
|
rendered
|
||||||
|
.queryAllByRole('row')
|
||||||
|
.slice(2)
|
||||||
|
.map(d => d.children[0].textContent)
|
||||||
|
).toEqual(['firstName: derek', 'firstName: joe', 'firstName: tanner'])
|
||||||
|
|
||||||
fireEvent.click(getByText('First Name'))
|
fireEvent.click(rendered.getByText('First Name 🔼0'))
|
||||||
|
rendered.getByText('First Name 🔽0')
|
||||||
|
expect(
|
||||||
|
rendered
|
||||||
|
.queryAllByRole('row')
|
||||||
|
.slice(2)
|
||||||
|
.map(d => d.children[0].textContent)
|
||||||
|
).toEqual(['firstName: tanner', 'firstName: joe', 'firstName: derek'])
|
||||||
|
|
||||||
const afterSort1 = asFragment()
|
fireEvent.click(rendered.getByText('Profile Progress'))
|
||||||
|
rendered.getByText('Profile Progress 🔼0')
|
||||||
|
expect(
|
||||||
|
rendered
|
||||||
|
.queryAllByRole('row')
|
||||||
|
.slice(2)
|
||||||
|
.map(d => d.children[0].textContent)
|
||||||
|
).toEqual(['firstName: joe', 'firstName: tanner', 'firstName: derek'])
|
||||||
|
|
||||||
fireEvent.click(getByText('First Name'))
|
fireEvent.click(rendered.getByText('First Name'), { shiftKey: true })
|
||||||
|
rendered.getByText('Profile Progress 🔼0')
|
||||||
const afterSort2 = asFragment()
|
rendered.getByText('First Name 🔼1')
|
||||||
|
expect(
|
||||||
expect(beforeSort).toMatchDiffSnapshot(afterSort1)
|
rendered
|
||||||
expect(afterSort1).toMatchDiffSnapshot(afterSort2)
|
.queryAllByRole('row')
|
||||||
|
.slice(2)
|
||||||
|
.map(d => d.children[0].textContent)
|
||||||
|
).toEqual(['firstName: joe', 'firstName: derek', 'firstName: tanner'])
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { ensurePluginOrder } from '../utils'
|
import { ensurePluginOrder } from '../publicUtils'
|
||||||
|
|
||||||
const cellStyles = {
|
const cellStyles = {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { functionalUpdate, actions } from '../utils'
|
import { functionalUpdate, actions } from '../publicUtils'
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
actions.resetColumnOrder = 'resetColumnOrder'
|
actions.resetColumnOrder = 'resetColumnOrder'
|
||||||
@ -8,10 +8,10 @@ actions.setColumnOrder = 'setColumnOrder'
|
|||||||
|
|
||||||
export const useColumnOrder = hooks => {
|
export const useColumnOrder = hooks => {
|
||||||
hooks.stateReducers.push(reducer)
|
hooks.stateReducers.push(reducer)
|
||||||
hooks.flatColumnsDeps.push((deps, { instance }) => {
|
hooks.visibleColumnsDeps.push((deps, { instance }) => {
|
||||||
return [...deps, instance.state.columnOrder]
|
return [...deps, instance.state.columnOrder]
|
||||||
})
|
})
|
||||||
hooks.flatColumns.push(flatColumns)
|
hooks.visibleColumns.push(visibleColumns)
|
||||||
hooks.useInstance.push(useInstance)
|
hooks.useInstance.push(useInstance)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ function reducer(state, action, previousState, instance) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function flatColumns(
|
function visibleColumns(
|
||||||
columns,
|
columns,
|
||||||
{
|
{
|
||||||
instance: {
|
instance: {
|
||||||
|
|||||||
@ -1,13 +1,14 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
import { expandRows } from '../utils'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
actions,
|
|
||||||
makePropGetter,
|
|
||||||
expandRows,
|
|
||||||
useMountedLayoutEffect,
|
|
||||||
useGetLatest,
|
useGetLatest,
|
||||||
} from '../utils'
|
actions,
|
||||||
import { useConsumeHookGetter, functionalUpdate } from '../publicUtils'
|
functionalUpdate,
|
||||||
|
useMountedLayoutEffect,
|
||||||
|
makePropGetter,
|
||||||
|
} from '../publicUtils'
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
actions.toggleExpanded = 'toggleExpanded'
|
actions.toggleExpanded = 'toggleExpanded'
|
||||||
@ -19,6 +20,7 @@ export const useExpanded = hooks => {
|
|||||||
hooks.getExpandedToggleProps = [defaultGetExpandedToggleProps]
|
hooks.getExpandedToggleProps = [defaultGetExpandedToggleProps]
|
||||||
hooks.stateReducers.push(reducer)
|
hooks.stateReducers.push(reducer)
|
||||||
hooks.useInstance.push(useInstance)
|
hooks.useInstance.push(useInstance)
|
||||||
|
hooks.prepareRow.push(prepareRow)
|
||||||
}
|
}
|
||||||
|
|
||||||
useExpanded.pluginName = 'useExpanded'
|
useExpanded.pluginName = 'useExpanded'
|
||||||
@ -61,7 +63,7 @@ function reducer(state, action, previousState, instance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (action.type === actions.toggleExpanded) {
|
if (action.type === actions.toggleExpanded) {
|
||||||
const { id, expanded: setExpanded } = action
|
const { id, value: setExpanded } = action
|
||||||
const exists = state.expanded[id]
|
const exists = state.expanded[id]
|
||||||
|
|
||||||
const shouldExist =
|
const shouldExist =
|
||||||
@ -94,7 +96,6 @@ function useInstance(instance) {
|
|||||||
manualExpandedKey = 'expanded',
|
manualExpandedKey = 'expanded',
|
||||||
paginateExpandedRows = true,
|
paginateExpandedRows = true,
|
||||||
expandSubRows = true,
|
expandSubRows = true,
|
||||||
hooks,
|
|
||||||
autoResetExpanded = true,
|
autoResetExpanded = true,
|
||||||
state: { expanded },
|
state: { expanded },
|
||||||
dispatch,
|
dispatch,
|
||||||
@ -109,27 +110,10 @@ function useInstance(instance) {
|
|||||||
}
|
}
|
||||||
}, [dispatch, data])
|
}, [dispatch, data])
|
||||||
|
|
||||||
const toggleExpanded = (id, expanded) => {
|
const toggleExpanded = (id, value) => {
|
||||||
dispatch({ type: actions.toggleExpanded, id, expanded })
|
dispatch({ type: actions.toggleExpanded, id, value })
|
||||||
}
|
}
|
||||||
|
|
||||||
// use reference to avoid memory leak in #1608
|
|
||||||
const getInstance = useGetLatest(instance)
|
|
||||||
|
|
||||||
const getExpandedTogglePropsHooks = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
|
||||||
'getExpandedToggleProps'
|
|
||||||
)
|
|
||||||
|
|
||||||
hooks.prepareRow.push(row => {
|
|
||||||
row.toggleExpanded = set => instance.toggleExpanded(row.id, set)
|
|
||||||
|
|
||||||
row.getExpandedToggleProps = makePropGetter(getExpandedTogglePropsHooks(), {
|
|
||||||
instance: getInstance(),
|
|
||||||
row,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const expandedRows = React.useMemo(() => {
|
const expandedRows = React.useMemo(() => {
|
||||||
if (paginateExpandedRows) {
|
if (paginateExpandedRows) {
|
||||||
return expandRows(rows, { manualExpandedKey, expanded, expandSubRows })
|
return expandRows(rows, { manualExpandedKey, expanded, expandSubRows })
|
||||||
@ -151,6 +135,18 @@ function useInstance(instance) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function prepareRow(row, { instance: { getHooks }, instance }) {
|
||||||
|
row.toggleExpanded = set => instance.toggleExpanded(row.id, set)
|
||||||
|
|
||||||
|
row.getExpandedToggleProps = makePropGetter(
|
||||||
|
getHooks().getExpandedToggleProps,
|
||||||
|
{
|
||||||
|
instance,
|
||||||
|
row,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function findExpandedDepth(expanded) {
|
function findExpandedDepth(expanded) {
|
||||||
let maxDepth = 0
|
let maxDepth = 0
|
||||||
|
|
||||||
|
|||||||
@ -1,14 +1,18 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
actions,
|
|
||||||
getFirstDefined,
|
getFirstDefined,
|
||||||
getFilterMethod,
|
getFilterMethod,
|
||||||
useMountedLayoutEffect,
|
|
||||||
functionalUpdate,
|
|
||||||
useGetLatest,
|
|
||||||
shouldAutoRemoveFilter,
|
shouldAutoRemoveFilter,
|
||||||
} from '../utils'
|
} from '../utils'
|
||||||
|
|
||||||
|
import {
|
||||||
|
actions,
|
||||||
|
useGetLatest,
|
||||||
|
functionalUpdate,
|
||||||
|
useMountedLayoutEffect,
|
||||||
|
} from '../publicUtils'
|
||||||
|
|
||||||
import * as filterTypes from '../filterTypes'
|
import * as filterTypes from '../filterTypes'
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
@ -40,9 +44,9 @@ function reducer(state, action, previousState, instance) {
|
|||||||
|
|
||||||
if (action.type === actions.setFilter) {
|
if (action.type === actions.setFilter) {
|
||||||
const { columnId, filterValue } = action
|
const { columnId, filterValue } = action
|
||||||
const { flatColumns, userFilterTypes } = instance
|
const { allColumns, userFilterTypes } = instance
|
||||||
|
|
||||||
const column = flatColumns.find(d => d.id === columnId)
|
const column = allColumns.find(d => d.id === columnId)
|
||||||
|
|
||||||
if (!column) {
|
if (!column) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -91,13 +95,13 @@ function reducer(state, action, previousState, instance) {
|
|||||||
|
|
||||||
if (action.type === actions.setAllFilters) {
|
if (action.type === actions.setAllFilters) {
|
||||||
const { filters } = action
|
const { filters } = action
|
||||||
const { flatColumns, filterTypes: userFilterTypes } = instance
|
const { allColumns, filterTypes: userFilterTypes } = instance
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
// Filter out undefined values
|
// Filter out undefined values
|
||||||
filters: functionalUpdate(filters, state.filters).filter(filter => {
|
filters: functionalUpdate(filters, state.filters).filter(filter => {
|
||||||
const column = flatColumns.find(d => d.id === filter.id)
|
const column = allColumns.find(d => d.id === filter.id)
|
||||||
const filterMethod = getFilterMethod(
|
const filterMethod = getFilterMethod(
|
||||||
column.filter,
|
column.filter,
|
||||||
userFilterTypes || {},
|
userFilterTypes || {},
|
||||||
@ -118,7 +122,7 @@ function useInstance(instance) {
|
|||||||
data,
|
data,
|
||||||
rows,
|
rows,
|
||||||
flatRows,
|
flatRows,
|
||||||
flatColumns,
|
allColumns,
|
||||||
filterTypes: userFilterTypes,
|
filterTypes: userFilterTypes,
|
||||||
manualFilters,
|
manualFilters,
|
||||||
defaultCanFilter = false,
|
defaultCanFilter = false,
|
||||||
@ -139,7 +143,7 @@ function useInstance(instance) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
flatColumns.forEach(column => {
|
allColumns.forEach(column => {
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
accessor,
|
accessor,
|
||||||
@ -179,7 +183,7 @@ function useInstance(instance) {
|
|||||||
filteredRows = filters.reduce(
|
filteredRows = filters.reduce(
|
||||||
(filteredSoFar, { id: columnId, value: filterValue }) => {
|
(filteredSoFar, { id: columnId, value: filterValue }) => {
|
||||||
// Find the filters column
|
// Find the filters column
|
||||||
const column = flatColumns.find(d => d.id === columnId)
|
const column = allColumns.find(d => d.id === columnId)
|
||||||
|
|
||||||
if (!column) {
|
if (!column) {
|
||||||
return filteredSoFar
|
return filteredSoFar
|
||||||
@ -237,12 +241,12 @@ function useInstance(instance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return [filterRows(rows), filteredFlatRows]
|
return [filterRows(rows), filteredFlatRows]
|
||||||
}, [manualFilters, filters, rows, flatRows, flatColumns, userFilterTypes])
|
}, [manualFilters, filters, rows, flatRows, allColumns, userFilterTypes])
|
||||||
|
|
||||||
React.useMemo(() => {
|
React.useMemo(() => {
|
||||||
// Now that each filtered column has it's partially filtered rows,
|
// Now that each filtered column has it's partially filtered rows,
|
||||||
// lets assign the final filtered rows to all of the other columns
|
// lets assign the final filtered rows to all of the other columns
|
||||||
const nonFilteredColumns = flatColumns.filter(
|
const nonFilteredColumns = allColumns.filter(
|
||||||
column => !filters.find(d => d.id === column.id)
|
column => !filters.find(d => d.id === column.id)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -252,7 +256,7 @@ function useInstance(instance) {
|
|||||||
column.preFilteredRows = filteredRows
|
column.preFilteredRows = filteredRows
|
||||||
column.filteredRows = filteredRows
|
column.filteredRows = filteredRows
|
||||||
})
|
})
|
||||||
}, [filteredRows, filters, flatColumns])
|
}, [filteredRows, filters, allColumns])
|
||||||
|
|
||||||
const getAutoResetFilters = useGetLatest(autoResetFilters)
|
const getAutoResetFilters = useGetLatest(autoResetFilters)
|
||||||
|
|
||||||
|
|||||||
@ -1,14 +1,15 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
|
import { getFilterMethod, shouldAutoRemoveFilter } from '../utils'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
actions,
|
actions,
|
||||||
getFilterMethod,
|
|
||||||
useMountedLayoutEffect,
|
useMountedLayoutEffect,
|
||||||
functionalUpdate,
|
functionalUpdate,
|
||||||
useGetLatest,
|
|
||||||
shouldAutoRemoveFilter,
|
|
||||||
ensurePluginOrder,
|
ensurePluginOrder,
|
||||||
} from '../utils'
|
useGetLatest,
|
||||||
|
} from '../publicUtils'
|
||||||
|
|
||||||
import * as filterTypes from '../filterTypes'
|
import * as filterTypes from '../filterTypes'
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
@ -60,7 +61,7 @@ function useInstance(instance) {
|
|||||||
data,
|
data,
|
||||||
rows,
|
rows,
|
||||||
flatRows,
|
flatRows,
|
||||||
flatColumns,
|
allColumns,
|
||||||
filterTypes: userFilterTypes,
|
filterTypes: userFilterTypes,
|
||||||
globalFilter,
|
globalFilter,
|
||||||
manualGlobalFilter,
|
manualGlobalFilter,
|
||||||
@ -106,7 +107,7 @@ function useInstance(instance) {
|
|||||||
const filterRows = filteredRows => {
|
const filterRows = filteredRows => {
|
||||||
return filterMethod(
|
return filterMethod(
|
||||||
filteredRows,
|
filteredRows,
|
||||||
flatColumns.map(d => d.id),
|
allColumns.map(d => d.id),
|
||||||
globalFilterValue
|
globalFilterValue
|
||||||
).map(row => {
|
).map(row => {
|
||||||
filteredFlatRows.push(row)
|
filteredFlatRows.push(row)
|
||||||
@ -128,7 +129,7 @@ function useInstance(instance) {
|
|||||||
userFilterTypes,
|
userFilterTypes,
|
||||||
rows,
|
rows,
|
||||||
flatRows,
|
flatRows,
|
||||||
flatColumns,
|
allColumns,
|
||||||
globalFilterValue,
|
globalFilterValue,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|||||||
@ -1,16 +1,17 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import * as aggregations from '../aggregations'
|
import * as aggregations from '../aggregations'
|
||||||
|
|
||||||
|
import { getFirstDefined, flattenBy } from '../utils'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
actions,
|
actions,
|
||||||
makePropGetter,
|
makePropGetter,
|
||||||
defaultGroupByFn,
|
defaultGroupByFn,
|
||||||
getFirstDefined,
|
|
||||||
ensurePluginOrder,
|
ensurePluginOrder,
|
||||||
useMountedLayoutEffect,
|
useMountedLayoutEffect,
|
||||||
useGetLatest,
|
useGetLatest,
|
||||||
} from '../utils'
|
} from '../publicUtils'
|
||||||
import { useConsumeHookGetter } from '../publicUtils'
|
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
actions.resetGroupBy = 'resetGroupBy'
|
actions.resetGroupBy = 'resetGroupBy'
|
||||||
@ -19,12 +20,13 @@ actions.toggleGroupBy = 'toggleGroupBy'
|
|||||||
export const useGroupBy = hooks => {
|
export const useGroupBy = hooks => {
|
||||||
hooks.getGroupByToggleProps = [defaultGetGroupByToggleProps]
|
hooks.getGroupByToggleProps = [defaultGetGroupByToggleProps]
|
||||||
hooks.stateReducers.push(reducer)
|
hooks.stateReducers.push(reducer)
|
||||||
hooks.flatColumnsDeps.push((deps, { instance }) => [
|
hooks.visibleColumnsDeps.push((deps, { instance }) => [
|
||||||
...deps,
|
...deps,
|
||||||
instance.state.groupBy,
|
instance.state.groupBy,
|
||||||
])
|
])
|
||||||
hooks.flatColumns.push(flatColumns)
|
hooks.visibleColumns.push(visibleColumns)
|
||||||
hooks.useInstance.push(useInstance)
|
hooks.useInstance.push(useInstance)
|
||||||
|
hooks.prepareRow.push(prepareRow)
|
||||||
}
|
}
|
||||||
|
|
||||||
useGroupBy.pluginName = 'useGroupBy'
|
useGroupBy.pluginName = 'useGroupBy'
|
||||||
@ -62,12 +64,14 @@ function reducer(state, action, previousState, instance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (action.type === actions.toggleGroupBy) {
|
if (action.type === actions.toggleGroupBy) {
|
||||||
const { columnId, toggle } = action
|
const { columnId, value: setGroupBy } = action
|
||||||
|
|
||||||
const resolvedToggle =
|
const resolvedGroupBy =
|
||||||
typeof toggle !== 'undefined' ? toggle : !state.groupBy.includes(columnId)
|
typeof setGroupBy !== 'undefined'
|
||||||
|
? setGroupBy
|
||||||
|
: !state.groupBy.includes(columnId)
|
||||||
|
|
||||||
if (resolvedToggle) {
|
if (resolvedGroupBy) {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
groupBy: [...state.groupBy, columnId],
|
groupBy: [...state.groupBy, columnId],
|
||||||
@ -81,8 +85,8 @@ function reducer(state, action, previousState, instance) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function flatColumns(
|
function visibleColumns(
|
||||||
flatColumns,
|
columns,
|
||||||
{
|
{
|
||||||
instance: {
|
instance: {
|
||||||
state: { groupBy },
|
state: { groupBy },
|
||||||
@ -93,18 +97,19 @@ function flatColumns(
|
|||||||
// before the headers are built
|
// before the headers are built
|
||||||
|
|
||||||
const groupByColumns = groupBy
|
const groupByColumns = groupBy
|
||||||
.map(g => flatColumns.find(col => col.id === g))
|
.map(g => columns.find(col => col.id === g))
|
||||||
.filter(col => !!col)
|
.filter(Boolean)
|
||||||
const nonGroupByColumns = flatColumns.filter(col => !groupBy.includes(col.id))
|
|
||||||
|
|
||||||
flatColumns = [...groupByColumns, ...nonGroupByColumns]
|
const nonGroupByColumns = columns.filter(col => !groupBy.includes(col.id))
|
||||||
|
|
||||||
flatColumns.forEach(column => {
|
columns = [...groupByColumns, ...nonGroupByColumns]
|
||||||
|
|
||||||
|
columns.forEach(column => {
|
||||||
column.isGrouped = groupBy.includes(column.id)
|
column.isGrouped = groupBy.includes(column.id)
|
||||||
column.groupedIndex = groupBy.indexOf(column.id)
|
column.groupedIndex = groupBy.indexOf(column.id)
|
||||||
})
|
})
|
||||||
|
|
||||||
return flatColumns
|
return columns
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultUserAggregations = {}
|
const defaultUserAggregations = {}
|
||||||
@ -114,12 +119,11 @@ function useInstance(instance) {
|
|||||||
data,
|
data,
|
||||||
rows,
|
rows,
|
||||||
flatRows,
|
flatRows,
|
||||||
flatColumns,
|
allColumns,
|
||||||
flatHeaders,
|
flatHeaders,
|
||||||
groupByFn = defaultGroupByFn,
|
groupByFn = defaultGroupByFn,
|
||||||
manualGroupBy,
|
manualGroupBy,
|
||||||
aggregations: userAggregations = defaultUserAggregations,
|
aggregations: userAggregations = defaultUserAggregations,
|
||||||
hooks,
|
|
||||||
plugins,
|
plugins,
|
||||||
state: { groupBy },
|
state: { groupBy },
|
||||||
dispatch,
|
dispatch,
|
||||||
@ -127,13 +131,14 @@ function useInstance(instance) {
|
|||||||
manaulGroupBy,
|
manaulGroupBy,
|
||||||
disableGroupBy,
|
disableGroupBy,
|
||||||
defaultCanGroupBy,
|
defaultCanGroupBy,
|
||||||
|
getHooks,
|
||||||
} = instance
|
} = instance
|
||||||
|
|
||||||
ensurePluginOrder(plugins, [], 'useGroupBy', ['useSortBy', 'useExpanded'])
|
ensurePluginOrder(plugins, [], 'useGroupBy', ['useSortBy', 'useExpanded'])
|
||||||
|
|
||||||
const getInstance = useGetLatest(instance)
|
const getInstance = useGetLatest(instance)
|
||||||
|
|
||||||
flatColumns.forEach(column => {
|
allColumns.forEach(column => {
|
||||||
const {
|
const {
|
||||||
accessor,
|
accessor,
|
||||||
defaultGroupBy: defaultColumnGroupBy,
|
defaultGroupBy: defaultColumnGroupBy,
|
||||||
@ -142,11 +147,17 @@ function useInstance(instance) {
|
|||||||
|
|
||||||
column.canGroupBy = accessor
|
column.canGroupBy = accessor
|
||||||
? getFirstDefined(
|
? getFirstDefined(
|
||||||
|
column.canGroupBy,
|
||||||
columnDisableGroupBy === true ? false : undefined,
|
columnDisableGroupBy === true ? false : undefined,
|
||||||
disableGroupBy === true ? false : undefined,
|
disableGroupBy === true ? false : undefined,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
: getFirstDefined(defaultColumnGroupBy, defaultCanGroupBy, false)
|
: getFirstDefined(
|
||||||
|
column.canGroupBy,
|
||||||
|
defaultColumnGroupBy,
|
||||||
|
defaultCanGroupBy,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
if (column.canGroupBy) {
|
if (column.canGroupBy) {
|
||||||
column.toggleGroupBy = () => instance.toggleGroupBy(column.id)
|
column.toggleGroupBy = () => instance.toggleGroupBy(column.id)
|
||||||
@ -155,34 +166,17 @@ function useInstance(instance) {
|
|||||||
column.Aggregated = column.Aggregated || column.Cell
|
column.Aggregated = column.Aggregated || column.Cell
|
||||||
})
|
})
|
||||||
|
|
||||||
const toggleGroupBy = (columnId, toggle) => {
|
const toggleGroupBy = (columnId, value) => {
|
||||||
dispatch({ type: actions.toggleGroupBy, columnId, toggle })
|
dispatch({ type: actions.toggleGroupBy, columnId, value })
|
||||||
}
|
}
|
||||||
|
|
||||||
const getGroupByTogglePropsHooks = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
|
||||||
'getGroupByToggleProps'
|
|
||||||
)
|
|
||||||
|
|
||||||
flatHeaders.forEach(header => {
|
flatHeaders.forEach(header => {
|
||||||
header.getGroupByToggleProps = makePropGetter(
|
header.getGroupByToggleProps = makePropGetter(
|
||||||
getGroupByTogglePropsHooks(),
|
getHooks().getGroupByToggleProps,
|
||||||
{ instance: getInstance(), header }
|
{ instance: getInstance(), header }
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
hooks.prepareRow.push(row => {
|
|
||||||
row.allCells.forEach(cell => {
|
|
||||||
// Grouped cells are in the groupBy and the pivot cell for the row
|
|
||||||
cell.isGrouped = cell.column.isGrouped && cell.column.id === row.groupByID
|
|
||||||
// Repeated cells are any columns in the groupBy that are not grouped
|
|
||||||
cell.isRepeatedValue = !cell.isGrouped && cell.column.isGrouped
|
|
||||||
// Aggregated cells are not grouped, not repeated, but still have subRows
|
|
||||||
cell.isAggregated =
|
|
||||||
!cell.isGrouped && !cell.isRepeatedValue && row.canExpand
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
const [groupedRows, groupedFlatRows] = React.useMemo(() => {
|
const [groupedRows, groupedFlatRows] = React.useMemo(() => {
|
||||||
if (manualGroupBy || !groupBy.length) {
|
if (manualGroupBy || !groupBy.length) {
|
||||||
return [rows, flatRows]
|
return [rows, flatRows]
|
||||||
@ -190,62 +184,75 @@ function useInstance(instance) {
|
|||||||
|
|
||||||
// Ensure that the list of filtered columns exist
|
// Ensure that the list of filtered columns exist
|
||||||
const existingGroupBy = groupBy.filter(g =>
|
const existingGroupBy = groupBy.filter(g =>
|
||||||
flatColumns.find(col => col.id === g)
|
allColumns.find(col => col.id === g)
|
||||||
)
|
)
|
||||||
|
|
||||||
// Find the columns that can or are aggregating
|
// Find the columns that can or are aggregating
|
||||||
// Uses each column to aggregate rows into a single value
|
// Uses each column to aggregate rows into a single value
|
||||||
const aggregateRowsToValues = (rows, isAggregated) => {
|
const aggregateRowsToValues = (leafRows, groupedRows, depth) => {
|
||||||
const values = {}
|
const values = {}
|
||||||
|
|
||||||
flatColumns.forEach(column => {
|
allColumns.forEach(column => {
|
||||||
// Don't aggregate columns that are in the groupBy
|
// Don't aggregate columns that are in the groupBy
|
||||||
if (existingGroupBy.includes(column.id)) {
|
if (existingGroupBy.includes(column.id)) {
|
||||||
values[column.id] = rows[0] ? rows[0].values[column.id] : null
|
values[column.id] = groupedRows[0]
|
||||||
|
? groupedRows[0].values[column.id]
|
||||||
|
: null
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const columnValues = rows.map(d => d.values[column.id])
|
// Get the columnValues to aggregate
|
||||||
|
const groupedValues = groupedRows.map(row => row.values[column.id])
|
||||||
|
|
||||||
let aggregator = column.aggregate
|
// Get the columnValues to aggregate
|
||||||
|
const leafValues = leafRows.map(row => {
|
||||||
|
let columnValue = row.values[column.id]
|
||||||
|
|
||||||
if (Array.isArray(aggregator)) {
|
if (!depth && column.aggregatedValue) {
|
||||||
if (aggregator.length !== 2) {
|
const aggregateValueFn =
|
||||||
console.info({ column })
|
typeof column.aggregateValue === 'function'
|
||||||
throw new Error(
|
? column.aggregateValue
|
||||||
`React Table: Complex aggregators must have 2 values, eg. aggregate: ['sum', 'count']. More info above...`
|
: userAggregations[column.aggregateValue] ||
|
||||||
)
|
aggregations[column.aggregateValue]
|
||||||
|
|
||||||
|
if (!aggregateValueFn) {
|
||||||
|
console.info({ column })
|
||||||
|
throw new Error(
|
||||||
|
`React Table: Invalid column.aggregateValue option for column listed above`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
columnValue = aggregateValueFn(columnValue, row, column)
|
||||||
}
|
}
|
||||||
if (isAggregated) {
|
return columnValue
|
||||||
aggregator = aggregator[1]
|
})
|
||||||
} else {
|
|
||||||
aggregator = aggregator[0]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Aggregate the values
|
||||||
let aggregateFn =
|
let aggregateFn =
|
||||||
typeof aggregator === 'function'
|
typeof column.aggregate === 'function'
|
||||||
? aggregator
|
? column.aggregate
|
||||||
: userAggregations[aggregator] || aggregations[aggregator]
|
: userAggregations[column.aggregate] ||
|
||||||
|
aggregations[column.aggregate]
|
||||||
|
|
||||||
if (aggregateFn) {
|
if (aggregateFn) {
|
||||||
values[column.id] = aggregateFn(columnValues, rows, isAggregated)
|
values[column.id] = aggregateFn(leafValues, groupedValues)
|
||||||
} else if (aggregator) {
|
} else if (column.aggregate) {
|
||||||
console.info({ column })
|
console.info({ column })
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`React Table: Invalid aggregate option for column listed above`
|
`React Table: Invalid column.aggregate option for column listed above`
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
values[column.id] = null
|
values[column.id] = null
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return values
|
return values
|
||||||
}
|
}
|
||||||
|
|
||||||
let groupedFlatRows = []
|
let groupedFlatRows = []
|
||||||
|
|
||||||
// Recursively group the data
|
// Recursively group the data
|
||||||
const groupRecursively = (rows, depth = 0, parentId) => {
|
const groupUpRecursively = (rows, depth = 0, parentId) => {
|
||||||
// This is the last level, just return the rows
|
// This is the last level, just return the rows
|
||||||
if (depth === existingGroupBy.length) {
|
if (depth === existingGroupBy.length) {
|
||||||
return rows
|
return rows
|
||||||
@ -254,20 +261,23 @@ function useInstance(instance) {
|
|||||||
const columnId = existingGroupBy[depth]
|
const columnId = existingGroupBy[depth]
|
||||||
|
|
||||||
// Group the rows together for this level
|
// Group the rows together for this level
|
||||||
let groupedRows = groupByFn(rows, columnId)
|
let rowGroupsMap = groupByFn(rows, columnId)
|
||||||
|
|
||||||
// Recurse to sub rows before aggregation
|
// Peform aggregations for each group
|
||||||
groupedRows = Object.entries(groupedRows).map(
|
const aggregatedGroupedRows = Object.entries(rowGroupsMap).map(
|
||||||
([groupByVal, subRows], index) => {
|
([groupByVal, groupedRows], index) => {
|
||||||
let id = `${columnId}:${groupByVal}`
|
let id = `${columnId}:${groupByVal}`
|
||||||
id = parentId ? `${parentId}>${id}` : id
|
id = parentId ? `${parentId}>${id}` : id
|
||||||
|
|
||||||
subRows = groupRecursively(subRows, depth + 1, id)
|
// First, Recurse to group sub rows before aggregation
|
||||||
|
const subRows = groupUpRecursively(groupedRows, depth + 1, id)
|
||||||
|
|
||||||
const values = aggregateRowsToValues(
|
// Flatten the leaf rows of the rows in this group
|
||||||
subRows,
|
const leafRows = depth
|
||||||
depth < existingGroupBy.length
|
? flattenBy(groupedRows, 'leafRows')
|
||||||
)
|
: groupedRows
|
||||||
|
|
||||||
|
const values = aggregateRowsToValues(leafRows, groupedRows, depth)
|
||||||
|
|
||||||
const row = {
|
const row = {
|
||||||
id,
|
id,
|
||||||
@ -276,6 +286,7 @@ function useInstance(instance) {
|
|||||||
groupByVal,
|
groupByVal,
|
||||||
values,
|
values,
|
||||||
subRows,
|
subRows,
|
||||||
|
leafRows,
|
||||||
depth,
|
depth,
|
||||||
index,
|
index,
|
||||||
}
|
}
|
||||||
@ -286,10 +297,10 @@ function useInstance(instance) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return groupedRows
|
return aggregatedGroupedRows
|
||||||
}
|
}
|
||||||
|
|
||||||
const groupedRows = groupRecursively(rows)
|
const groupedRows = groupUpRecursively(rows)
|
||||||
|
|
||||||
// Assign the new data
|
// Assign the new data
|
||||||
return [groupedRows, groupedFlatRows]
|
return [groupedRows, groupedFlatRows]
|
||||||
@ -298,7 +309,7 @@ function useInstance(instance) {
|
|||||||
groupBy,
|
groupBy,
|
||||||
rows,
|
rows,
|
||||||
flatRows,
|
flatRows,
|
||||||
flatColumns,
|
allColumns,
|
||||||
userAggregations,
|
userAggregations,
|
||||||
groupByFn,
|
groupByFn,
|
||||||
])
|
])
|
||||||
@ -321,3 +332,14 @@ function useInstance(instance) {
|
|||||||
toggleGroupBy,
|
toggleGroupBy,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function prepareRow(row) {
|
||||||
|
row.allCells.forEach(cell => {
|
||||||
|
// Grouped cells are in the groupBy and the pivot cell for the row
|
||||||
|
cell.isGrouped = cell.column.isGrouped && cell.column.id === row.groupByID
|
||||||
|
// Placeholder cells are any columns in the groupBy that are not grouped
|
||||||
|
cell.isPlaceholder = !cell.isGrouped && cell.column.isGrouped
|
||||||
|
// Aggregated cells are not grouped, not repeated, but still have subRows
|
||||||
|
cell.isAggregated = !cell.isGrouped && !cell.isPlaceholder && row.canExpand
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@ -5,11 +5,12 @@ import React from 'react'
|
|||||||
import {
|
import {
|
||||||
actions,
|
actions,
|
||||||
ensurePluginOrder,
|
ensurePluginOrder,
|
||||||
expandRows,
|
|
||||||
functionalUpdate,
|
functionalUpdate,
|
||||||
useMountedLayoutEffect,
|
useMountedLayoutEffect,
|
||||||
useGetLatest,
|
useGetLatest,
|
||||||
} from '../utils'
|
} from '../publicUtils'
|
||||||
|
|
||||||
|
import { expandRows } from '../utils'
|
||||||
|
|
||||||
const pluginName = 'usePagination'
|
const pluginName = 'usePagination'
|
||||||
|
|
||||||
|
|||||||
297
src/plugin-hooks/usePivotColumns.js
Normal file
297
src/plugin-hooks/usePivotColumns.js
Normal file
@ -0,0 +1,297 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
|
import {
|
||||||
|
actions,
|
||||||
|
makePropGetter,
|
||||||
|
ensurePluginOrder,
|
||||||
|
useMountedLayoutEffect,
|
||||||
|
useGetLatest,
|
||||||
|
} from '../publicUtils'
|
||||||
|
|
||||||
|
import { flattenColumns, getFirstDefined } from '../utils'
|
||||||
|
|
||||||
|
// Actions
|
||||||
|
actions.resetPivot = 'resetPivot'
|
||||||
|
actions.togglePivot = 'togglePivot'
|
||||||
|
|
||||||
|
export const usePivotColumns = hooks => {
|
||||||
|
hooks.getPivotToggleProps = [defaultGetPivotToggleProps]
|
||||||
|
hooks.stateReducers.push(reducer)
|
||||||
|
hooks.useInstanceAfterData.push(useInstanceAfterData)
|
||||||
|
hooks.allColumns.push(allColumns)
|
||||||
|
hooks.accessValue.push(accessValue)
|
||||||
|
hooks.materializedColumns.push(materializedColumns)
|
||||||
|
hooks.materializedColumnsDeps.push(materializedColumnsDeps)
|
||||||
|
hooks.visibleColumns.push(visibleColumns)
|
||||||
|
hooks.visibleColumnsDeps.push(visibleColumnsDeps)
|
||||||
|
hooks.useInstance.push(useInstance)
|
||||||
|
hooks.prepareRow.push(prepareRow)
|
||||||
|
}
|
||||||
|
|
||||||
|
usePivotColumns.pluginName = 'usePivotColumns'
|
||||||
|
|
||||||
|
const defaultPivotColumns = []
|
||||||
|
|
||||||
|
const defaultGetPivotToggleProps = (props, { header }) => [
|
||||||
|
props,
|
||||||
|
{
|
||||||
|
onClick: header.canPivot
|
||||||
|
? e => {
|
||||||
|
e.persist()
|
||||||
|
header.togglePivot()
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
style: {
|
||||||
|
cursor: header.canPivot ? 'pointer' : undefined,
|
||||||
|
},
|
||||||
|
title: 'Toggle Pivot',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
// Reducer
|
||||||
|
function reducer(state, action, previousState, instance) {
|
||||||
|
if (action.type === actions.init) {
|
||||||
|
return {
|
||||||
|
pivotColumns: defaultPivotColumns,
|
||||||
|
...state,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.type === actions.resetPivot) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
pivotColumns: instance.initialState.pivotColumns || defaultPivotColumns,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.type === actions.togglePivot) {
|
||||||
|
const { columnId, value: setPivot } = action
|
||||||
|
|
||||||
|
const resolvedPivot =
|
||||||
|
typeof setPivot !== 'undefined'
|
||||||
|
? setPivot
|
||||||
|
: !state.pivotColumns.includes(columnId)
|
||||||
|
|
||||||
|
if (resolvedPivot) {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
pivotColumns: [...state.pivotColumns, columnId],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
pivotColumns: state.pivotColumns.filter(d => d !== columnId),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function useInstanceAfterData(instance) {
|
||||||
|
instance.allColumns.forEach(column => {
|
||||||
|
column.isPivotSource = instance.state.pivotColumns.includes(column.id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function allColumns(columns, { instance }) {
|
||||||
|
columns.forEach(column => {
|
||||||
|
column.isPivotSource = instance.state.pivotColumns.includes(column.id)
|
||||||
|
column.uniqueValues = new Set()
|
||||||
|
})
|
||||||
|
return columns
|
||||||
|
}
|
||||||
|
|
||||||
|
function accessValue(value, { column }) {
|
||||||
|
if (column.uniqueValues && typeof value !== 'undefined') {
|
||||||
|
column.uniqueValues.add(value)
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
||||||
|
function materializedColumns(materialized, { instance }) {
|
||||||
|
const { allColumns, state } = instance
|
||||||
|
|
||||||
|
if (!state.pivotColumns.length || !state.groupBy || !state.groupBy.length) {
|
||||||
|
return materialized
|
||||||
|
}
|
||||||
|
|
||||||
|
const pivotColumns = state.pivotColumns
|
||||||
|
.map(id => allColumns.find(d => d.id === id))
|
||||||
|
.filter(Boolean)
|
||||||
|
|
||||||
|
const sourceColumns = allColumns.filter(
|
||||||
|
d =>
|
||||||
|
!d.isPivotSource &&
|
||||||
|
!state.groupBy.includes(d.id) &&
|
||||||
|
!state.pivotColumns.includes(d.id)
|
||||||
|
)
|
||||||
|
|
||||||
|
const buildPivotColumns = (depth = 0, parent, pivotFilters = []) => {
|
||||||
|
const pivotColumn = pivotColumns[depth]
|
||||||
|
|
||||||
|
if (!pivotColumn) {
|
||||||
|
return sourceColumns.map(sourceColumn => {
|
||||||
|
// TODO: We could offer support here for renesting pivoted
|
||||||
|
// columns inside copies of their header groups. For now,
|
||||||
|
// that seems like it would be (1) overkill on nesting, considering
|
||||||
|
// you already get nesting for every pivot level and (2)
|
||||||
|
// really hard. :)
|
||||||
|
|
||||||
|
return {
|
||||||
|
...sourceColumn,
|
||||||
|
canPivot: false,
|
||||||
|
isPivoted: true,
|
||||||
|
parent,
|
||||||
|
depth: depth,
|
||||||
|
id: `${parent ? `${parent.id}.${sourceColumn.id}` : sourceColumn.id}`,
|
||||||
|
accessor: (originalRow, i, row) => {
|
||||||
|
if (pivotFilters.every(filter => filter(row))) {
|
||||||
|
return row.values[sourceColumn.id]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const uniqueValues = Array.from(pivotColumn.uniqueValues).sort()
|
||||||
|
|
||||||
|
return uniqueValues.map(uniqueValue => {
|
||||||
|
const columnGroup = {
|
||||||
|
...pivotColumn,
|
||||||
|
Header:
|
||||||
|
pivotColumn.PivotHeader || typeof pivotColumn.header === 'string'
|
||||||
|
? `${pivotColumn.Header}: ${uniqueValue}`
|
||||||
|
: uniqueValue,
|
||||||
|
isPivotGroup: true,
|
||||||
|
parent,
|
||||||
|
depth,
|
||||||
|
id: parent
|
||||||
|
? `${parent.id}.${pivotColumn.id}.${uniqueValue}`
|
||||||
|
: `${pivotColumn.id}.${uniqueValue}`,
|
||||||
|
pivotValue: uniqueValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
columnGroup.columns = buildPivotColumns(depth + 1, columnGroup, [
|
||||||
|
...pivotFilters,
|
||||||
|
row => row.values[pivotColumn.id] === uniqueValue,
|
||||||
|
])
|
||||||
|
|
||||||
|
return columnGroup
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const newMaterialized = flattenColumns(buildPivotColumns())
|
||||||
|
|
||||||
|
return [...materialized, ...newMaterialized]
|
||||||
|
}
|
||||||
|
|
||||||
|
function materializedColumnsDeps(
|
||||||
|
deps,
|
||||||
|
{
|
||||||
|
instance: {
|
||||||
|
state: { pivotColumns, groupBy },
|
||||||
|
},
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
return [...deps, pivotColumns, groupBy]
|
||||||
|
}
|
||||||
|
|
||||||
|
function visibleColumns(visibleColumns, { instance: { state } }) {
|
||||||
|
visibleColumns = visibleColumns.filter(d => !d.isPivotSource)
|
||||||
|
|
||||||
|
if (state.pivotColumns.length && state.groupBy && state.groupBy.length) {
|
||||||
|
visibleColumns = visibleColumns.filter(
|
||||||
|
column => column.isGrouped || column.isPivoted
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return visibleColumns
|
||||||
|
}
|
||||||
|
|
||||||
|
function visibleColumnsDeps(deps, { instance }) {
|
||||||
|
return [...deps, instance.state.pivotColumns, instance.state.groupBy]
|
||||||
|
}
|
||||||
|
|
||||||
|
function useInstance(instance) {
|
||||||
|
const {
|
||||||
|
columns,
|
||||||
|
allColumns,
|
||||||
|
flatHeaders,
|
||||||
|
// pivotFn = defaultPivotFn,
|
||||||
|
// manualPivot,
|
||||||
|
getHooks,
|
||||||
|
plugins,
|
||||||
|
dispatch,
|
||||||
|
autoResetPivot = true,
|
||||||
|
manaulPivot,
|
||||||
|
disablePivot,
|
||||||
|
defaultCanPivot,
|
||||||
|
} = instance
|
||||||
|
|
||||||
|
ensurePluginOrder(plugins, ['useGroupBy'], 'usePivotColumns', [
|
||||||
|
'useSortBy',
|
||||||
|
'useExpanded',
|
||||||
|
])
|
||||||
|
|
||||||
|
const getInstance = useGetLatest(instance)
|
||||||
|
|
||||||
|
allColumns.forEach(column => {
|
||||||
|
const {
|
||||||
|
accessor,
|
||||||
|
defaultPivot: defaultColumnPivot,
|
||||||
|
disablePivot: columnDisablePivot,
|
||||||
|
} = column
|
||||||
|
|
||||||
|
column.canPivot = accessor
|
||||||
|
? getFirstDefined(
|
||||||
|
column.canPivot,
|
||||||
|
columnDisablePivot === true ? false : undefined,
|
||||||
|
disablePivot === true ? false : undefined,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
: getFirstDefined(
|
||||||
|
column.canPivot,
|
||||||
|
defaultColumnPivot,
|
||||||
|
defaultCanPivot,
|
||||||
|
false
|
||||||
|
)
|
||||||
|
|
||||||
|
if (column.canPivot) {
|
||||||
|
column.togglePivot = () => instance.togglePivot(column.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
column.Aggregated = column.Aggregated || column.Cell
|
||||||
|
})
|
||||||
|
|
||||||
|
const togglePivot = (columnId, value) => {
|
||||||
|
dispatch({ type: actions.togglePivot, columnId, value })
|
||||||
|
}
|
||||||
|
|
||||||
|
flatHeaders.forEach(header => {
|
||||||
|
header.getPivotToggleProps = makePropGetter(
|
||||||
|
getHooks().getPivotToggleProps,
|
||||||
|
{
|
||||||
|
instance: getInstance(),
|
||||||
|
header,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const getAutoResetPivot = useGetLatest(autoResetPivot)
|
||||||
|
|
||||||
|
useMountedLayoutEffect(() => {
|
||||||
|
if (getAutoResetPivot()) {
|
||||||
|
dispatch({ type: actions.resetPivot })
|
||||||
|
}
|
||||||
|
}, [dispatch, manaulPivot ? null : columns])
|
||||||
|
|
||||||
|
Object.assign(instance, {
|
||||||
|
togglePivot,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function prepareRow(row) {
|
||||||
|
row.allCells.forEach(cell => {
|
||||||
|
// Grouped cells are in the pivotColumns and the pivot cell for the row
|
||||||
|
cell.isPivoted = cell.column.isPivoted
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -1,11 +1,11 @@
|
|||||||
import {
|
import {
|
||||||
actions,
|
actions,
|
||||||
defaultColumn,
|
defaultColumn,
|
||||||
getFirstDefined,
|
|
||||||
makePropGetter,
|
makePropGetter,
|
||||||
useGetLatest,
|
useGetLatest,
|
||||||
} from '../utils'
|
} from '../publicUtils'
|
||||||
import { useConsumeHookGetter } from '../publicUtils'
|
|
||||||
|
import { getFirstDefined } from '../utils'
|
||||||
|
|
||||||
// Default Column
|
// Default Column
|
||||||
defaultColumn.canResize = true
|
defaultColumn.canResize = true
|
||||||
@ -118,6 +118,7 @@ const defaultGetResizerProps = (props, { instance, header }) => {
|
|||||||
cursor: 'ew-resize',
|
cursor: 'ew-resize',
|
||||||
},
|
},
|
||||||
draggable: false,
|
draggable: false,
|
||||||
|
role: 'separator',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -193,16 +194,12 @@ const useInstanceBeforeDimensions = instance => {
|
|||||||
const {
|
const {
|
||||||
flatHeaders,
|
flatHeaders,
|
||||||
disableResizing,
|
disableResizing,
|
||||||
|
getHooks,
|
||||||
state: { columnResizing },
|
state: { columnResizing },
|
||||||
} = instance
|
} = instance
|
||||||
|
|
||||||
const getInstance = useGetLatest(instance)
|
const getInstance = useGetLatest(instance)
|
||||||
|
|
||||||
const getResizerPropsHooks = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
|
||||||
'getResizerProps'
|
|
||||||
)
|
|
||||||
|
|
||||||
flatHeaders.forEach(header => {
|
flatHeaders.forEach(header => {
|
||||||
const canResize = getFirstDefined(
|
const canResize = getFirstDefined(
|
||||||
header.disableResizing === true ? false : undefined,
|
header.disableResizing === true ? false : undefined,
|
||||||
@ -215,7 +212,7 @@ const useInstanceBeforeDimensions = instance => {
|
|||||||
header.isResizing = columnResizing.isResizingColumn === header.id
|
header.isResizing = columnResizing.isResizingColumn === header.id
|
||||||
|
|
||||||
if (canResize) {
|
if (canResize) {
|
||||||
header.getResizerProps = makePropGetter(getResizerPropsHooks(), {
|
header.getResizerProps = makePropGetter(getHooks().getResizerProps, {
|
||||||
instance: getInstance(),
|
instance: getInstance(),
|
||||||
header,
|
header,
|
||||||
})
|
})
|
||||||
|
|||||||
@ -6,8 +6,7 @@ import {
|
|||||||
ensurePluginOrder,
|
ensurePluginOrder,
|
||||||
useGetLatest,
|
useGetLatest,
|
||||||
useMountedLayoutEffect,
|
useMountedLayoutEffect,
|
||||||
useConsumeHookGetter,
|
} from '../publicUtils'
|
||||||
} from '../utils'
|
|
||||||
|
|
||||||
const pluginName = 'useRowSelect'
|
const pluginName = 'useRowSelect'
|
||||||
|
|
||||||
@ -20,8 +19,8 @@ export const useRowSelect = hooks => {
|
|||||||
hooks.getToggleRowSelectedProps = [defaultGetToggleRowSelectedProps]
|
hooks.getToggleRowSelectedProps = [defaultGetToggleRowSelectedProps]
|
||||||
hooks.getToggleAllRowsSelectedProps = [defaultGetToggleAllRowsSelectedProps]
|
hooks.getToggleAllRowsSelectedProps = [defaultGetToggleAllRowsSelectedProps]
|
||||||
hooks.stateReducers.push(reducer)
|
hooks.stateReducers.push(reducer)
|
||||||
hooks.useRows.push(useRows)
|
|
||||||
hooks.useInstance.push(useInstance)
|
hooks.useInstance.push(useInstance)
|
||||||
|
hooks.prepareRow.push(prepareRow)
|
||||||
}
|
}
|
||||||
|
|
||||||
useRowSelect.pluginName = pluginName
|
useRowSelect.pluginName = pluginName
|
||||||
@ -86,11 +85,11 @@ function reducer(state, action, previousState, instance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (action.type === actions.toggleAllRowsSelected) {
|
if (action.type === actions.toggleAllRowsSelected) {
|
||||||
const { selected } = action
|
const { value: setSelected } = action
|
||||||
const { isAllRowsSelected, flatRowsById } = instance
|
const { isAllRowsSelected, flatRowsById } = instance
|
||||||
|
|
||||||
const selectAll =
|
const selectAll =
|
||||||
typeof selected !== 'undefined' ? selected : !isAllRowsSelected
|
typeof setSelected !== 'undefined' ? setSelected : !isAllRowsSelected
|
||||||
|
|
||||||
if (selectAll) {
|
if (selectAll) {
|
||||||
const selectedRowIds = {}
|
const selectedRowIds = {}
|
||||||
@ -112,7 +111,7 @@ function reducer(state, action, previousState, instance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (action.type === actions.toggleRowSelected) {
|
if (action.type === actions.toggleRowSelected) {
|
||||||
const { id, selected } = action
|
const { id, value: setSelected } = action
|
||||||
const { flatGroupedRowsById } = instance
|
const { flatGroupedRowsById } = instance
|
||||||
|
|
||||||
// Join the ids of deep rows
|
// Join the ids of deep rows
|
||||||
@ -120,7 +119,8 @@ function reducer(state, action, previousState, instance) {
|
|||||||
// in a flat object
|
// in a flat object
|
||||||
const row = flatGroupedRowsById[id]
|
const row = flatGroupedRowsById[id]
|
||||||
const isSelected = row.isSelected
|
const isSelected = row.isSelected
|
||||||
const shouldExist = typeof selected !== 'undefined' ? selected : !isSelected
|
const shouldExist =
|
||||||
|
typeof setSelected !== 'undefined' ? setSelected : !isSelected
|
||||||
|
|
||||||
if (isSelected === shouldExist) {
|
if (isSelected === shouldExist) {
|
||||||
return state
|
return state
|
||||||
@ -153,34 +153,11 @@ function reducer(state, action, previousState, instance) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function useRows(rows, { instance }) {
|
|
||||||
const {
|
|
||||||
state: { selectedRowIds },
|
|
||||||
} = instance
|
|
||||||
|
|
||||||
instance.selectedFlatRows = React.useMemo(() => {
|
|
||||||
const selectedFlatRows = []
|
|
||||||
|
|
||||||
rows.forEach(row => {
|
|
||||||
const isSelected = getRowIsSelected(row, selectedRowIds)
|
|
||||||
row.isSelected = !!isSelected
|
|
||||||
row.isSomeSelected = isSelected === null
|
|
||||||
|
|
||||||
if (isSelected) {
|
|
||||||
selectedFlatRows.push(row)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return selectedFlatRows
|
|
||||||
}, [rows, selectedRowIds])
|
|
||||||
|
|
||||||
return rows
|
|
||||||
}
|
|
||||||
|
|
||||||
function useInstance(instance) {
|
function useInstance(instance) {
|
||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
hooks,
|
rows,
|
||||||
|
getHooks,
|
||||||
plugins,
|
plugins,
|
||||||
flatRows,
|
flatRows,
|
||||||
autoResetSelectedRows = true,
|
autoResetSelectedRows = true,
|
||||||
@ -209,6 +186,22 @@ function useInstance(instance) {
|
|||||||
return [all, grouped]
|
return [all, grouped]
|
||||||
}, [flatRows])
|
}, [flatRows])
|
||||||
|
|
||||||
|
const selectedFlatRows = React.useMemo(() => {
|
||||||
|
const selectedFlatRows = []
|
||||||
|
|
||||||
|
rows.forEach(row => {
|
||||||
|
const isSelected = getRowIsSelected(row, selectedRowIds)
|
||||||
|
row.isSelected = !!isSelected
|
||||||
|
row.isSomeSelected = isSelected === null
|
||||||
|
|
||||||
|
if (isSelected) {
|
||||||
|
selectedFlatRows.push(row)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return selectedFlatRows
|
||||||
|
}, [rows, selectedRowIds])
|
||||||
|
|
||||||
let isAllRowsSelected = Boolean(
|
let isAllRowsSelected = Boolean(
|
||||||
Object.keys(flatRowsById).length && Object.keys(selectedRowIds).length
|
Object.keys(flatRowsById).length && Object.keys(selectedRowIds).length
|
||||||
)
|
)
|
||||||
@ -227,41 +220,23 @@ function useInstance(instance) {
|
|||||||
}
|
}
|
||||||
}, [dispatch, data])
|
}, [dispatch, data])
|
||||||
|
|
||||||
const toggleAllRowsSelected = selected =>
|
const toggleAllRowsSelected = value =>
|
||||||
dispatch({ type: actions.toggleAllRowsSelected, selected })
|
dispatch({ type: actions.toggleAllRowsSelected, value })
|
||||||
|
|
||||||
const toggleRowSelected = (id, selected) =>
|
const toggleRowSelected = (id, value) =>
|
||||||
dispatch({ type: actions.toggleRowSelected, id, selected })
|
dispatch({ type: actions.toggleRowSelected, id, value })
|
||||||
|
|
||||||
const getInstance = useGetLatest(instance)
|
const getInstance = useGetLatest(instance)
|
||||||
|
|
||||||
const getToggleAllRowsSelectedPropsHooks = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
|
||||||
'getToggleAllRowsSelectedProps'
|
|
||||||
)
|
|
||||||
|
|
||||||
const getToggleAllRowsSelectedProps = makePropGetter(
|
const getToggleAllRowsSelectedProps = makePropGetter(
|
||||||
getToggleAllRowsSelectedPropsHooks(),
|
getHooks().getToggleAllRowsSelectedProps,
|
||||||
{ instance: getInstance() }
|
{ instance: getInstance() }
|
||||||
)
|
)
|
||||||
|
|
||||||
const getToggleRowSelectedPropsHooks = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
|
||||||
'getToggleRowSelectedProps'
|
|
||||||
)
|
|
||||||
|
|
||||||
hooks.prepareRow.push(row => {
|
|
||||||
row.toggleRowSelected = set => toggleRowSelected(row.id, set)
|
|
||||||
|
|
||||||
row.getToggleRowSelectedProps = makePropGetter(
|
|
||||||
getToggleRowSelectedPropsHooks(),
|
|
||||||
{ instance: getInstance(), row }
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
Object.assign(instance, {
|
Object.assign(instance, {
|
||||||
flatRowsById,
|
flatRowsById,
|
||||||
flatGroupedRowsById,
|
flatGroupedRowsById,
|
||||||
|
selectedFlatRows,
|
||||||
toggleRowSelected,
|
toggleRowSelected,
|
||||||
toggleAllRowsSelected,
|
toggleAllRowsSelected,
|
||||||
getToggleAllRowsSelectedProps,
|
getToggleAllRowsSelectedProps,
|
||||||
@ -269,6 +244,15 @@ function useInstance(instance) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function prepareRow(row, { instance }) {
|
||||||
|
row.toggleRowSelected = set => instance.toggleRowSelected(row.id, set)
|
||||||
|
|
||||||
|
row.getToggleRowSelectedProps = makePropGetter(
|
||||||
|
instance.getHooks().getToggleRowSelectedProps,
|
||||||
|
{ instance: instance, row }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
function getRowIsSelected(row, selectedRowIds) {
|
function getRowIsSelected(row, selectedRowIds) {
|
||||||
if (selectedRowIds[row.id]) {
|
if (selectedRowIds[row.id]) {
|
||||||
return true
|
return true
|
||||||
|
|||||||
@ -5,20 +5,31 @@ import {
|
|||||||
functionalUpdate,
|
functionalUpdate,
|
||||||
useMountedLayoutEffect,
|
useMountedLayoutEffect,
|
||||||
useGetLatest,
|
useGetLatest,
|
||||||
} from '../utils'
|
} from '../publicUtils'
|
||||||
|
|
||||||
|
const defaultInitialRowStateAccessor = originalRow => ({})
|
||||||
|
const defaultInitialCellStateAccessor = originalRow => ({})
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
actions.setRowState = 'setRowState'
|
actions.setRowState = 'setRowState'
|
||||||
|
actions.setCellState = 'setCellState'
|
||||||
actions.resetRowState = 'resetRowState'
|
actions.resetRowState = 'resetRowState'
|
||||||
|
|
||||||
export const useRowState = hooks => {
|
export const useRowState = hooks => {
|
||||||
hooks.stateReducers.push(reducer)
|
hooks.stateReducers.push(reducer)
|
||||||
hooks.useInstance.push(useInstance)
|
hooks.useInstance.push(useInstance)
|
||||||
|
hooks.prepareRow.push(prepareRow)
|
||||||
}
|
}
|
||||||
|
|
||||||
useRowState.pluginName = 'useRowState'
|
useRowState.pluginName = 'useRowState'
|
||||||
|
|
||||||
function reducer(state, action, previousState, instance) {
|
function reducer(state, action, previousState, instance) {
|
||||||
|
const {
|
||||||
|
initialRowStateAccessor = defaultInitialRowStateAccessor,
|
||||||
|
initialCellStateAccessor = defaultInitialCellStateAccessor,
|
||||||
|
rowsById,
|
||||||
|
} = instance
|
||||||
|
|
||||||
if (action.type === actions.init) {
|
if (action.type === actions.init) {
|
||||||
return {
|
return {
|
||||||
rowState: {},
|
rowState: {},
|
||||||
@ -34,82 +45,75 @@ function reducer(state, action, previousState, instance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (action.type === actions.setRowState) {
|
if (action.type === actions.setRowState) {
|
||||||
const { id, value } = action
|
const { rowId, value } = action
|
||||||
|
|
||||||
|
const oldRowState =
|
||||||
|
typeof state.rowState[rowId] !== 'undefined'
|
||||||
|
? state.rowState[rowId]
|
||||||
|
: initialRowStateAccessor(rowsById[rowId].original)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
rowState: {
|
rowState: {
|
||||||
...state.rowState,
|
...state.rowState,
|
||||||
[id]: functionalUpdate(value, state.rowState[id] || {}),
|
[rowId]: functionalUpdate(value, oldRowState),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.type === actions.setCellState) {
|
||||||
|
const { rowId, columnId, value } = action
|
||||||
|
|
||||||
|
const oldRowState =
|
||||||
|
typeof state.rowState[rowId] !== 'undefined'
|
||||||
|
? state.rowState[rowId]
|
||||||
|
: initialRowStateAccessor(rowsById[rowId].original)
|
||||||
|
|
||||||
|
const oldCellState =
|
||||||
|
typeof oldRowState?.cellState?.[columnId] !== 'undefined'
|
||||||
|
? oldRowState.cellState[columnId]
|
||||||
|
: initialCellStateAccessor(rowsById[rowId].original)
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
rowState: {
|
||||||
|
...state.rowState,
|
||||||
|
[rowId]: {
|
||||||
|
...oldRowState,
|
||||||
|
cellState: {
|
||||||
|
...(oldRowState.cellState || {}),
|
||||||
|
[columnId]: functionalUpdate(value, oldCellState),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function useInstance(instance) {
|
function useInstance(instance) {
|
||||||
const {
|
const { autoResetRowState = true, data, dispatch } = instance
|
||||||
hooks,
|
|
||||||
initialRowStateAccessor,
|
|
||||||
autoResetRowState = true,
|
|
||||||
state: { rowState },
|
|
||||||
data,
|
|
||||||
dispatch,
|
|
||||||
} = instance
|
|
||||||
|
|
||||||
const setRowState = React.useCallback(
|
const setRowState = React.useCallback(
|
||||||
(id, value, columnId) =>
|
(rowId, value) =>
|
||||||
dispatch({
|
dispatch({
|
||||||
type: actions.setRowState,
|
type: actions.setRowState,
|
||||||
id,
|
rowId,
|
||||||
value,
|
value,
|
||||||
columnId,
|
|
||||||
}),
|
}),
|
||||||
[dispatch]
|
[dispatch]
|
||||||
)
|
)
|
||||||
|
|
||||||
const setCellState = React.useCallback(
|
const setCellState = React.useCallback(
|
||||||
(rowPath, columnId, value) => {
|
(rowId, columnId, value) =>
|
||||||
return setRowState(
|
dispatch({
|
||||||
rowPath,
|
type: actions.setCellState,
|
||||||
old => {
|
rowId,
|
||||||
return {
|
columnId,
|
||||||
...old,
|
value,
|
||||||
cellState: {
|
}),
|
||||||
...old.cellState,
|
[dispatch]
|
||||||
[columnId]: functionalUpdate(
|
|
||||||
value,
|
|
||||||
(old.cellState || {})[columnId] || {}
|
|
||||||
),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
columnId
|
|
||||||
)
|
|
||||||
},
|
|
||||||
[setRowState]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
hooks.prepareRow.push(row => {
|
|
||||||
if (row.original) {
|
|
||||||
row.state =
|
|
||||||
(typeof rowState[row.id] !== 'undefined'
|
|
||||||
? rowState[row.id]
|
|
||||||
: initialRowStateAccessor && initialRowStateAccessor(row)) || {}
|
|
||||||
|
|
||||||
row.setState = updater => {
|
|
||||||
return setRowState(row.id, updater)
|
|
||||||
}
|
|
||||||
|
|
||||||
row.cells.forEach(cell => {
|
|
||||||
cell.state = row.state.cellState || {}
|
|
||||||
|
|
||||||
cell.setState = updater => {
|
|
||||||
return setCellState(row.id, cell.column.id, updater)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const getAutoResetRowState = useGetLatest(autoResetRowState)
|
const getAutoResetRowState = useGetLatest(autoResetRowState)
|
||||||
|
|
||||||
useMountedLayoutEffect(() => {
|
useMountedLayoutEffect(() => {
|
||||||
@ -123,3 +127,37 @@ function useInstance(instance) {
|
|||||||
setCellState,
|
setCellState,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function prepareRow(row, { instance }) {
|
||||||
|
const {
|
||||||
|
initialRowStateAccessor = defaultInitialRowStateAccessor,
|
||||||
|
initialCellStateAccessor = defaultInitialCellStateAccessor,
|
||||||
|
state: { rowState },
|
||||||
|
} = instance
|
||||||
|
|
||||||
|
if (row.original) {
|
||||||
|
row.state =
|
||||||
|
typeof rowState[row.id] !== 'undefined'
|
||||||
|
? rowState[row.id]
|
||||||
|
: initialRowStateAccessor(row.original)
|
||||||
|
|
||||||
|
row.setState = updater => {
|
||||||
|
return instance.setRowState(row.id, updater)
|
||||||
|
}
|
||||||
|
|
||||||
|
row.cells.forEach(cell => {
|
||||||
|
if (!row.state.cellState) {
|
||||||
|
row.state.cellState = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
cell.state =
|
||||||
|
typeof row.state.cellState[cell.column.id] !== 'undefined'
|
||||||
|
? row.state.cellState[cell.column.id]
|
||||||
|
: initialCellStateAccessor(row.original)
|
||||||
|
|
||||||
|
cell.setState = updater => {
|
||||||
|
return instance.setCellState(row.id, cell.column.id, updater)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -5,13 +5,12 @@ import {
|
|||||||
ensurePluginOrder,
|
ensurePluginOrder,
|
||||||
defaultColumn,
|
defaultColumn,
|
||||||
makePropGetter,
|
makePropGetter,
|
||||||
useConsumeHookGetter,
|
|
||||||
getFirstDefined,
|
|
||||||
defaultOrderByFn,
|
defaultOrderByFn,
|
||||||
isFunction,
|
|
||||||
useGetLatest,
|
useGetLatest,
|
||||||
useMountedLayoutEffect,
|
useMountedLayoutEffect,
|
||||||
} from '../utils'
|
} from '../publicUtils'
|
||||||
|
|
||||||
|
import { getFirstDefined, isFunction } from '../utils'
|
||||||
|
|
||||||
import * as sortTypes from '../sortTypes'
|
import * as sortTypes from '../sortTypes'
|
||||||
|
|
||||||
@ -84,7 +83,7 @@ function reducer(state, action, previousState, instance) {
|
|||||||
const { columnId, desc, multi } = action
|
const { columnId, desc, multi } = action
|
||||||
|
|
||||||
const {
|
const {
|
||||||
flatColumns,
|
allColumns,
|
||||||
disableMultiSort,
|
disableMultiSort,
|
||||||
disableSortRemove,
|
disableSortRemove,
|
||||||
disableMultiRemove,
|
disableMultiRemove,
|
||||||
@ -94,7 +93,7 @@ function reducer(state, action, previousState, instance) {
|
|||||||
const { sortBy } = state
|
const { sortBy } = state
|
||||||
|
|
||||||
// Find the column for this columnId
|
// Find the column for this columnId
|
||||||
const column = flatColumns.find(d => d.id === columnId)
|
const column = allColumns.find(d => d.id === columnId)
|
||||||
const { sortDescFirst } = column
|
const { sortDescFirst } = column
|
||||||
|
|
||||||
// Find any existing sortBy for this column
|
// Find any existing sortBy for this column
|
||||||
@ -181,7 +180,7 @@ function useInstance(instance) {
|
|||||||
const {
|
const {
|
||||||
data,
|
data,
|
||||||
rows,
|
rows,
|
||||||
flatColumns,
|
allColumns,
|
||||||
orderByFn = defaultOrderByFn,
|
orderByFn = defaultOrderByFn,
|
||||||
sortTypes: userSortTypes,
|
sortTypes: userSortTypes,
|
||||||
manualSortBy,
|
manualSortBy,
|
||||||
@ -191,6 +190,7 @@ function useInstance(instance) {
|
|||||||
state: { sortBy },
|
state: { sortBy },
|
||||||
dispatch,
|
dispatch,
|
||||||
plugins,
|
plugins,
|
||||||
|
getHooks,
|
||||||
autoResetSortBy = true,
|
autoResetSortBy = true,
|
||||||
} = instance
|
} = instance
|
||||||
|
|
||||||
@ -204,11 +204,6 @@ function useInstance(instance) {
|
|||||||
// use reference to avoid memory leak in #1608
|
// use reference to avoid memory leak in #1608
|
||||||
const getInstance = useGetLatest(instance)
|
const getInstance = useGetLatest(instance)
|
||||||
|
|
||||||
const getSortByTogglePropsHooks = useConsumeHookGetter(
|
|
||||||
getInstance().hooks,
|
|
||||||
'getSortByToggleProps'
|
|
||||||
)
|
|
||||||
|
|
||||||
// Add the getSortByToggleProps method to columns and headers
|
// Add the getSortByToggleProps method to columns and headers
|
||||||
flatHeaders.forEach(column => {
|
flatHeaders.forEach(column => {
|
||||||
const {
|
const {
|
||||||
@ -237,10 +232,13 @@ function useInstance(instance) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
column.getSortByToggleProps = makePropGetter(getSortByTogglePropsHooks(), {
|
column.getSortByToggleProps = makePropGetter(
|
||||||
instance: getInstance(),
|
getHooks().getSortByToggleProps,
|
||||||
column,
|
{
|
||||||
})
|
instance: getInstance(),
|
||||||
|
column,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const columnSort = sortBy.find(d => d.id === id)
|
const columnSort = sortBy.find(d => d.id === id)
|
||||||
column.isSorted = !!columnSort
|
column.isSorted = !!columnSort
|
||||||
@ -255,7 +253,7 @@ function useInstance(instance) {
|
|||||||
|
|
||||||
// Filter out sortBys that correspond to non existing columns
|
// Filter out sortBys that correspond to non existing columns
|
||||||
const availableSortBy = sortBy.filter(sort =>
|
const availableSortBy = sortBy.filter(sort =>
|
||||||
flatColumns.find(col => col.id === sort.id)
|
allColumns.find(col => col.id === sort.id)
|
||||||
)
|
)
|
||||||
|
|
||||||
const sortData = rows => {
|
const sortData = rows => {
|
||||||
@ -266,7 +264,7 @@ function useInstance(instance) {
|
|||||||
rows,
|
rows,
|
||||||
availableSortBy.map(sort => {
|
availableSortBy.map(sort => {
|
||||||
// Support custom sorting methods for each column
|
// Support custom sorting methods for each column
|
||||||
const column = flatColumns.find(d => d.id === sort.id)
|
const column = allColumns.find(d => d.id === sort.id)
|
||||||
|
|
||||||
if (!column) {
|
if (!column) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -301,7 +299,7 @@ function useInstance(instance) {
|
|||||||
// Map the directions
|
// Map the directions
|
||||||
availableSortBy.map(sort => {
|
availableSortBy.map(sort => {
|
||||||
// Detect and use the sortInverted option
|
// Detect and use the sortInverted option
|
||||||
const column = flatColumns.find(d => d.id === sort.id)
|
const column = allColumns.find(d => d.id === sort.id)
|
||||||
|
|
||||||
if (column && column.sortInverted) {
|
if (column && column.sortInverted) {
|
||||||
return sort.desc
|
return sort.desc
|
||||||
@ -323,7 +321,7 @@ function useInstance(instance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return sortData(rows)
|
return sortData(rows)
|
||||||
}, [manualSortBy, sortBy, rows, flatColumns, orderByFn, userSortTypes])
|
}, [manualSortBy, sortBy, rows, allColumns, orderByFn, userSortTypes])
|
||||||
|
|
||||||
const getAutoResetSortBy = useGetLatest(autoResetSortBy)
|
const getAutoResetSortBy = useGetLatest(autoResetSortBy)
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
let renderErr = 'Renderer Error'
|
let renderErr = 'Renderer Error ☝️'
|
||||||
|
|
||||||
export const actions = {
|
export const actions = {
|
||||||
init: 'init',
|
init: 'init',
|
||||||
@ -94,11 +94,11 @@ export const makePropGetter = (hooks, meta = {}) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const reduceHooks = (hooks, initial, meta = {}) =>
|
export const reduceHooks = (hooks, initial, meta = {}, allowUndefined) =>
|
||||||
hooks.reduce((prev, next) => {
|
hooks.reduce((prev, next) => {
|
||||||
const nextValue = next(prev, meta)
|
const nextValue = next(prev, meta)
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
if (typeof nextValue === 'undefined') {
|
if (!allowUndefined && typeof nextValue === 'undefined') {
|
||||||
console.info(next)
|
console.info(next)
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'React Table: A reducer hook ☝️ just returned undefined! This is not allowed.'
|
'React Table: A reducer hook ☝️ just returned undefined! This is not allowed.'
|
||||||
@ -108,9 +108,9 @@ export const reduceHooks = (hooks, initial, meta = {}) =>
|
|||||||
return nextValue
|
return nextValue
|
||||||
}, initial)
|
}, initial)
|
||||||
|
|
||||||
export const loopHooks = (hooks, meta = {}) =>
|
export const loopHooks = (hooks, context, meta = {}) =>
|
||||||
hooks.forEach(hook => {
|
hooks.forEach(hook => {
|
||||||
const nextValue = hook(meta)
|
const nextValue = hook(context, meta)
|
||||||
if (process.env.NODE_ENV !== 'production') {
|
if (process.env.NODE_ENV !== 'production') {
|
||||||
if (typeof nextValue !== 'undefined') {
|
if (typeof nextValue !== 'undefined') {
|
||||||
console.info(hook, nextValue)
|
console.info(hook, nextValue)
|
||||||
@ -190,14 +190,12 @@ export function useMountedLayoutEffect(fn, deps) {
|
|||||||
|
|
||||||
export function useAsyncDebounce(defaultFn, defaultWait = 0) {
|
export function useAsyncDebounce(defaultFn, defaultWait = 0) {
|
||||||
const debounceRef = React.useRef({})
|
const debounceRef = React.useRef({})
|
||||||
debounceRef.current.defaultFn = defaultFn
|
|
||||||
debounceRef.current.defaultWait = defaultWait
|
|
||||||
|
|
||||||
const debounce = React.useCallback(
|
const getDefaultFn = useGetLatest(defaultFn)
|
||||||
async (
|
const getDefaultWait = useGetLatest(defaultWait)
|
||||||
fn = debounceRef.current.defaultFn,
|
|
||||||
wait = debounceRef.current.defaultWait
|
return React.useCallback(
|
||||||
) => {
|
async (...args) => {
|
||||||
if (!debounceRef.current.promise) {
|
if (!debounceRef.current.promise) {
|
||||||
debounceRef.current.promise = new Promise((resolve, reject) => {
|
debounceRef.current.promise = new Promise((resolve, reject) => {
|
||||||
debounceRef.current.resolve = resolve
|
debounceRef.current.resolve = resolve
|
||||||
@ -212,26 +210,18 @@ export function useAsyncDebounce(defaultFn, defaultWait = 0) {
|
|||||||
debounceRef.current.timeout = setTimeout(async () => {
|
debounceRef.current.timeout = setTimeout(async () => {
|
||||||
delete debounceRef.current.timeout
|
delete debounceRef.current.timeout
|
||||||
try {
|
try {
|
||||||
debounceRef.current.resolve(await fn())
|
debounceRef.current.resolve(await getDefaultFn()(...args))
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
debounceRef.current.reject(err)
|
debounceRef.current.reject(err)
|
||||||
} finally {
|
} finally {
|
||||||
delete debounceRef.current.promise
|
delete debounceRef.current.promise
|
||||||
}
|
}
|
||||||
}, wait)
|
}, getDefaultWait())
|
||||||
|
|
||||||
return debounceRef.current.promise
|
return debounceRef.current.promise
|
||||||
},
|
},
|
||||||
[]
|
[getDefaultFn, getDefaultWait]
|
||||||
)
|
)
|
||||||
|
|
||||||
return debounce
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useConsumeHookGetter(hooks, hookName) {
|
|
||||||
const getter = useGetLatest(hooks[hookName])
|
|
||||||
hooks[hookName] = undefined
|
|
||||||
return getter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function makeRenderer(instance, column, meta = {}) {
|
export function makeRenderer(instance, column, meta = {}) {
|
||||||
@ -239,6 +229,7 @@ export function makeRenderer(instance, column, meta = {}) {
|
|||||||
const Comp = typeof type === 'string' ? column[type] : type
|
const Comp = typeof type === 'string' ? column[type] : type
|
||||||
|
|
||||||
if (typeof Comp === 'undefined') {
|
if (typeof Comp === 'undefined') {
|
||||||
|
console.info(column)
|
||||||
throw new Error(renderErr)
|
throw new Error(renderErr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
/* istanbul ignore file */
|
||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
// Token pagination behaves a bit different from
|
// Token pagination behaves a bit different from
|
||||||
|
|||||||
293
src/utils.js
293
src/utils.js
@ -1,7 +1,5 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { defaultColumn } from './publicUtils'
|
import { defaultColumn, reduceHooks } from './publicUtils'
|
||||||
|
|
||||||
export * from './publicUtils'
|
|
||||||
|
|
||||||
// Find the depth of the columns
|
// Find the depth of the columns
|
||||||
export function findMaxDepth(columns, depth = 0) {
|
export function findMaxDepth(columns, depth = 0) {
|
||||||
@ -13,10 +11,29 @@ export function findMaxDepth(columns, depth = 0) {
|
|||||||
}, 0)
|
}, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
function decorateColumn(column, userDefaultColumn, parent, depth, index) {
|
// Build the visible columns, headers and flat column list
|
||||||
// Apply the userDefaultColumn
|
export function linkColumnStructure(columns, parent, depth = 0) {
|
||||||
column = { ...defaultColumn, ...userDefaultColumn, ...column }
|
return columns.map(column => {
|
||||||
|
column = {
|
||||||
|
...column,
|
||||||
|
parent,
|
||||||
|
depth,
|
||||||
|
}
|
||||||
|
|
||||||
|
assignColumnAccessor(column)
|
||||||
|
|
||||||
|
if (column.columns) {
|
||||||
|
column.columns = linkColumnStructure(column.columns, column, depth + 1)
|
||||||
|
}
|
||||||
|
return column
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function flattenColumns(columns) {
|
||||||
|
return flattenBy(columns, 'columns')
|
||||||
|
}
|
||||||
|
|
||||||
|
export function assignColumnAccessor(column) {
|
||||||
// First check for string accessor
|
// First check for string accessor
|
||||||
let { id, accessor, Header } = column
|
let { id, accessor, Header } = column
|
||||||
|
|
||||||
@ -40,123 +57,199 @@ function decorateColumn(column, userDefaultColumn, parent, depth, index) {
|
|||||||
throw new Error('A column ID (or string accessor) is required!')
|
throw new Error('A column ID (or string accessor) is required!')
|
||||||
}
|
}
|
||||||
|
|
||||||
column = {
|
Object.assign(column, {
|
||||||
// Make sure there is a fallback header, just in case
|
|
||||||
Header: () => <> </>,
|
|
||||||
Footer: () => <> </>,
|
|
||||||
...column,
|
|
||||||
// Materialize and override this stuff
|
|
||||||
id,
|
id,
|
||||||
accessor,
|
accessor,
|
||||||
parent,
|
})
|
||||||
depth,
|
|
||||||
index,
|
|
||||||
}
|
|
||||||
|
|
||||||
return column
|
return column
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the visible columns, headers and flat column list
|
// Find the depth of the columns
|
||||||
export function decorateColumnTree(columns, defaultColumn, parent, depth = 0) {
|
export function dedupeBy(arr, fn) {
|
||||||
return columns.map((column, columnIndex) => {
|
return [...arr]
|
||||||
column = decorateColumn(column, defaultColumn, parent, depth, columnIndex)
|
.reverse()
|
||||||
if (column.columns) {
|
.filter((d, i, all) => all.findIndex(dd => fn(dd) === fn(d)) === i)
|
||||||
column.columns = decorateColumnTree(
|
.reverse()
|
||||||
column.columns,
|
}
|
||||||
defaultColumn,
|
|
||||||
column,
|
export function decorateColumn(column, userDefaultColumn) {
|
||||||
depth + 1
|
if (!userDefaultColumn) {
|
||||||
)
|
throw new Error()
|
||||||
}
|
}
|
||||||
return column
|
Object.assign(column, {
|
||||||
|
// Make sure there is a fallback header, just in case
|
||||||
|
Header: () => <> </>,
|
||||||
|
Footer: () => <> </>,
|
||||||
|
...defaultColumn,
|
||||||
|
...userDefaultColumn,
|
||||||
|
...column,
|
||||||
})
|
})
|
||||||
|
return column
|
||||||
|
}
|
||||||
|
|
||||||
|
export function accessRowsForColumn({
|
||||||
|
data,
|
||||||
|
rows,
|
||||||
|
flatRows,
|
||||||
|
rowsById,
|
||||||
|
column,
|
||||||
|
getRowId,
|
||||||
|
getSubRows,
|
||||||
|
accessValueHooks,
|
||||||
|
getInstance,
|
||||||
|
}) {
|
||||||
|
// Access the row's data column-by-column
|
||||||
|
// We do it this way so we can incrementally add materialized
|
||||||
|
// columns after the first pass and avoid excessive looping
|
||||||
|
const accessRow = (originalRow, rowIndex, depth = 0, parent, parentRows) => {
|
||||||
|
// Keep the original reference around
|
||||||
|
const original = originalRow
|
||||||
|
|
||||||
|
const id = getRowId(originalRow, rowIndex, parent)
|
||||||
|
|
||||||
|
let row = rowsById[id]
|
||||||
|
|
||||||
|
// If the row hasn't been created, let's make it
|
||||||
|
if (!row) {
|
||||||
|
row = {
|
||||||
|
id,
|
||||||
|
original,
|
||||||
|
index: rowIndex,
|
||||||
|
depth,
|
||||||
|
cells: [{}], // This is a dummy cell
|
||||||
|
}
|
||||||
|
|
||||||
|
// Override common array functions (and the dummy cell's getCellProps function)
|
||||||
|
// to show an error if it is accessed without calling prepareRow
|
||||||
|
row.cells.map = unpreparedAccessWarning
|
||||||
|
row.cells.filter = unpreparedAccessWarning
|
||||||
|
row.cells.forEach = unpreparedAccessWarning
|
||||||
|
row.cells[0].getCellProps = unpreparedAccessWarning
|
||||||
|
|
||||||
|
// Create the cells and values
|
||||||
|
row.values = {}
|
||||||
|
|
||||||
|
// Push this row into the parentRows array
|
||||||
|
parentRows.push(row)
|
||||||
|
// Keep track of every row in a flat array
|
||||||
|
flatRows.push(row)
|
||||||
|
// Also keep track of every row by its ID
|
||||||
|
rowsById[id] = row
|
||||||
|
|
||||||
|
// Get the original subrows
|
||||||
|
row.originalSubRows = getSubRows(originalRow, rowIndex)
|
||||||
|
|
||||||
|
// Then recursively access them
|
||||||
|
if (row.originalSubRows) {
|
||||||
|
const subRows = []
|
||||||
|
row.originalSubRows.forEach((d, i) =>
|
||||||
|
accessRow(d, i, depth + 1, row, subRows)
|
||||||
|
)
|
||||||
|
// Keep the new subRows array on the row
|
||||||
|
row.subRows = subRows
|
||||||
|
}
|
||||||
|
} else if (row.subRows) {
|
||||||
|
// If the row exists, then it's already been accessed
|
||||||
|
// Keep recursing, but don't worry about passing the
|
||||||
|
// accumlator array (those rows already exist)
|
||||||
|
row.originalSubRows.forEach((d, i) => accessRow(d, i, depth + 1, row))
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the column has an accessor, use it to get a value
|
||||||
|
if (column.accessor) {
|
||||||
|
row.values[column.id] = column.accessor(originalRow, rowIndex, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Allow plugins to manipulate the column value
|
||||||
|
row.values[column.id] = reduceHooks(
|
||||||
|
accessValueHooks,
|
||||||
|
row.values[column.id],
|
||||||
|
{
|
||||||
|
row,
|
||||||
|
column,
|
||||||
|
instance: getInstance(),
|
||||||
|
},
|
||||||
|
true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
data.forEach((originalRow, rowIndex) =>
|
||||||
|
accessRow(originalRow, rowIndex, 0, undefined, rows)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the header groups from the bottom up
|
// Build the header groups from the bottom up
|
||||||
export function makeHeaderGroups(flatColumns, defaultColumn) {
|
export function makeHeaderGroups(allColumns, defaultColumn) {
|
||||||
const headerGroups = []
|
const headerGroups = []
|
||||||
|
|
||||||
// Build each header group from the bottom up
|
let scanColumns = allColumns
|
||||||
const buildGroup = (columns, depth) => {
|
|
||||||
|
let uid = 0
|
||||||
|
const getUID = () => uid++
|
||||||
|
|
||||||
|
while (scanColumns.length) {
|
||||||
|
// The header group we are creating
|
||||||
const headerGroup = {
|
const headerGroup = {
|
||||||
headers: [],
|
headers: [],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The parent columns we're going to scan next
|
||||||
const parentColumns = []
|
const parentColumns = []
|
||||||
|
|
||||||
// Do any of these columns have parents?
|
const hasParents = scanColumns.some(d => d.parent)
|
||||||
const hasParents = columns.some(col => col.parent)
|
|
||||||
|
|
||||||
columns.forEach(column => {
|
|
||||||
// Are we the first column in this group?
|
|
||||||
const isFirst = !parentColumns.length
|
|
||||||
|
|
||||||
|
// Scan each column for parents
|
||||||
|
scanColumns.forEach(column => {
|
||||||
// What is the latest (last) parent column?
|
// What is the latest (last) parent column?
|
||||||
let latestParentColumn = [...parentColumns].reverse()[0]
|
let latestParentColumn = [...parentColumns].reverse()[0]
|
||||||
|
|
||||||
// If the column has a parent, add it if necessary
|
let newParent
|
||||||
if (column.parent) {
|
|
||||||
const similarParentColumns = parentColumns.filter(
|
if (hasParents) {
|
||||||
d => d.originalId === column.parent.id
|
// If the column has a parent, add it if necessary
|
||||||
)
|
if (column.parent) {
|
||||||
if (isFirst || latestParentColumn.originalId !== column.parent.id) {
|
newParent = {
|
||||||
parentColumns.push({
|
|
||||||
...column.parent,
|
...column.parent,
|
||||||
originalId: column.parent.id,
|
originalId: column.parent.id,
|
||||||
id: [column.parent.id, similarParentColumns.length].join('_'),
|
id: `${column.parent.id}_${getUID()}`,
|
||||||
})
|
headers: [column],
|
||||||
}
|
}
|
||||||
} else if (hasParents) {
|
} else {
|
||||||
// If other columns have parents, we'll need to add a place holder if necessary
|
// If other columns have parents, we'll need to add a place holder if necessary
|
||||||
const originalId = [column.id, 'placeholder'].join('_')
|
const originalId = `${column.id}_placeholder`
|
||||||
const similarParentColumns = parentColumns.filter(
|
newParent = decorateColumn(
|
||||||
d => d.originalId === originalId
|
{
|
||||||
)
|
originalId,
|
||||||
const placeholderColumn = decorateColumn(
|
id: `${column.id}_placeholder_${getUID()}`,
|
||||||
{
|
placeholderOf: column,
|
||||||
originalId,
|
headers: [column],
|
||||||
id: [column.id, 'placeholder', similarParentColumns.length].join(
|
},
|
||||||
'_'
|
defaultColumn
|
||||||
),
|
|
||||||
placeholderOf: column,
|
|
||||||
},
|
|
||||||
defaultColumn
|
|
||||||
)
|
|
||||||
if (
|
|
||||||
isFirst ||
|
|
||||||
latestParentColumn.originalId !== placeholderColumn.originalId
|
|
||||||
) {
|
|
||||||
parentColumns.push(placeholderColumn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Establish the new headers[] relationship on the parent
|
|
||||||
if (column.parent || hasParents) {
|
|
||||||
latestParentColumn = [...parentColumns].reverse()[0]
|
|
||||||
latestParentColumn.headers = latestParentColumn.headers || []
|
|
||||||
if (!latestParentColumn.headers.includes(column)) {
|
|
||||||
latestParentColumn.headers.push(column)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
column.totalHeaderCount = column.headers
|
|
||||||
? column.headers.reduce(
|
|
||||||
(sum, header) => sum + header.totalHeaderCount,
|
|
||||||
0
|
|
||||||
)
|
)
|
||||||
: 1 // Leaf node columns take up at least one count
|
}
|
||||||
|
|
||||||
|
// If the resulting parent columns are the same, just add
|
||||||
|
// the column and increment the header span
|
||||||
|
if (
|
||||||
|
latestParentColumn &&
|
||||||
|
latestParentColumn.originalId === newParent.originalId
|
||||||
|
) {
|
||||||
|
latestParentColumn.headers.push(column)
|
||||||
|
} else {
|
||||||
|
parentColumns.push(newParent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
headerGroup.headers.push(column)
|
headerGroup.headers.push(column)
|
||||||
})
|
})
|
||||||
|
|
||||||
headerGroups.push(headerGroup)
|
headerGroups.push(headerGroup)
|
||||||
|
|
||||||
if (parentColumns.length) {
|
// Start scanning the parent columns
|
||||||
buildGroup(parentColumns, depth + 1)
|
scanColumns = parentColumns
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buildGroup(flatColumns, 0)
|
|
||||||
|
|
||||||
return headerGroups.reverse()
|
return headerGroups.reverse()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,20 +318,20 @@ export function isFunction(a) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function flattenBy(columns, childKey) {
|
export function flattenBy(arr, key) {
|
||||||
const flatColumns = []
|
const flatColumns = []
|
||||||
|
|
||||||
const recurse = columns => {
|
const recurse = arr => {
|
||||||
columns.forEach(d => {
|
arr.forEach(d => {
|
||||||
if (!d[childKey]) {
|
if (!d[key]) {
|
||||||
flatColumns.push(d)
|
flatColumns.push(d)
|
||||||
} else {
|
} else {
|
||||||
recurse(d[childKey])
|
recurse(d[key])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
recurse(columns)
|
recurse(arr)
|
||||||
|
|
||||||
return flatColumns
|
return flatColumns
|
||||||
}
|
}
|
||||||
@ -280,6 +373,12 @@ export function shouldAutoRemoveFilter(autoRemove, value) {
|
|||||||
return autoRemove ? autoRemove(value) : typeof value === 'undefined'
|
return autoRemove ? autoRemove(value) : typeof value === 'undefined'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function unpreparedAccessWarning() {
|
||||||
|
throw new Error(
|
||||||
|
'React-Table: You have not called prepareRow(row) one or more rows you are attempting to render.'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
const reOpenBracket = /\[/g
|
const reOpenBracket = /\[/g
|
||||||
|
|||||||
41
test-utils/makeTestData.js
Normal file
41
test-utils/makeTestData.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
const range = len => {
|
||||||
|
const arr = []
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
arr.push(i)
|
||||||
|
}
|
||||||
|
return arr
|
||||||
|
}
|
||||||
|
|
||||||
|
const newPerson = (depth, index, total) => {
|
||||||
|
const age = (depth + 1) * (index + 1)
|
||||||
|
const visits = age * 10
|
||||||
|
const progress = (index + 1) / total
|
||||||
|
|
||||||
|
return {
|
||||||
|
firstName: `${depth} ${index} firstName`,
|
||||||
|
lastName: `${depth} ${index} lastName`,
|
||||||
|
age,
|
||||||
|
visits,
|
||||||
|
progress,
|
||||||
|
status:
|
||||||
|
progress > 0.66
|
||||||
|
? 'relationship'
|
||||||
|
: progress > 0.33
|
||||||
|
? 'complicated'
|
||||||
|
: 'single',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function makeTestData(...lens) {
|
||||||
|
const makeDataLevel = (depth = 0) => {
|
||||||
|
const len = lens[depth]
|
||||||
|
return range(len).map(d => {
|
||||||
|
return {
|
||||||
|
...newPerson(depth, d, len),
|
||||||
|
subRows: lens[depth + 1] ? makeDataLevel(depth + 1) : undefined,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return makeDataLevel()
|
||||||
|
}
|
||||||
31
test-utils/react-testing.js
vendored
Normal file
31
test-utils/react-testing.js
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { render as originalRender } from '@testing-library/react'
|
||||||
|
import diff from 'jest-diff'
|
||||||
|
import chalk from 'chalk'
|
||||||
|
|
||||||
|
const render = (...args) => {
|
||||||
|
const rendered = originalRender(...args)
|
||||||
|
|
||||||
|
rendered.lastFragment = new DocumentFragment()
|
||||||
|
|
||||||
|
rendered.debugDiff = (log = true) => {
|
||||||
|
const nextFragment = rendered.asFragment()
|
||||||
|
|
||||||
|
if (log) {
|
||||||
|
console.log(
|
||||||
|
diff(rendered.lastFragment, nextFragment, {
|
||||||
|
aAnnotation: 'Previous',
|
||||||
|
bAnnotation: 'Next',
|
||||||
|
aColor: chalk.red,
|
||||||
|
bColor: chalk.green,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
rendered.lastFragment = nextFragment
|
||||||
|
}
|
||||||
|
return rendered
|
||||||
|
}
|
||||||
|
|
||||||
|
export * from '@testing-library/react'
|
||||||
|
|
||||||
|
export { render }
|
||||||
Loading…
Reference in New Issue
Block a user