@tanstack/react-table 0.0.1-alpha.0

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.
Files changed (49) hide show
  1. package/dist/react-table.development.js +3406 -0
  2. package/dist/react-table.development.js.map +1 -0
  3. package/dist/react-table.production.min.js +2 -0
  4. package/dist/react-table.production.min.js.map +1 -0
  5. package/lib/index.js +65 -0
  6. package/package.json +122 -0
  7. package/src/.DS_Store +0 -0
  8. package/src/aggregationTypes.ts +115 -0
  9. package/src/core.tsx +1194 -0
  10. package/src/createTable.tsx +181 -0
  11. package/src/features/Expanding.ts +388 -0
  12. package/src/features/Filters.ts +707 -0
  13. package/src/features/Grouping.ts +451 -0
  14. package/src/features/Headers.ts +907 -0
  15. package/src/features/Ordering.ts +134 -0
  16. package/src/features/Pinning.ts +213 -0
  17. package/src/features/Sorting.ts +487 -0
  18. package/src/features/Visibility.ts +281 -0
  19. package/src/features/notest/useAbsoluteLayout.test.js +152 -0
  20. package/src/features/notest/useBlockLayout.test.js +158 -0
  21. package/src/features/notest/useColumnOrder.test.js +186 -0
  22. package/src/features/notest/useExpanded.test.js +125 -0
  23. package/src/features/notest/useFilters.test.js +393 -0
  24. package/src/features/notest/useFiltersAndRowSelect.test.js +256 -0
  25. package/src/features/notest/useFlexLayout.test.js +152 -0
  26. package/src/features/notest/useGroupBy.test.js +259 -0
  27. package/src/features/notest/usePagination.test.js +231 -0
  28. package/src/features/notest/useResizeColumns.test.js +229 -0
  29. package/src/features/notest/useRowSelect.test.js +250 -0
  30. package/src/features/notest/useRowState.test.js +178 -0
  31. package/src/features/tests/Visibility.test.tsx +225 -0
  32. package/src/features/tests/__snapshots__/Visibility.test.tsx.snap +390 -0
  33. package/src/features/tests/withSorting.notest.tsx +341 -0
  34. package/src/features/withColumnResizing.ts +281 -0
  35. package/src/features/withPagination.ts +208 -0
  36. package/src/features/withRowSelection.ts +467 -0
  37. package/src/filterTypes.ts +251 -0
  38. package/src/index.tsx +7 -0
  39. package/src/sortTypes.ts +159 -0
  40. package/src/test-utils/makeTestData.ts +41 -0
  41. package/src/tests/__snapshots__/core.test.tsx.snap +148 -0
  42. package/src/tests/core.test.tsx +241 -0
  43. package/src/types.ts +285 -0
  44. package/src/utils/columnFilterRowsFn.ts +162 -0
  45. package/src/utils/expandRowsFn.ts +53 -0
  46. package/src/utils/globalFilterRowsFn.ts +129 -0
  47. package/src/utils/groupRowsFn.ts +196 -0
  48. package/src/utils/sortRowsFn.ts +115 -0
  49. package/src/utils.tsx +243 -0
@@ -0,0 +1,281 @@
1
+ import {
2
+ Cell,
3
+ Column,
4
+ Getter,
5
+ OnChangeFn,
6
+ PropGetterValue,
7
+ ReactTable,
8
+ Updater,
9
+ } from '../types'
10
+ import { functionalUpdate, makeStateUpdater, memo, propGetter } from '../utils'
11
+
12
+ export type VisibilityOptions = {
13
+ onColumnVisibilityChange?: OnChangeFn<VisibilityState>
14
+ enableHiding?: boolean
15
+ }
16
+
17
+ export type VisibilityDefaultOptions = {
18
+ onColumnVisibilityChange: OnChangeFn<VisibilityState>
19
+ }
20
+
21
+ export type VisibilityState = Record<string, boolean>
22
+
23
+ export type VisibilityTableState = {
24
+ columnVisibility: VisibilityState
25
+ }
26
+
27
+ export type VisibilityInstance<
28
+ TData,
29
+ TValue,
30
+ TFilterFns,
31
+ TSortingFns,
32
+ TAggregationFns
33
+ > = {
34
+ getVisibleFlatColumns: () => Column<
35
+ TData,
36
+ TValue,
37
+ TFilterFns,
38
+ TSortingFns,
39
+ TAggregationFns
40
+ >[]
41
+ getVisibleLeafColumns: () => Column<
42
+ TData,
43
+ TValue,
44
+ TFilterFns,
45
+ TSortingFns,
46
+ TAggregationFns
47
+ >[]
48
+ setColumnVisibility: (updater: Updater<VisibilityState>) => void
49
+ toggleColumnVisibility: (columnId: string, value?: boolean) => void
50
+ toggleAllColumnsVisible: (value?: boolean) => void
51
+ getColumnIsVisible: (columId: string) => boolean
52
+ getColumnCanHide: (columnId: string) => boolean
53
+ getIsAllColumnsVisible: () => boolean
54
+ getIsSomeColumnsVisible: () => boolean
55
+ getToggleAllColumnsVisibilityProps: <
56
+ TGetter extends Getter<ToggleAllColumnsVisibilityProps>
57
+ >(
58
+ userProps?: TGetter
59
+ ) => undefined | PropGetterValue<ToggleAllColumnsVisibilityProps, TGetter>
60
+ }
61
+
62
+ type ToggleVisibilityProps = {}
63
+ type ToggleAllColumnsVisibilityProps = {}
64
+
65
+ export type VisibilityColumnDef = {
66
+ enableHiding?: boolean
67
+ defaultCanHide?: boolean
68
+ }
69
+
70
+ export type VisibilityRow<
71
+ TData,
72
+ TValue,
73
+ TFilterFns,
74
+ TSortingFns,
75
+ TAggregationFns
76
+ > = {
77
+ getVisibleCells: () => Cell<
78
+ TData,
79
+ TValue,
80
+ TFilterFns,
81
+ TSortingFns,
82
+ TAggregationFns
83
+ >[]
84
+ }
85
+
86
+ export type VisibilityColumn = {
87
+ getCanHide: () => boolean
88
+ getIsVisible: () => boolean
89
+ toggleVisibility: (value?: boolean) => void
90
+ getToggleVisibilityProps: <TGetter extends Getter<ToggleVisibilityProps>>(
91
+ userProps?: TGetter
92
+ ) => PropGetterValue<ToggleVisibilityProps, TGetter>
93
+ }
94
+
95
+ //
96
+
97
+ export function getInitialState(): VisibilityTableState {
98
+ return {
99
+ columnVisibility: {},
100
+ }
101
+ }
102
+
103
+ export function getDefaultOptions<
104
+ TData,
105
+ TValue,
106
+ TFilterFns,
107
+ TSortingFns,
108
+ TAggregationFns
109
+ >(
110
+ instance: ReactTable<TData, TValue, TFilterFns, TSortingFns, TAggregationFns>
111
+ ): VisibilityDefaultOptions {
112
+ return {
113
+ onColumnVisibilityChange: makeStateUpdater('columnVisibility', instance),
114
+ }
115
+ }
116
+
117
+ export function getDefaultColumn() {
118
+ return {
119
+ defaultIsVisible: true,
120
+ }
121
+ }
122
+
123
+ export function createColumn<
124
+ TData,
125
+ TValue,
126
+ TFilterFns,
127
+ TSortingFns,
128
+ TAggregationFns
129
+ >(
130
+ column: Column<TData, TValue, TFilterFns, TSortingFns, TAggregationFns>,
131
+ instance: ReactTable<TData, TValue, TFilterFns, TSortingFns, TAggregationFns>
132
+ ): VisibilityColumn {
133
+ return {
134
+ getCanHide: () => instance.getColumnCanHide(column.id),
135
+ getIsVisible: () => instance.getColumnIsVisible(column.id),
136
+ toggleVisibility: value =>
137
+ instance.toggleColumnVisibility(column.id, value),
138
+ getToggleVisibilityProps: userProps => {
139
+ const props: ToggleVisibilityProps = {
140
+ type: 'checkbox',
141
+ checked: column.getIsVisible?.(),
142
+ title: 'Toggle Column Visibility',
143
+ onChange: (e: MouseEvent | TouchEvent) => {
144
+ column.toggleVisibility?.((e.target as HTMLInputElement).checked)
145
+ },
146
+ }
147
+
148
+ return propGetter(props, userProps)
149
+ },
150
+ }
151
+ }
152
+
153
+ export function getInstance<
154
+ TData,
155
+ TValue,
156
+ TFilterFns,
157
+ TSortingFns,
158
+ TAggregationFns
159
+ >(
160
+ instance: ReactTable<TData, TValue, TFilterFns, TSortingFns, TAggregationFns>
161
+ ): VisibilityInstance<TData, TValue, TFilterFns, TSortingFns, TAggregationFns> {
162
+ return {
163
+ getVisibleFlatColumns: memo(
164
+ () => [
165
+ instance.getAllFlatColumns(),
166
+ instance
167
+ .getAllFlatColumns()
168
+ .filter(d => d.getIsVisible?.())
169
+ .map(d => d.id)
170
+ .join('_'),
171
+ ],
172
+ allFlatColumns => {
173
+ return allFlatColumns.filter(d => d.getIsVisible?.())
174
+ },
175
+ 'getVisibleFlatColumns',
176
+ instance.options.debug
177
+ ),
178
+
179
+ getVisibleLeafColumns: memo(
180
+ () => [
181
+ instance.getAllLeafColumns(),
182
+ instance
183
+ .getAllLeafColumns()
184
+ .filter(d => d.getIsVisible?.())
185
+ .map(d => d.id)
186
+ .join('_'),
187
+ ],
188
+ allFlatColumns => {
189
+ return allFlatColumns.filter(d => d.getIsVisible?.())
190
+ },
191
+ 'getVisibleLeafColumns',
192
+ instance.options.debug
193
+ ),
194
+
195
+ setColumnVisibility: updater =>
196
+ instance.options.onColumnVisibilityChange?.(
197
+ updater,
198
+ functionalUpdate(updater, instance.getState().columnVisibility)
199
+ ),
200
+
201
+ toggleColumnVisibility: (columnId, value) => {
202
+ if (!columnId) return
203
+
204
+ if (instance.getColumnCanHide(columnId)) {
205
+ instance.setColumnVisibility(old => ({
206
+ ...old,
207
+ [columnId]: value ?? !instance.getColumnIsVisible(columnId),
208
+ }))
209
+ }
210
+ },
211
+
212
+ toggleAllColumnsVisible: value => {
213
+ value = value ?? !instance.getIsAllColumnsVisible()
214
+
215
+ instance.setColumnVisibility(
216
+ instance.getAllLeafColumns().reduce(
217
+ (obj, column) => ({
218
+ ...obj,
219
+ [column.id]: !value ? !column.getCanHide?.() : value,
220
+ }),
221
+ {}
222
+ )
223
+ )
224
+ },
225
+
226
+ getColumnIsVisible: columnId => {
227
+ const column = instance.getColumn(columnId)
228
+
229
+ if (!column) {
230
+ throw new Error()
231
+ }
232
+
233
+ return (
234
+ instance.getState().columnVisibility?.[columnId] ??
235
+ column.defaultIsVisible ??
236
+ true
237
+ )
238
+ },
239
+
240
+ getColumnCanHide: columnId => {
241
+ const column = instance.getColumn(columnId)
242
+
243
+ if (!column) {
244
+ throw new Error()
245
+ }
246
+
247
+ return (
248
+ instance.options.enableHiding ??
249
+ column.enableHiding ??
250
+ column.defaultCanHide ??
251
+ true
252
+ )
253
+ },
254
+
255
+ getIsAllColumnsVisible: () =>
256
+ !instance.getAllLeafColumns().some(column => !column.getIsVisible?.()),
257
+
258
+ getIsSomeColumnsVisible: () =>
259
+ instance.getAllLeafColumns().some(column => column.getIsVisible?.()),
260
+
261
+ getToggleAllColumnsVisibilityProps: userProps => {
262
+ const props: ToggleAllColumnsVisibilityProps = {
263
+ onChange: (e: MouseEvent) => {
264
+ instance.toggleAllColumnsVisible(
265
+ (e.target as HTMLInputElement)?.checked
266
+ )
267
+ },
268
+ type: 'checkbox',
269
+ title: 'Toggle visibility for all columns',
270
+ checked: instance.getIsAllColumnsVisible(),
271
+ indeterminate:
272
+ !instance.getIsAllColumnsVisible() &&
273
+ instance.getIsSomeColumnsVisible()
274
+ ? 'indeterminate'
275
+ : undefined,
276
+ }
277
+
278
+ return propGetter(props, userProps)
279
+ },
280
+ }
281
+ }
@@ -0,0 +1,152 @@
1
+ import React from 'react'
2
+ import { render } from '@testing-library/react'
3
+ import { useTable } from '../../hooks/useTable'
4
+ import { useAbsoluteLayout } from '../useAbsoluteLayout'
5
+
6
+ const data = [
7
+ {
8
+ firstName: 'tanner',
9
+ lastName: 'linsley',
10
+ age: 29,
11
+ visits: 100,
12
+ status: 'In Relationship',
13
+ progress: 50,
14
+ },
15
+ {
16
+ firstName: 'derek',
17
+ lastName: 'perkins',
18
+ age: 30,
19
+ visits: 40,
20
+ status: 'Single',
21
+ progress: 80,
22
+ },
23
+ {
24
+ firstName: 'joe',
25
+ lastName: 'bergevin',
26
+ age: 45,
27
+ visits: 20,
28
+ status: 'Complicated',
29
+ progress: 10,
30
+ },
31
+ ]
32
+
33
+ const defaultColumn = {
34
+ Cell: ({ value, column: { id } }) => `${id}: ${value}`,
35
+ width: 200,
36
+ minWidth: 100,
37
+ maxWidth: 300,
38
+ }
39
+
40
+ function Table({ columns, data }) {
41
+ const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } =
42
+ useTable(
43
+ {
44
+ columns,
45
+ data,
46
+ defaultColumn,
47
+ },
48
+ useAbsoluteLayout
49
+ )
50
+
51
+ return (
52
+ <div {...getTableProps()} className="table">
53
+ <div>
54
+ {headerGroups.map(headerGroup => (
55
+ <div {...headerGroup.getHeaderGroupProps()} className="row">
56
+ {headerGroup.headers.map(column => (
57
+ <div {...column.getHeaderProps()} className="cell header">
58
+ {column.render('Header')}
59
+ </div>
60
+ ))}
61
+ </div>
62
+ ))}
63
+ </div>
64
+
65
+ <div {...getTableBodyProps()}>
66
+ {rows.map(
67
+ (row, i) =>
68
+ prepareRow(row) || (
69
+ <div {...row.getRowProps()} className="row">
70
+ {row.cells.map(cell => {
71
+ return (
72
+ <div {...cell.getCellProps()} className="cell">
73
+ {cell.render('Cell')}
74
+ </div>
75
+ )
76
+ })}
77
+ </div>
78
+ )
79
+ )}
80
+ </div>
81
+ </div>
82
+ )
83
+ }
84
+
85
+ function App() {
86
+ const columns = React.useMemo(
87
+ () => [
88
+ {
89
+ Header: 'Name',
90
+ columns: [
91
+ {
92
+ Header: 'First Name',
93
+ accessor: 'firstName',
94
+ width: 250,
95
+ },
96
+ {
97
+ Header: 'Last Name',
98
+ accessor: 'lastName',
99
+ width: 350,
100
+ },
101
+ ],
102
+ },
103
+ {
104
+ Header: 'Info',
105
+ columns: [
106
+ {
107
+ Header: 'Age',
108
+ accessor: 'age',
109
+ minWidth: 300,
110
+ },
111
+ {
112
+ Header: 'Visits',
113
+ accessor: 'visits',
114
+ maxWidth: 150,
115
+ },
116
+ {
117
+ Header: 'Status',
118
+ accessor: 'status',
119
+ },
120
+ {
121
+ Header: 'Profile Progress',
122
+ accessor: 'progress',
123
+ },
124
+ ],
125
+ },
126
+ ],
127
+ []
128
+ )
129
+
130
+ return <Table columns={columns} data={data} />
131
+ }
132
+
133
+ test('renders a table', () => {
134
+ const rtl = render(<App />)
135
+
136
+ expect(
137
+ rtl.getAllByRole('columnheader').every(d => d.style.position === 'absolute')
138
+ ).toBe(true)
139
+
140
+ expect(
141
+ rtl.getAllByRole('columnheader').map(d => [d.style.left, d.style.width])
142
+ ).toStrictEqual([
143
+ ['0px', '550px'],
144
+ ['550px', '850px'],
145
+ ['0px', '250px'],
146
+ ['250px', '300px'],
147
+ ['550px', '300px'],
148
+ ['850px', '150px'],
149
+ ['1000px', '200px'],
150
+ ['1200px', '200px'],
151
+ ])
152
+ })
@@ -0,0 +1,158 @@
1
+ import React from 'react'
2
+ import { render } from '@testing-library/react'
3
+ import { useTable } from '../../hooks/useTable'
4
+ import { useBlockLayout } from '../useBlockLayout'
5
+
6
+ const data = [
7
+ {
8
+ firstName: 'tanner',
9
+ lastName: 'linsley',
10
+ age: 29,
11
+ visits: 100,
12
+ status: 'In Relationship',
13
+ progress: 50,
14
+ },
15
+ {
16
+ firstName: 'derek',
17
+ lastName: 'perkins',
18
+ age: 30,
19
+ visits: 40,
20
+ status: 'Single',
21
+ progress: 80,
22
+ },
23
+ {
24
+ firstName: 'joe',
25
+ lastName: 'bergevin',
26
+ age: 45,
27
+ visits: 20,
28
+ status: 'Complicated',
29
+ progress: 10,
30
+ },
31
+ ]
32
+
33
+ const defaultColumn = {
34
+ Cell: ({ value, column: { id } }) => `${id}: ${value}`,
35
+ width: 200,
36
+ minWidth: 100,
37
+ maxWidth: 300,
38
+ }
39
+
40
+ function Table({ columns, data }) {
41
+ const { getTableProps, getTableBodyProps, headerGroups, rows, prepareRow } =
42
+ useTable(
43
+ {
44
+ columns,
45
+ data,
46
+ defaultColumn,
47
+ },
48
+ useBlockLayout
49
+ )
50
+
51
+ return (
52
+ <div {...getTableProps()} className="table">
53
+ <div>
54
+ {headerGroups.map(headerGroup => (
55
+ <div {...headerGroup.getHeaderGroupProps()} className="row">
56
+ {headerGroup.headers.map(column => (
57
+ <div {...column.getHeaderProps()} className="cell header">
58
+ {column.render('Header')}
59
+ </div>
60
+ ))}
61
+ </div>
62
+ ))}
63
+ </div>
64
+
65
+ <div {...getTableBodyProps()}>
66
+ {rows.map(
67
+ (row, i) =>
68
+ prepareRow(row) || (
69
+ <div {...row.getRowProps()} className="row">
70
+ {row.cells.map(cell => {
71
+ return (
72
+ <div {...cell.getCellProps()} className="cell">
73
+ {cell.render('Cell')}
74
+ </div>
75
+ )
76
+ })}
77
+ </div>
78
+ )
79
+ )}
80
+ </div>
81
+ </div>
82
+ )
83
+ }
84
+
85
+ function App() {
86
+ const columns = React.useMemo(
87
+ () => [
88
+ {
89
+ Header: 'Name',
90
+ columns: [
91
+ {
92
+ Header: 'First Name',
93
+ accessor: 'firstName',
94
+ width: 250,
95
+ },
96
+ {
97
+ Header: 'Last Name',
98
+ accessor: 'lastName',
99
+ width: 350,
100
+ },
101
+ ],
102
+ },
103
+ {
104
+ Header: 'Info',
105
+ columns: [
106
+ {
107
+ Header: 'Age',
108
+ accessor: 'age',
109
+ minWidth: 300,
110
+ },
111
+ {
112
+ Header: 'Visits',
113
+ accessor: 'visits',
114
+ maxWidth: 150,
115
+ },
116
+ {
117
+ Header: 'Status',
118
+ accessor: 'status',
119
+ },
120
+ {
121
+ Header: 'Profile Progress',
122
+ accessor: 'progress',
123
+ },
124
+ ],
125
+ },
126
+ ],
127
+ []
128
+ )
129
+
130
+ return <Table columns={columns} data={data} />
131
+ }
132
+
133
+ test('renders a table', () => {
134
+ const rtl = render(<App />)
135
+
136
+ expect(
137
+ rtl
138
+ .getAllByRole('columnheader')
139
+ .every(d => d.style.display === 'inline-block')
140
+ ).toBe(true)
141
+
142
+ expect(rtl.getAllByRole('row').every(d => d.style.display === 'flex')).toBe(
143
+ true
144
+ )
145
+
146
+ expect(
147
+ rtl.getAllByRole('columnheader').map(d => d.style.width)
148
+ ).toStrictEqual([
149
+ '550px',
150
+ '850px',
151
+ '250px',
152
+ '300px',
153
+ '300px',
154
+ '150px',
155
+ '200px',
156
+ '200px',
157
+ ])
158
+ })