@tanstack/react-table 0.0.1-alpha.8 → 8.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cjs/_virtual/_rollupPluginBabelHelpers.js +112 -0
- package/build/cjs/_virtual/_rollupPluginBabelHelpers.js.map +1 -0
- package/build/cjs/aggregationTypes.js +130 -0
- package/build/cjs/aggregationTypes.js.map +1 -0
- package/build/cjs/core.js +519 -0
- package/build/cjs/core.js.map +1 -0
- package/build/cjs/createTable.js +103 -0
- package/build/cjs/createTable.js.map +1 -0
- package/build/cjs/features/ColumnSizing.js +331 -0
- package/build/cjs/features/ColumnSizing.js.map +1 -0
- package/build/cjs/features/Expanding.js +234 -0
- package/build/cjs/features/Expanding.js.map +1 -0
- package/build/cjs/features/Filters.js +396 -0
- package/build/cjs/features/Filters.js.map +1 -0
- package/build/cjs/features/Grouping.js +228 -0
- package/build/cjs/features/Grouping.js.map +1 -0
- package/build/cjs/features/Headers.js +486 -0
- package/build/cjs/features/Headers.js.map +1 -0
- package/build/cjs/features/Ordering.js +83 -0
- package/build/cjs/features/Ordering.js.map +1 -0
- package/build/cjs/features/Pinning.js +163 -0
- package/build/cjs/features/Pinning.js.map +1 -0
- package/build/cjs/features/Sorting.js +269 -0
- package/build/cjs/features/Sorting.js.map +1 -0
- package/build/cjs/features/Visibility.js +160 -0
- package/build/cjs/features/Visibility.js.map +1 -0
- package/build/cjs/filterTypes.js +172 -0
- package/build/cjs/filterTypes.js.map +1 -0
- package/build/cjs/index.js +30 -0
- package/build/cjs/index.js.map +1 -0
- package/build/cjs/sortTypes.js +121 -0
- package/build/cjs/sortTypes.js.map +1 -0
- package/build/cjs/utils/columnFilterRowsFn.js +130 -0
- package/build/cjs/utils/columnFilterRowsFn.js.map +1 -0
- package/build/cjs/utils/expandRowsFn.js +38 -0
- package/build/cjs/utils/expandRowsFn.js.map +1 -0
- package/build/cjs/utils/globalFilterRowsFn.js +100 -0
- package/build/cjs/utils/globalFilterRowsFn.js.map +1 -0
- package/build/cjs/utils/groupRowsFn.js +154 -0
- package/build/cjs/utils/groupRowsFn.js.map +1 -0
- package/build/cjs/utils/sortRowsFn.js +93 -0
- package/build/cjs/utils/sortRowsFn.js.map +1 -0
- package/build/cjs/utils.js +143 -0
- package/build/cjs/utils.js.map +1 -0
- package/build/esm/index.js +3722 -0
- package/build/esm/index.js.map +1 -0
- package/build/stats-html.html +2689 -0
- package/build/stats-react.json +747 -0
- package/build/types/aggregationTypes.d.ts +22 -0
- package/build/types/core.d.ts +126 -0
- package/build/types/createTable.d.ts +35 -0
- package/build/types/features/ColumnSizing.d.ts +73 -0
- package/build/types/features/Expanding.d.ts +52 -0
- package/build/types/features/Filters.d.ts +93 -0
- package/build/types/features/Grouping.d.ts +82 -0
- package/build/types/features/Headers.d.ts +41 -0
- package/build/types/features/Ordering.d.ts +19 -0
- package/build/types/features/Pinning.d.ts +39 -0
- package/build/types/features/Sorting.d.ts +75 -0
- package/build/types/features/Visibility.d.ts +47 -0
- package/build/types/filterTypes.d.ts +50 -0
- package/build/types/index.d.ts +7 -0
- package/build/types/sortTypes.d.ts +17 -0
- package/build/types/types.d.ts +124 -0
- package/build/types/utils/columnFilterRowsFn.d.ts +2 -0
- package/build/types/utils/expandRowsFn.d.ts +2 -0
- package/build/types/utils/globalFilterRowsFn.d.ts +2 -0
- package/build/types/utils/groupRowsFn.d.ts +2 -0
- package/build/types/utils/sortRowsFn.d.ts +2 -0
- package/build/types/utils.d.ts +24 -0
- package/{dist/react-table.development.js → build/umd/index.development.js} +372 -29
- package/build/umd/index.development.js.map +1 -0
- package/build/umd/index.production.js +12 -0
- package/build/umd/index.production.js.map +1 -0
- package/package.json +9 -94
- package/src/core.tsx +43 -5
- package/src/createTable.tsx +1 -1
- package/src/features/ColumnSizing.ts +482 -0
- package/src/features/Filters.ts +1 -1
- package/src/features/Headers.ts +43 -6
- package/src/features/{withPagination.ts → withPagination.oldts} +0 -0
- package/src/features/{withRowSelection.ts → withRowSelection.oldts} +0 -0
- package/src/types.ts +35 -6
- package/src/utils.tsx +8 -2
- package/dist/react-table.development.js.map +0 -1
- package/dist/react-table.production.min.js +0 -2
- package/dist/react-table.production.min.js.map +0 -1
- package/lib/index.js +0 -65
- package/src/features/notest/useAbsoluteLayout.test.js +0 -152
- package/src/features/notest/useBlockLayout.test.js +0 -158
- package/src/features/notest/useColumnOrder.test.js +0 -186
- package/src/features/notest/useExpanded.test.js +0 -125
- package/src/features/notest/useFilters.test.js +0 -393
- package/src/features/notest/useFiltersAndRowSelect.test.js +0 -256
- package/src/features/notest/useFlexLayout.test.js +0 -152
- package/src/features/notest/useGroupBy.test.js +0 -259
- package/src/features/notest/usePagination.test.js +0 -231
- package/src/features/notest/useResizeColumns.test.js +0 -229
- package/src/features/notest/useRowSelect.test.js +0 -250
- package/src/features/notest/useRowState.test.js +0 -178
- package/src/features/tests/Visibility.test.tsx +0 -225
- package/src/features/tests/__snapshots__/Visibility.test.tsx.snap +0 -390
- package/src/features/tests/withSorting.notest.tsx +0 -341
- package/src/features/withColumnResizing.ts +0 -281
- package/src/test-utils/makeTestData.ts +0 -41
- package/src/tests/__snapshots__/core.test.tsx.snap +0 -148
- package/src/tests/core.test.tsx +0 -241
|
@@ -1,393 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { render, fireEvent } from '../../../test-utils/react-testing'
|
|
3
|
-
import { useTable } from '../../hooks/useTable'
|
|
4
|
-
import { useFilters } from '../useFilters'
|
|
5
|
-
import { useGlobalFilter } from '../useGlobalFilter'
|
|
6
|
-
|
|
7
|
-
const makeData = () => [
|
|
8
|
-
{
|
|
9
|
-
firstName: 'tanner',
|
|
10
|
-
lastName: 'linsley',
|
|
11
|
-
age: 29,
|
|
12
|
-
visits: 100,
|
|
13
|
-
status: 'In Relationship',
|
|
14
|
-
progress: 50,
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
firstName: 'derek',
|
|
18
|
-
lastName: 'perkins',
|
|
19
|
-
age: 40,
|
|
20
|
-
visits: 40,
|
|
21
|
-
status: 'Single',
|
|
22
|
-
progress: 80,
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
firstName: 'joe',
|
|
26
|
-
lastName: 'bergevin',
|
|
27
|
-
age: 45,
|
|
28
|
-
visits: 20,
|
|
29
|
-
status: 'Complicated',
|
|
30
|
-
progress: 10,
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
firstName: 'jaylen',
|
|
34
|
-
lastName: 'linsley',
|
|
35
|
-
age: 26,
|
|
36
|
-
visits: 99,
|
|
37
|
-
status: 'In Relationship',
|
|
38
|
-
progress: 70,
|
|
39
|
-
},
|
|
40
|
-
]
|
|
41
|
-
|
|
42
|
-
const defaultColumn = {
|
|
43
|
-
Cell: ({ value, column: { id } }) => `${id}: ${value}`,
|
|
44
|
-
Filter: ({ column: { filterValue, setFilter } }) => (
|
|
45
|
-
<input
|
|
46
|
-
value={filterValue || ''}
|
|
47
|
-
onChange={e => {
|
|
48
|
-
setFilter(e.target.value || undefined) // Set undefined to remove the filter entirely
|
|
49
|
-
}}
|
|
50
|
-
placeholder="Search..."
|
|
51
|
-
/>
|
|
52
|
-
),
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function App(props) {
|
|
56
|
-
const [data, setData] = React.useState(makeData)
|
|
57
|
-
|
|
58
|
-
const columns = React.useMemo(() => {
|
|
59
|
-
if (props.columns) {
|
|
60
|
-
return props.columns
|
|
61
|
-
}
|
|
62
|
-
return [
|
|
63
|
-
{
|
|
64
|
-
Header: 'Name',
|
|
65
|
-
columns: [
|
|
66
|
-
{
|
|
67
|
-
Header: 'First Name',
|
|
68
|
-
accessor: 'firstName',
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
Header: 'Last Name',
|
|
72
|
-
accessor: 'lastName',
|
|
73
|
-
},
|
|
74
|
-
],
|
|
75
|
-
},
|
|
76
|
-
{
|
|
77
|
-
Header: 'Info',
|
|
78
|
-
columns: [
|
|
79
|
-
{
|
|
80
|
-
Header: 'Age',
|
|
81
|
-
accessor: 'age',
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
Header: 'Visits',
|
|
85
|
-
accessor: 'visits',
|
|
86
|
-
},
|
|
87
|
-
{
|
|
88
|
-
Header: 'Status',
|
|
89
|
-
accessor: 'status',
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
Header: 'Profile Progress',
|
|
93
|
-
accessor: 'progress',
|
|
94
|
-
},
|
|
95
|
-
],
|
|
96
|
-
},
|
|
97
|
-
]
|
|
98
|
-
}, [props.columns])
|
|
99
|
-
|
|
100
|
-
const {
|
|
101
|
-
getTableProps,
|
|
102
|
-
getTableBodyProps,
|
|
103
|
-
headerGroups,
|
|
104
|
-
rows,
|
|
105
|
-
prepareRow,
|
|
106
|
-
leafColumns,
|
|
107
|
-
state,
|
|
108
|
-
setGlobalFilter,
|
|
109
|
-
} = useTable(
|
|
110
|
-
{
|
|
111
|
-
columns,
|
|
112
|
-
data,
|
|
113
|
-
defaultColumn,
|
|
114
|
-
},
|
|
115
|
-
useFilters,
|
|
116
|
-
useGlobalFilter
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
const reset = () => setData(makeData())
|
|
120
|
-
|
|
121
|
-
return (
|
|
122
|
-
<>
|
|
123
|
-
<button onClick={reset}>Reset Data</button>
|
|
124
|
-
<table {...getTableProps()}>
|
|
125
|
-
<thead>
|
|
126
|
-
{headerGroups.map(headerGroup => (
|
|
127
|
-
<tr {...headerGroup.getHeaderGroupProps()}>
|
|
128
|
-
{headerGroup.headers.map(column => (
|
|
129
|
-
<th {...column.getHeaderProps()}>
|
|
130
|
-
{column.render('Header')}
|
|
131
|
-
{column.canFilter ? column.render('Filter') : null}
|
|
132
|
-
</th>
|
|
133
|
-
))}
|
|
134
|
-
</tr>
|
|
135
|
-
))}
|
|
136
|
-
<tr>
|
|
137
|
-
<th
|
|
138
|
-
colSpan={leafColumns.length}
|
|
139
|
-
style={{
|
|
140
|
-
textAlign: 'left',
|
|
141
|
-
}}
|
|
142
|
-
>
|
|
143
|
-
<span>
|
|
144
|
-
<input
|
|
145
|
-
value={state.globalFilter || ''}
|
|
146
|
-
onChange={e => {
|
|
147
|
-
setGlobalFilter(e.target.value || undefined) // Set undefined to remove the filter entirely
|
|
148
|
-
}}
|
|
149
|
-
placeholder={`Global search...`}
|
|
150
|
-
style={{
|
|
151
|
-
fontSize: '1.1rem',
|
|
152
|
-
border: '0',
|
|
153
|
-
}}
|
|
154
|
-
/>
|
|
155
|
-
</span>
|
|
156
|
-
</th>
|
|
157
|
-
</tr>
|
|
158
|
-
</thead>
|
|
159
|
-
<tbody {...getTableBodyProps()}>
|
|
160
|
-
{rows.map(
|
|
161
|
-
(row, i) =>
|
|
162
|
-
prepareRow(row) || (
|
|
163
|
-
<tr {...row.getRowProps()}>
|
|
164
|
-
{row.cells.map(cell => (
|
|
165
|
-
<td {...cell.getCellProps()}>{cell.render('Cell')}</td>
|
|
166
|
-
))}
|
|
167
|
-
</tr>
|
|
168
|
-
)
|
|
169
|
-
)}
|
|
170
|
-
</tbody>
|
|
171
|
-
</table>
|
|
172
|
-
</>
|
|
173
|
-
)
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
test('renders a filterable table', async () => {
|
|
177
|
-
const rendered = render(<App />)
|
|
178
|
-
|
|
179
|
-
const resetButton = rendered.getByText('Reset Data')
|
|
180
|
-
const globalFilterInput = rendered.getByPlaceholderText('Global search...')
|
|
181
|
-
const filterInputs = rendered.getAllByPlaceholderText('Search...')
|
|
182
|
-
|
|
183
|
-
expect(filterInputs).toHaveLength(6)
|
|
184
|
-
|
|
185
|
-
fireEvent.change(filterInputs[1], { target: { value: 'l' } })
|
|
186
|
-
expect(
|
|
187
|
-
rendered
|
|
188
|
-
.queryAllByRole('row')
|
|
189
|
-
.slice(3)
|
|
190
|
-
.map(row => Array.from(row.children)[0].textContent)
|
|
191
|
-
).toEqual(['firstName: tanner', 'firstName: jaylen'])
|
|
192
|
-
|
|
193
|
-
fireEvent.change(filterInputs[1], { target: { value: 'er' } })
|
|
194
|
-
expect(
|
|
195
|
-
rendered
|
|
196
|
-
.queryAllByRole('row')
|
|
197
|
-
.slice(3)
|
|
198
|
-
.map(row => Array.from(row.children)[0].textContent)
|
|
199
|
-
).toEqual(['firstName: derek', 'firstName: joe'])
|
|
200
|
-
|
|
201
|
-
fireEvent.change(filterInputs[2], { target: { value: 'nothing' } })
|
|
202
|
-
expect(
|
|
203
|
-
rendered
|
|
204
|
-
.queryAllByRole('row')
|
|
205
|
-
.slice(3)
|
|
206
|
-
.map(row => Array.from(row.children)[0].textContent)
|
|
207
|
-
).toEqual([])
|
|
208
|
-
|
|
209
|
-
fireEvent.change(filterInputs[1], { target: { value: '' } })
|
|
210
|
-
expect(
|
|
211
|
-
rendered
|
|
212
|
-
.queryAllByRole('row')
|
|
213
|
-
.slice(3)
|
|
214
|
-
.map(row => Array.from(row.children)[0].textContent)
|
|
215
|
-
).toEqual([])
|
|
216
|
-
|
|
217
|
-
fireEvent.change(filterInputs[2], { target: { value: '' } })
|
|
218
|
-
expect(
|
|
219
|
-
rendered
|
|
220
|
-
.queryAllByRole('row')
|
|
221
|
-
.slice(3)
|
|
222
|
-
.map(row => Array.from(row.children)[0].textContent)
|
|
223
|
-
).toEqual([
|
|
224
|
-
'firstName: tanner',
|
|
225
|
-
'firstName: derek',
|
|
226
|
-
'firstName: joe',
|
|
227
|
-
'firstName: jaylen',
|
|
228
|
-
])
|
|
229
|
-
|
|
230
|
-
fireEvent.change(globalFilterInput, { target: { value: 'li' } })
|
|
231
|
-
expect(
|
|
232
|
-
rendered
|
|
233
|
-
.queryAllByRole('row')
|
|
234
|
-
.slice(3)
|
|
235
|
-
.map(row => Array.from(row.children)[0].textContent)
|
|
236
|
-
).toEqual(['firstName: tanner', 'firstName: joe', 'firstName: jaylen'])
|
|
237
|
-
|
|
238
|
-
fireEvent.click(resetButton)
|
|
239
|
-
expect(
|
|
240
|
-
rendered
|
|
241
|
-
.queryAllByRole('row')
|
|
242
|
-
.slice(3)
|
|
243
|
-
.map(row => Array.from(row.children)[0].textContent)
|
|
244
|
-
).toEqual([
|
|
245
|
-
'firstName: tanner',
|
|
246
|
-
'firstName: derek',
|
|
247
|
-
'firstName: joe',
|
|
248
|
-
'firstName: jaylen',
|
|
249
|
-
])
|
|
250
|
-
})
|
|
251
|
-
|
|
252
|
-
test('does not filter columns marked as disableFilters', () => {
|
|
253
|
-
const columns = [
|
|
254
|
-
{
|
|
255
|
-
Header: 'Name',
|
|
256
|
-
columns: [
|
|
257
|
-
{
|
|
258
|
-
Header: 'First Name',
|
|
259
|
-
accessor: 'firstName',
|
|
260
|
-
disableFilters: true,
|
|
261
|
-
},
|
|
262
|
-
{
|
|
263
|
-
Header: 'Last Name',
|
|
264
|
-
accessor: 'lastName',
|
|
265
|
-
disableFilters: true,
|
|
266
|
-
},
|
|
267
|
-
],
|
|
268
|
-
},
|
|
269
|
-
{
|
|
270
|
-
Header: 'Info',
|
|
271
|
-
columns: [
|
|
272
|
-
{
|
|
273
|
-
Header: 'Age',
|
|
274
|
-
accessor: 'age',
|
|
275
|
-
},
|
|
276
|
-
{
|
|
277
|
-
Header: 'Visits',
|
|
278
|
-
accessor: 'visits',
|
|
279
|
-
},
|
|
280
|
-
{
|
|
281
|
-
Header: 'Status',
|
|
282
|
-
accessor: 'status',
|
|
283
|
-
},
|
|
284
|
-
{
|
|
285
|
-
Header: 'Profile Progress',
|
|
286
|
-
accessor: 'progress',
|
|
287
|
-
},
|
|
288
|
-
],
|
|
289
|
-
},
|
|
290
|
-
]
|
|
291
|
-
const rendered = render(<App columns={columns} />)
|
|
292
|
-
|
|
293
|
-
const filterInputs = rendered.getAllByPlaceholderText('Search...')
|
|
294
|
-
|
|
295
|
-
expect(filterInputs).toHaveLength(4)
|
|
296
|
-
|
|
297
|
-
// should be Age column
|
|
298
|
-
fireEvent.change(filterInputs[0], { target: { value: '45' } })
|
|
299
|
-
expect(
|
|
300
|
-
rendered
|
|
301
|
-
.queryAllByRole('row')
|
|
302
|
-
.slice(3)
|
|
303
|
-
.map(row => Array.from(row.children)[0].textContent)
|
|
304
|
-
).toEqual(['firstName: joe'])
|
|
305
|
-
|
|
306
|
-
fireEvent.change(filterInputs[0], { target: { value: '' } })
|
|
307
|
-
expect(
|
|
308
|
-
rendered
|
|
309
|
-
.queryAllByRole('row')
|
|
310
|
-
.slice(3)
|
|
311
|
-
.map(row => Array.from(row.children)[0].textContent)
|
|
312
|
-
).toEqual([
|
|
313
|
-
'firstName: tanner',
|
|
314
|
-
'firstName: derek',
|
|
315
|
-
'firstName: joe',
|
|
316
|
-
'firstName: jaylen',
|
|
317
|
-
])
|
|
318
|
-
})
|
|
319
|
-
|
|
320
|
-
test('does not filter columns with GlobalFilter if marked disableGlobalFilter', () => {
|
|
321
|
-
const columns = [
|
|
322
|
-
{
|
|
323
|
-
Header: 'Name',
|
|
324
|
-
columns: [
|
|
325
|
-
{
|
|
326
|
-
Header: 'First Name',
|
|
327
|
-
accessor: 'firstName',
|
|
328
|
-
disableGlobalFilter: true,
|
|
329
|
-
},
|
|
330
|
-
{
|
|
331
|
-
Header: 'Last Name',
|
|
332
|
-
accessor: 'lastName',
|
|
333
|
-
disableGlobalFilter: true,
|
|
334
|
-
},
|
|
335
|
-
],
|
|
336
|
-
},
|
|
337
|
-
{
|
|
338
|
-
Header: 'Info',
|
|
339
|
-
columns: [
|
|
340
|
-
{
|
|
341
|
-
Header: 'Age',
|
|
342
|
-
accessor: 'age',
|
|
343
|
-
},
|
|
344
|
-
{
|
|
345
|
-
Header: 'Visits',
|
|
346
|
-
accessor: 'visits',
|
|
347
|
-
},
|
|
348
|
-
{
|
|
349
|
-
Header: 'Status',
|
|
350
|
-
accessor: 'status',
|
|
351
|
-
},
|
|
352
|
-
{
|
|
353
|
-
Header: 'Profile Progress',
|
|
354
|
-
accessor: 'progress',
|
|
355
|
-
},
|
|
356
|
-
],
|
|
357
|
-
},
|
|
358
|
-
]
|
|
359
|
-
const rendered = render(<App columns={columns} />)
|
|
360
|
-
|
|
361
|
-
const globalFilterInput = rendered.getByPlaceholderText('Global search...')
|
|
362
|
-
|
|
363
|
-
fireEvent.change(globalFilterInput, { target: { value: '' } })
|
|
364
|
-
expect(
|
|
365
|
-
rendered
|
|
366
|
-
.queryAllByRole('row')
|
|
367
|
-
.slice(3)
|
|
368
|
-
.map(row => Array.from(row.children)[0].textContent)
|
|
369
|
-
).toEqual([
|
|
370
|
-
'firstName: tanner',
|
|
371
|
-
'firstName: derek',
|
|
372
|
-
'firstName: joe',
|
|
373
|
-
'firstName: jaylen',
|
|
374
|
-
])
|
|
375
|
-
|
|
376
|
-
// global filter shouldn't apply to firstName or lastName
|
|
377
|
-
fireEvent.change(globalFilterInput, { target: { value: 'li' } })
|
|
378
|
-
expect(
|
|
379
|
-
rendered
|
|
380
|
-
.queryAllByRole('row')
|
|
381
|
-
.slice(3)
|
|
382
|
-
.map(row => Array.from(row.children)[0].textContent)
|
|
383
|
-
).toEqual(['firstName: joe'])
|
|
384
|
-
|
|
385
|
-
// double check global filter ignore (should ignore joe bergevin)
|
|
386
|
-
fireEvent.change(globalFilterInput, { target: { value: 'in' } })
|
|
387
|
-
expect(
|
|
388
|
-
rendered
|
|
389
|
-
.queryAllByRole('row')
|
|
390
|
-
.slice(3)
|
|
391
|
-
.map(row => Array.from(row.children)[0].textContent)
|
|
392
|
-
).toEqual(['firstName: tanner', 'firstName: derek', 'firstName: jaylen'])
|
|
393
|
-
})
|
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
|
-
import { render, fireEvent } from '@testing-library/react'
|
|
3
|
-
import { useTable } from '../../hooks/useTable'
|
|
4
|
-
import { useRowSelect } from '../useRowSelect'
|
|
5
|
-
import { useFilters } from '../useFilters'
|
|
6
|
-
|
|
7
|
-
const dataPiece = [
|
|
8
|
-
{
|
|
9
|
-
firstName: 'tanner',
|
|
10
|
-
lastName: 'linsley',
|
|
11
|
-
age: 29,
|
|
12
|
-
visits: 100,
|
|
13
|
-
status: 'In Relationship',
|
|
14
|
-
progress: 50,
|
|
15
|
-
},
|
|
16
|
-
{
|
|
17
|
-
firstName: 'derek',
|
|
18
|
-
lastName: 'perkins',
|
|
19
|
-
age: 40,
|
|
20
|
-
visits: 40,
|
|
21
|
-
status: 'Single',
|
|
22
|
-
progress: 80,
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
firstName: 'jaylen',
|
|
26
|
-
lastName: 'linsley',
|
|
27
|
-
age: 26,
|
|
28
|
-
visits: 99,
|
|
29
|
-
status: 'In Relationship',
|
|
30
|
-
progress: 70,
|
|
31
|
-
},
|
|
32
|
-
]
|
|
33
|
-
|
|
34
|
-
const data = [
|
|
35
|
-
...dataPiece,
|
|
36
|
-
...dataPiece,
|
|
37
|
-
...dataPiece,
|
|
38
|
-
...dataPiece,
|
|
39
|
-
...dataPiece,
|
|
40
|
-
...dataPiece,
|
|
41
|
-
]
|
|
42
|
-
|
|
43
|
-
function Table({ columns, data }) {
|
|
44
|
-
// Use the state and functions returned from useTable to build your UI
|
|
45
|
-
const {
|
|
46
|
-
getTableProps,
|
|
47
|
-
getTableBodyProps,
|
|
48
|
-
headerGroups,
|
|
49
|
-
rows,
|
|
50
|
-
prepareRow,
|
|
51
|
-
state,
|
|
52
|
-
} = useTable(
|
|
53
|
-
{
|
|
54
|
-
columns,
|
|
55
|
-
data,
|
|
56
|
-
defaultColumn,
|
|
57
|
-
},
|
|
58
|
-
useFilters,
|
|
59
|
-
useRowSelect
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
// Render the UI for your table
|
|
63
|
-
return (
|
|
64
|
-
<>
|
|
65
|
-
<table {...getTableProps()}>
|
|
66
|
-
<thead>
|
|
67
|
-
{headerGroups.map(headerGroup => (
|
|
68
|
-
<tr {...headerGroup.getHeaderGroupProps()}>
|
|
69
|
-
{headerGroup.headers.map(column => (
|
|
70
|
-
<th {...column.getHeaderProps()}>
|
|
71
|
-
{column.render('Header')}
|
|
72
|
-
{column.canFilter ? column.render('Filter') : null}
|
|
73
|
-
</th>
|
|
74
|
-
))}
|
|
75
|
-
</tr>
|
|
76
|
-
))}
|
|
77
|
-
</thead>
|
|
78
|
-
<tbody {...getTableBodyProps()}>
|
|
79
|
-
{rows.map(
|
|
80
|
-
(row, i) =>
|
|
81
|
-
prepareRow(row) || (
|
|
82
|
-
<tr {...row.getRowProps()}>
|
|
83
|
-
{row.cells.map(cell => {
|
|
84
|
-
return (
|
|
85
|
-
<td {...cell.getCellProps()}>{cell.render('Cell')}</td>
|
|
86
|
-
)
|
|
87
|
-
})}
|
|
88
|
-
</tr>
|
|
89
|
-
)
|
|
90
|
-
)}
|
|
91
|
-
</tbody>
|
|
92
|
-
</table>
|
|
93
|
-
<p>
|
|
94
|
-
Selected Rows:{' '}
|
|
95
|
-
<span data-testid="selected-count">
|
|
96
|
-
{Object.keys(state.selection).length}
|
|
97
|
-
</span>
|
|
98
|
-
</p>
|
|
99
|
-
<pre>
|
|
100
|
-
<code>{JSON.stringify({ selection: state.selection }, null, 2)}</code>
|
|
101
|
-
</pre>
|
|
102
|
-
</>
|
|
103
|
-
)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const defaultColumn = {
|
|
107
|
-
Cell: ({ value, column: { id } }) => `${id}: ${value}`,
|
|
108
|
-
Filter: ({ column: { filterValue, setFilter } }) => (
|
|
109
|
-
<input
|
|
110
|
-
value={filterValue || ''}
|
|
111
|
-
onChange={e => {
|
|
112
|
-
setFilter(e.target.value || undefined) // Set undefined to remove the filter entirely
|
|
113
|
-
}}
|
|
114
|
-
placeholder="Search..."
|
|
115
|
-
/>
|
|
116
|
-
),
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const IndeterminateCheckbox = React.forwardRef(
|
|
120
|
-
({ indeterminate, ...rest }, ref) => {
|
|
121
|
-
const defaultRef = React.useRef()
|
|
122
|
-
const resolvedRef = ref || defaultRef
|
|
123
|
-
|
|
124
|
-
React.useEffect(() => {
|
|
125
|
-
resolvedRef.current.indeterminate = indeterminate
|
|
126
|
-
}, [resolvedRef, indeterminate])
|
|
127
|
-
|
|
128
|
-
return <input type="checkbox" ref={resolvedRef} {...rest} />
|
|
129
|
-
}
|
|
130
|
-
)
|
|
131
|
-
|
|
132
|
-
function App() {
|
|
133
|
-
const columns = React.useMemo(
|
|
134
|
-
() => [
|
|
135
|
-
{
|
|
136
|
-
id: 'selection',
|
|
137
|
-
// The header can use the table's getToggleAllRowsSelectedProps method
|
|
138
|
-
// to render a checkbox
|
|
139
|
-
Header: ({ getToggleAllRowsSelectedProps }) => (
|
|
140
|
-
<div>
|
|
141
|
-
<label>
|
|
142
|
-
<IndeterminateCheckbox {...getToggleAllRowsSelectedProps()} />{' '}
|
|
143
|
-
Select All
|
|
144
|
-
</label>
|
|
145
|
-
</div>
|
|
146
|
-
),
|
|
147
|
-
// The cell can use the individual row's getToggleRowSelectedProps method
|
|
148
|
-
// to the render a checkbox
|
|
149
|
-
Cell: ({ row }) => (
|
|
150
|
-
<div>
|
|
151
|
-
<label>
|
|
152
|
-
<IndeterminateCheckbox {...row.getToggleRowSelectedProps()} />{' '}
|
|
153
|
-
Select Row
|
|
154
|
-
</label>
|
|
155
|
-
</div>
|
|
156
|
-
),
|
|
157
|
-
},
|
|
158
|
-
{
|
|
159
|
-
id: 'selectedStatus',
|
|
160
|
-
Cell: ({ row }) => (
|
|
161
|
-
<div>
|
|
162
|
-
Row {row.id} {row.isSelected ? 'Selected' : 'Not Selected'}
|
|
163
|
-
</div>
|
|
164
|
-
),
|
|
165
|
-
},
|
|
166
|
-
{
|
|
167
|
-
Header: 'Name',
|
|
168
|
-
columns: [
|
|
169
|
-
{
|
|
170
|
-
Header: 'First Name',
|
|
171
|
-
accessor: 'firstName',
|
|
172
|
-
},
|
|
173
|
-
{
|
|
174
|
-
Header: 'Last Name',
|
|
175
|
-
accessor: 'lastName',
|
|
176
|
-
},
|
|
177
|
-
],
|
|
178
|
-
},
|
|
179
|
-
{
|
|
180
|
-
Header: 'Info',
|
|
181
|
-
columns: [
|
|
182
|
-
{
|
|
183
|
-
Header: 'Age',
|
|
184
|
-
accessor: 'age',
|
|
185
|
-
},
|
|
186
|
-
{
|
|
187
|
-
id: 'visits',
|
|
188
|
-
Header: 'Visits',
|
|
189
|
-
accessor: 'visits',
|
|
190
|
-
filter: 'equals',
|
|
191
|
-
},
|
|
192
|
-
{
|
|
193
|
-
Header: 'Status',
|
|
194
|
-
accessor: 'status',
|
|
195
|
-
},
|
|
196
|
-
{
|
|
197
|
-
Header: 'Profile Progress',
|
|
198
|
-
accessor: 'progress',
|
|
199
|
-
},
|
|
200
|
-
],
|
|
201
|
-
},
|
|
202
|
-
],
|
|
203
|
-
[]
|
|
204
|
-
)
|
|
205
|
-
|
|
206
|
-
return <Table columns={columns} data={data} />
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
test('Select/Clear All while filtered only affects visible rows', () => {
|
|
210
|
-
const { getAllByPlaceholderText, getByLabelText, getByTestId } = render(
|
|
211
|
-
<App />
|
|
212
|
-
)
|
|
213
|
-
const selectedCount = getByTestId('selected-count')
|
|
214
|
-
const selectAllCheckbox = getByLabelText('Select All')
|
|
215
|
-
const fe = fireEvent
|
|
216
|
-
const filterInputs = getAllByPlaceholderText('Search...')
|
|
217
|
-
|
|
218
|
-
fireEvent.change(filterInputs[3], { target: { value: '40' } })
|
|
219
|
-
expect(selectedCount.textContent).toBe('0') // No selection has been made
|
|
220
|
-
|
|
221
|
-
expect(selectAllCheckbox.checked).toBe(false)
|
|
222
|
-
fireEvent.click(selectAllCheckbox)
|
|
223
|
-
expect(selectAllCheckbox.checked).toBe(true)
|
|
224
|
-
expect(selectedCount.textContent).toBe('6') // "Select All" has happened
|
|
225
|
-
|
|
226
|
-
// This filter hides all the rows (nothing matches it)
|
|
227
|
-
fireEvent.change(filterInputs[3], { target: { value: '10' } })
|
|
228
|
-
expect(selectedCount.textContent).toBe('6') // Filtering does not alter the selection
|
|
229
|
-
|
|
230
|
-
expect(selectAllCheckbox.checked).toBe(false) // None of the selected items are visible, this should be false
|
|
231
|
-
fireEvent.click(selectAllCheckbox) // The selection is is for no rows
|
|
232
|
-
expect(selectAllCheckbox.checked).toBe(false) // So clicking this checkbox does nothing
|
|
233
|
-
expect(selectedCount.textContent).toBe('6') // And does not affect the existing selection
|
|
234
|
-
|
|
235
|
-
fireEvent.change(filterInputs[3], { target: { value: '100' } })
|
|
236
|
-
expect(selectAllCheckbox.checked).toBe(false) // None of the selected items are visible, this should be false
|
|
237
|
-
fireEvent.click(selectAllCheckbox)
|
|
238
|
-
expect(selectAllCheckbox.checked).toBe(true) // Now all of the visible rows are ALSO selected
|
|
239
|
-
expect(selectedCount.textContent).toBe('12') // Clearing all should leave the original 6 selected
|
|
240
|
-
|
|
241
|
-
// Now deselect all the rows that match the filter
|
|
242
|
-
fireEvent.click(selectAllCheckbox)
|
|
243
|
-
expect(selectAllCheckbox.checked).toBe(false) // Now all of the visible rows are ALSO selected
|
|
244
|
-
expect(selectedCount.textContent).toBe('6') // Clearing all should leave the original 6 selected
|
|
245
|
-
|
|
246
|
-
fireEvent.change(filterInputs[3], { target: { value: '' } })
|
|
247
|
-
expect(selectedCount.textContent).toBe('6') // Clearing the Filter does not alter the selection
|
|
248
|
-
|
|
249
|
-
expect(selectAllCheckbox.checked).toBe(false) // Only a subset are selected so this button should show indeterminant
|
|
250
|
-
fireEvent.click(selectAllCheckbox)
|
|
251
|
-
expect(selectAllCheckbox.checked).toBe(true) // Now all of the visible rows are ALSO selected
|
|
252
|
-
expect(selectedCount.textContent).toBe(data.length.toString()) // Select All should select ALL of the rows
|
|
253
|
-
|
|
254
|
-
fireEvent.click(selectAllCheckbox)
|
|
255
|
-
expect(selectedCount.textContent).toBe('0') // Select All should now clear ALL of the rows
|
|
256
|
-
})
|