@patternfly/react-data-view 5.1.0 → 5.1.1

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 (77) hide show
  1. package/dist/cjs/DataView/DataView.d.ts +4 -0
  2. package/dist/cjs/DataView/DataView.js +7 -1
  3. package/dist/cjs/DataViewEventsContext/DataViewEventsContext.d.ts +16 -0
  4. package/dist/cjs/DataViewEventsContext/DataViewEventsContext.js +62 -0
  5. package/dist/cjs/DataViewEventsContext/DataViewEventsContext.test.d.ts +1 -0
  6. package/dist/cjs/DataViewEventsContext/DataViewEventsContext.test.js +72 -0
  7. package/dist/cjs/DataViewEventsContext/index.d.ts +2 -0
  8. package/dist/cjs/DataViewEventsContext/index.js +23 -0
  9. package/dist/cjs/DataViewTable/DataViewTable.d.ts +37 -0
  10. package/dist/cjs/DataViewTable/DataViewTable.js +59 -0
  11. package/dist/cjs/DataViewTable/DataViewTable.test.d.ts +1 -0
  12. package/dist/cjs/DataViewTable/DataViewTable.test.js +27 -0
  13. package/dist/cjs/DataViewTable/index.d.ts +2 -0
  14. package/dist/cjs/DataViewTable/index.js +23 -0
  15. package/dist/cjs/Hooks/selection.d.ts +3 -3
  16. package/dist/cjs/Hooks/selection.js +4 -6
  17. package/dist/cjs/Hooks/selection.test.js +4 -4
  18. package/dist/cjs/InternalContext/InternalContext.d.ts +17 -0
  19. package/dist/cjs/InternalContext/InternalContext.js +35 -0
  20. package/dist/cjs/InternalContext/InternalContext.test.d.ts +1 -0
  21. package/dist/cjs/InternalContext/InternalContext.test.js +55 -0
  22. package/dist/cjs/InternalContext/index.d.ts +2 -0
  23. package/dist/cjs/InternalContext/index.js +23 -0
  24. package/dist/cjs/index.d.ts +6 -0
  25. package/dist/cjs/index.js +11 -2
  26. package/dist/dynamic/DataViewEventsContext/package.json +1 -0
  27. package/dist/dynamic/DataViewTable/package.json +1 -0
  28. package/dist/dynamic/InternalContext/package.json +1 -0
  29. package/dist/esm/DataView/DataView.d.ts +4 -0
  30. package/dist/esm/DataView/DataView.js +7 -1
  31. package/dist/esm/DataViewEventsContext/DataViewEventsContext.d.ts +16 -0
  32. package/dist/esm/DataViewEventsContext/DataViewEventsContext.js +34 -0
  33. package/dist/esm/DataViewEventsContext/DataViewEventsContext.test.d.ts +1 -0
  34. package/dist/esm/DataViewEventsContext/DataViewEventsContext.test.js +67 -0
  35. package/dist/esm/DataViewEventsContext/index.d.ts +2 -0
  36. package/dist/esm/DataViewEventsContext/index.js +2 -0
  37. package/dist/esm/DataViewTable/DataViewTable.d.ts +37 -0
  38. package/dist/esm/DataViewTable/DataViewTable.js +49 -0
  39. package/dist/esm/DataViewTable/DataViewTable.test.d.ts +1 -0
  40. package/dist/esm/DataViewTable/DataViewTable.test.js +22 -0
  41. package/dist/esm/DataViewTable/index.d.ts +2 -0
  42. package/dist/esm/DataViewTable/index.js +2 -0
  43. package/dist/esm/Hooks/selection.d.ts +3 -3
  44. package/dist/esm/Hooks/selection.js +4 -6
  45. package/dist/esm/Hooks/selection.test.js +4 -4
  46. package/dist/esm/InternalContext/InternalContext.d.ts +17 -0
  47. package/dist/esm/InternalContext/InternalContext.js +7 -0
  48. package/dist/esm/InternalContext/InternalContext.test.d.ts +1 -0
  49. package/dist/esm/InternalContext/InternalContext.test.js +50 -0
  50. package/dist/esm/InternalContext/index.d.ts +2 -0
  51. package/dist/esm/InternalContext/index.js +2 -0
  52. package/dist/esm/index.d.ts +6 -0
  53. package/dist/esm/index.js +6 -0
  54. package/package.json +1 -1
  55. package/patternfly-docs/content/extensions/data-view/examples/Components/Components.md +33 -3
  56. package/patternfly-docs/content/extensions/data-view/examples/Components/DataViewTableExample.tsx +47 -0
  57. package/patternfly-docs/content/extensions/data-view/examples/EventsContext/EventsContext.md +32 -0
  58. package/patternfly-docs/content/extensions/data-view/examples/EventsContext/EventsExample.tsx +108 -0
  59. package/patternfly-docs/content/extensions/data-view/examples/Functionality/Functionality.md +6 -5
  60. package/patternfly-docs/content/extensions/data-view/examples/Functionality/PaginationExample.tsx +9 -26
  61. package/patternfly-docs/content/extensions/data-view/examples/Functionality/SelectionExample.tsx +9 -40
  62. package/patternfly-docs/content/extensions/data-view/examples/Layout/Layout.md +1 -0
  63. package/patternfly-docs/content/extensions/data-view/examples/Layout/PredefinedLayoutExample.tsx +14 -45
  64. package/src/DataView/DataView.tsx +14 -2
  65. package/src/DataViewEventsContext/DataViewEventsContext.test.tsx +105 -0
  66. package/src/DataViewEventsContext/DataViewEventsContext.tsx +70 -0
  67. package/src/DataViewEventsContext/index.ts +2 -0
  68. package/src/DataViewTable/DataViewTable.test.tsx +37 -0
  69. package/src/DataViewTable/DataViewTable.tsx +96 -0
  70. package/src/DataViewTable/__snapshots__/DataViewTable.test.tsx.snap +320 -0
  71. package/src/DataViewTable/index.ts +2 -0
  72. package/src/Hooks/selection.test.tsx +5 -5
  73. package/src/Hooks/selection.ts +6 -7
  74. package/src/InternalContext/InternalContext.test.tsx +88 -0
  75. package/src/InternalContext/InternalContext.tsx +35 -0
  76. package/src/InternalContext/index.ts +2 -0
  77. package/src/index.ts +9 -0
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+ import { render } from '@testing-library/react';
3
+ import { DataViewTable } from './DataViewTable';
4
+
5
+ interface Repository {
6
+ name: string;
7
+ branches: string | null;
8
+ prs: string | null;
9
+ workspaces: string;
10
+ lastCommit: string;
11
+ }
12
+
13
+ const repositories: Repository[] = [
14
+ { name: 'one', branches: 'two', prs: 'three', workspaces: 'four', lastCommit: 'five' },
15
+ { name: 'one - 2', branches: null, prs: null, workspaces: 'four - 2', lastCommit: 'five - 2' },
16
+ { name: 'one - 3', branches: 'two - 3', prs: 'three - 3', workspaces: 'four - 3', lastCommit: 'five - 3' },
17
+ { name: 'one - 4', branches: 'two - 4', prs: 'null', workspaces: 'four - 4', lastCommit: 'five - 4' },
18
+ { name: 'one - 5', branches: 'two - 5', prs: 'three - 5', workspaces: 'four - 5', lastCommit: 'five - 5' },
19
+ { name: 'one - 6', branches: 'two - 6', prs: 'three - 6', workspaces: 'four - 6', lastCommit: 'five - 6' }
20
+ ];
21
+
22
+ const rows = repositories.map(repo => ({
23
+ row: Object.values(repo),
24
+ }));
25
+
26
+ const columns = [ 'Repositories', 'Branches', 'Pull requests', 'Workspaces', 'Last commit' ];
27
+
28
+ const ouiaId = 'TableExample';
29
+
30
+ describe('DataViewTable component', () => {
31
+ test('should render correctly', () => {
32
+ const { container } = render(
33
+ <DataViewTable aria-label='Repositories table' ouiaId={ouiaId} columns={columns} rows={rows} />
34
+ );
35
+ expect(container).toMatchSnapshot();
36
+ });
37
+ });
@@ -0,0 +1,96 @@
1
+ import React, { ReactNode } from 'react';
2
+ import {
3
+ Table,
4
+ TableProps,
5
+ Tbody,
6
+ Td,
7
+ TdProps,
8
+ Th,
9
+ Thead,
10
+ ThProps,
11
+ Tr,
12
+ TrProps
13
+ } from '@patternfly/react-table';
14
+ import { useInternalContext } from '../InternalContext';
15
+
16
+ export type DataViewTh = ReactNode | { cell: ReactNode; props?: ThProps };
17
+ export type DataViewTd = ReactNode | { cell: ReactNode; props?: TdProps };
18
+ export type DataViewTr = DataViewTd[] | { row: DataViewTd[], id?: string, props?: TrProps };
19
+
20
+ export const isDataViewThObject = (value: DataViewTh): value is { cell: ReactNode; props?: ThProps } => value != null && typeof value === 'object' && 'cell' in value;
21
+ export const isDataViewTdObject = (value: DataViewTd): value is { cell: ReactNode; props?: TdProps } => value != null && typeof value === 'object' && 'cell' in value;
22
+ export const isDataViewTrObject = (value: DataViewTr): value is { row: DataViewTd[], id?: string } => value != null && typeof value === 'object' && 'row' in value;
23
+
24
+
25
+ export interface DataViewTableProps extends Omit<TableProps, 'onSelect' | 'rows'> {
26
+ /** Columns definition */
27
+ columns: DataViewTh[];
28
+ /** Current page rows */
29
+ rows: DataViewTr[];
30
+ /** Custom OUIA ID */
31
+ ouiaId?: string;
32
+ }
33
+
34
+ export const DataViewTable: React.FC<DataViewTableProps> = ({
35
+ columns,
36
+ rows,
37
+ ouiaId = 'DataViewTable',
38
+ ...props
39
+ }: DataViewTableProps) => {
40
+ const { selection } = useInternalContext();
41
+ const { onSelect, isSelected, isSelectDisabled } = selection ?? {};
42
+
43
+ return (
44
+ <Table aria-label="Data table" ouiaId={ouiaId} {...props}>
45
+ <Thead data-ouia-component-id={`${ouiaId}-thead`}>
46
+ <Tr ouiaId={`${ouiaId}-tr-head`}>
47
+ {onSelect && isSelected && <Th key="row-select" />}
48
+ {columns.map((column, index) => (
49
+ <Th
50
+ key={index}
51
+ {...(isDataViewThObject(column) && (column?.props ?? {}))}
52
+ data-ouia-component-id={`${ouiaId}-th-${index}`}
53
+ >
54
+ {isDataViewThObject(column) ? column.cell : column}
55
+ </Th>
56
+ ))}
57
+ </Tr>
58
+ </Thead>
59
+ <Tbody>
60
+ {rows.map((row, rowIndex) => {
61
+ const rowIsObject = isDataViewTrObject(row);
62
+ return (
63
+ <Tr key={rowIndex} ouiaId={`${ouiaId}-tr-${rowIndex}`} {...(rowIsObject && (row?.props ?? {}))}>
64
+ {onSelect && isSelected && (
65
+ <Td
66
+ key={`select-${rowIndex}`}
67
+ select={{
68
+ rowIndex,
69
+ onSelect: (_event, isSelecting) => {
70
+ onSelect?.(isSelecting, rowIsObject ? row : [ row ])
71
+ },
72
+ isSelected: isSelected?.(row) || false,
73
+ isDisabled: isSelectDisabled?.(row) || false,
74
+ }}
75
+ />
76
+ )}
77
+ {(rowIsObject ? row.row : row).map((cell, colIndex) => {
78
+ const cellIsObject = isDataViewTdObject(cell);
79
+ return (
80
+ <Td
81
+ key={colIndex}
82
+ {...(cellIsObject && (cell?.props ?? {}))}
83
+ data-ouia-component-id={`${ouiaId}-td-${rowIndex}-${colIndex}`}
84
+ >
85
+ {cellIsObject ? cell.cell : cell}
86
+ </Td>
87
+ )
88
+ })}
89
+ </Tr>
90
+ )})}
91
+ </Tbody>
92
+ </Table>
93
+ );
94
+ };
95
+
96
+ export default DataViewTable;
@@ -0,0 +1,320 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`DataViewTable component should render correctly 1`] = `
4
+ <div>
5
+ <table
6
+ aria-label="Repositories table"
7
+ class="pf-v5-c-table pf-m-grid-md"
8
+ data-ouia-component-id="TableExample"
9
+ data-ouia-component-type="PF5/Table"
10
+ data-ouia-safe="true"
11
+ role="grid"
12
+ >
13
+ <thead
14
+ class="pf-v5-c-table__thead"
15
+ data-ouia-component-id="TableExample-thead"
16
+ >
17
+ <tr
18
+ class="pf-v5-c-table__tr"
19
+ data-ouia-component-id="TableExample-tr-head"
20
+ data-ouia-component-type="PF5/TableRow"
21
+ data-ouia-safe="true"
22
+ >
23
+ <th
24
+ class="pf-v5-c-table__th"
25
+ data-ouia-component-id="TableExample-th-0"
26
+ scope="col"
27
+ tabindex="-1"
28
+ >
29
+ Repositories
30
+ </th>
31
+ <th
32
+ class="pf-v5-c-table__th"
33
+ data-ouia-component-id="TableExample-th-1"
34
+ scope="col"
35
+ tabindex="-1"
36
+ >
37
+ Branches
38
+ </th>
39
+ <th
40
+ class="pf-v5-c-table__th"
41
+ data-ouia-component-id="TableExample-th-2"
42
+ scope="col"
43
+ tabindex="-1"
44
+ >
45
+ Pull requests
46
+ </th>
47
+ <th
48
+ class="pf-v5-c-table__th"
49
+ data-ouia-component-id="TableExample-th-3"
50
+ scope="col"
51
+ tabindex="-1"
52
+ >
53
+ Workspaces
54
+ </th>
55
+ <th
56
+ class="pf-v5-c-table__th"
57
+ data-ouia-component-id="TableExample-th-4"
58
+ scope="col"
59
+ tabindex="-1"
60
+ >
61
+ Last commit
62
+ </th>
63
+ </tr>
64
+ </thead>
65
+ <tbody
66
+ class="pf-v5-c-table__tbody"
67
+ role="rowgroup"
68
+ >
69
+ <tr
70
+ class="pf-v5-c-table__tr"
71
+ data-ouia-component-id="TableExample-tr-0"
72
+ data-ouia-component-type="PF5/TableRow"
73
+ data-ouia-safe="true"
74
+ >
75
+ <td
76
+ class="pf-v5-c-table__td"
77
+ data-ouia-component-id="TableExample-td-0-0"
78
+ tabindex="-1"
79
+ >
80
+ one
81
+ </td>
82
+ <td
83
+ class="pf-v5-c-table__td"
84
+ data-ouia-component-id="TableExample-td-0-1"
85
+ tabindex="-1"
86
+ >
87
+ two
88
+ </td>
89
+ <td
90
+ class="pf-v5-c-table__td"
91
+ data-ouia-component-id="TableExample-td-0-2"
92
+ tabindex="-1"
93
+ >
94
+ three
95
+ </td>
96
+ <td
97
+ class="pf-v5-c-table__td"
98
+ data-ouia-component-id="TableExample-td-0-3"
99
+ tabindex="-1"
100
+ >
101
+ four
102
+ </td>
103
+ <td
104
+ class="pf-v5-c-table__td"
105
+ data-ouia-component-id="TableExample-td-0-4"
106
+ tabindex="-1"
107
+ >
108
+ five
109
+ </td>
110
+ </tr>
111
+ <tr
112
+ class="pf-v5-c-table__tr"
113
+ data-ouia-component-id="TableExample-tr-1"
114
+ data-ouia-component-type="PF5/TableRow"
115
+ data-ouia-safe="true"
116
+ >
117
+ <td
118
+ class="pf-v5-c-table__td"
119
+ data-ouia-component-id="TableExample-td-1-0"
120
+ tabindex="-1"
121
+ >
122
+ one - 2
123
+ </td>
124
+ <td
125
+ class="pf-v5-c-table__td"
126
+ data-ouia-component-id="TableExample-td-1-1"
127
+ tabindex="-1"
128
+ />
129
+ <td
130
+ class="pf-v5-c-table__td"
131
+ data-ouia-component-id="TableExample-td-1-2"
132
+ tabindex="-1"
133
+ />
134
+ <td
135
+ class="pf-v5-c-table__td"
136
+ data-ouia-component-id="TableExample-td-1-3"
137
+ tabindex="-1"
138
+ >
139
+ four - 2
140
+ </td>
141
+ <td
142
+ class="pf-v5-c-table__td"
143
+ data-ouia-component-id="TableExample-td-1-4"
144
+ tabindex="-1"
145
+ >
146
+ five - 2
147
+ </td>
148
+ </tr>
149
+ <tr
150
+ class="pf-v5-c-table__tr"
151
+ data-ouia-component-id="TableExample-tr-2"
152
+ data-ouia-component-type="PF5/TableRow"
153
+ data-ouia-safe="true"
154
+ >
155
+ <td
156
+ class="pf-v5-c-table__td"
157
+ data-ouia-component-id="TableExample-td-2-0"
158
+ tabindex="-1"
159
+ >
160
+ one - 3
161
+ </td>
162
+ <td
163
+ class="pf-v5-c-table__td"
164
+ data-ouia-component-id="TableExample-td-2-1"
165
+ tabindex="-1"
166
+ >
167
+ two - 3
168
+ </td>
169
+ <td
170
+ class="pf-v5-c-table__td"
171
+ data-ouia-component-id="TableExample-td-2-2"
172
+ tabindex="-1"
173
+ >
174
+ three - 3
175
+ </td>
176
+ <td
177
+ class="pf-v5-c-table__td"
178
+ data-ouia-component-id="TableExample-td-2-3"
179
+ tabindex="-1"
180
+ >
181
+ four - 3
182
+ </td>
183
+ <td
184
+ class="pf-v5-c-table__td"
185
+ data-ouia-component-id="TableExample-td-2-4"
186
+ tabindex="-1"
187
+ >
188
+ five - 3
189
+ </td>
190
+ </tr>
191
+ <tr
192
+ class="pf-v5-c-table__tr"
193
+ data-ouia-component-id="TableExample-tr-3"
194
+ data-ouia-component-type="PF5/TableRow"
195
+ data-ouia-safe="true"
196
+ >
197
+ <td
198
+ class="pf-v5-c-table__td"
199
+ data-ouia-component-id="TableExample-td-3-0"
200
+ tabindex="-1"
201
+ >
202
+ one - 4
203
+ </td>
204
+ <td
205
+ class="pf-v5-c-table__td"
206
+ data-ouia-component-id="TableExample-td-3-1"
207
+ tabindex="-1"
208
+ >
209
+ two - 4
210
+ </td>
211
+ <td
212
+ class="pf-v5-c-table__td"
213
+ data-ouia-component-id="TableExample-td-3-2"
214
+ tabindex="-1"
215
+ >
216
+ null
217
+ </td>
218
+ <td
219
+ class="pf-v5-c-table__td"
220
+ data-ouia-component-id="TableExample-td-3-3"
221
+ tabindex="-1"
222
+ >
223
+ four - 4
224
+ </td>
225
+ <td
226
+ class="pf-v5-c-table__td"
227
+ data-ouia-component-id="TableExample-td-3-4"
228
+ tabindex="-1"
229
+ >
230
+ five - 4
231
+ </td>
232
+ </tr>
233
+ <tr
234
+ class="pf-v5-c-table__tr"
235
+ data-ouia-component-id="TableExample-tr-4"
236
+ data-ouia-component-type="PF5/TableRow"
237
+ data-ouia-safe="true"
238
+ >
239
+ <td
240
+ class="pf-v5-c-table__td"
241
+ data-ouia-component-id="TableExample-td-4-0"
242
+ tabindex="-1"
243
+ >
244
+ one - 5
245
+ </td>
246
+ <td
247
+ class="pf-v5-c-table__td"
248
+ data-ouia-component-id="TableExample-td-4-1"
249
+ tabindex="-1"
250
+ >
251
+ two - 5
252
+ </td>
253
+ <td
254
+ class="pf-v5-c-table__td"
255
+ data-ouia-component-id="TableExample-td-4-2"
256
+ tabindex="-1"
257
+ >
258
+ three - 5
259
+ </td>
260
+ <td
261
+ class="pf-v5-c-table__td"
262
+ data-ouia-component-id="TableExample-td-4-3"
263
+ tabindex="-1"
264
+ >
265
+ four - 5
266
+ </td>
267
+ <td
268
+ class="pf-v5-c-table__td"
269
+ data-ouia-component-id="TableExample-td-4-4"
270
+ tabindex="-1"
271
+ >
272
+ five - 5
273
+ </td>
274
+ </tr>
275
+ <tr
276
+ class="pf-v5-c-table__tr"
277
+ data-ouia-component-id="TableExample-tr-5"
278
+ data-ouia-component-type="PF5/TableRow"
279
+ data-ouia-safe="true"
280
+ >
281
+ <td
282
+ class="pf-v5-c-table__td"
283
+ data-ouia-component-id="TableExample-td-5-0"
284
+ tabindex="-1"
285
+ >
286
+ one - 6
287
+ </td>
288
+ <td
289
+ class="pf-v5-c-table__td"
290
+ data-ouia-component-id="TableExample-td-5-1"
291
+ tabindex="-1"
292
+ >
293
+ two - 6
294
+ </td>
295
+ <td
296
+ class="pf-v5-c-table__td"
297
+ data-ouia-component-id="TableExample-td-5-2"
298
+ tabindex="-1"
299
+ >
300
+ three - 6
301
+ </td>
302
+ <td
303
+ class="pf-v5-c-table__td"
304
+ data-ouia-component-id="TableExample-td-5-3"
305
+ tabindex="-1"
306
+ >
307
+ four - 6
308
+ </td>
309
+ <td
310
+ class="pf-v5-c-table__td"
311
+ data-ouia-component-id="TableExample-td-5-4"
312
+ tabindex="-1"
313
+ >
314
+ five - 6
315
+ </td>
316
+ </tr>
317
+ </tbody>
318
+ </table>
319
+ </div>
320
+ `;
@@ -0,0 +1,2 @@
1
+ export { default } from './DataViewTable';
2
+ export * from './DataViewTable';
@@ -4,7 +4,7 @@ import { useDataViewSelection } from './selection';
4
4
 
5
5
  describe('useDataViewSelection', () => {
6
6
  it('should get initial state correctly - no initialSelected', () => {
7
- const { result } = renderHook(() => useDataViewSelection({}))
7
+ const { result } = renderHook(() => useDataViewSelection({ matchOption: (a, b) => a.id === b.id }))
8
8
  expect(result.current).toEqual({
9
9
  selected: [],
10
10
  onSelect: expect.any(Function),
@@ -14,7 +14,7 @@ describe('useDataViewSelection', () => {
14
14
 
15
15
  it('should get initial state correctly - with initialSelected', () => {
16
16
  const initialSelected = [ { id: 1, name: 'test1' } ];
17
- const { result } = renderHook(() => useDataViewSelection({ initialSelected }))
17
+ const { result } = renderHook(() => useDataViewSelection({ initialSelected, matchOption: (a, b) => a.id === b.id }))
18
18
  expect(result.current).toEqual({
19
19
  selected: initialSelected,
20
20
  onSelect: expect.any(Function),
@@ -24,7 +24,7 @@ describe('useDataViewSelection', () => {
24
24
 
25
25
  it('should select items correctly - objects', async () => {
26
26
  const initialSelected = [ { id: 1, name: 'test1' } ];
27
- const { result } = renderHook(() => useDataViewSelection({ initialSelected }))
27
+ const { result } = renderHook(() => useDataViewSelection({ initialSelected, matchOption: (a, b) => a.id === b.id }))
28
28
 
29
29
  await act(async () => {
30
30
  result.current.onSelect(true, { id: 2, name: 'test2' });
@@ -34,7 +34,7 @@ describe('useDataViewSelection', () => {
34
34
 
35
35
  it('should deselect items correctly - strings', async () => {
36
36
  const initialSelected = [ 'test1', 'test2' ];
37
- const { result } = renderHook(() => useDataViewSelection({ initialSelected }))
37
+ const { result } = renderHook(() => useDataViewSelection({ initialSelected, matchOption: (a, b) => a === b }))
38
38
 
39
39
  await act(async () => {
40
40
  result.current.onSelect(false, 'test2');
@@ -44,7 +44,7 @@ describe('useDataViewSelection', () => {
44
44
 
45
45
  it('should check if item is selected correctly - objects', () => {
46
46
  const initialSelected = [ { id: 1, name: 'test1' }, { id: 2, name: 'test2' } ];
47
- const { result } = renderHook(() => useDataViewSelection({ initialSelected, matchOption: (a,b) => a.id === b.id }))
47
+ const { result } = renderHook(() => useDataViewSelection({ initialSelected, matchOption: (a, b) => a.id === b.id }))
48
48
 
49
49
  expect(result.current.isSelected({ id: 1, name: 'test1' })).toBe(true);
50
50
  expect(result.current.isSelected({ id: 3, name: 'test2' })).toBe(false);
@@ -2,18 +2,17 @@
2
2
  import { useState } from "react";
3
3
 
4
4
  export interface UseDataViewSelectionProps {
5
+ /** Function to compare items when checking if item is selected */
6
+ matchOption: (item: any, another: any) => boolean;
5
7
  /** Array of initially selected entries */
6
8
  initialSelected?: (any)[];
7
- /** Function to compare items when checking if entry is selected */
8
- matchOption?: (item: any, another: any) => boolean;
9
9
  }
10
10
 
11
- export const useDataViewSelection = (props: UseDataViewSelectionProps) => {
12
- const [ selected, setSelected ] = useState<any[]>(props.initialSelected ?? []);
13
- const matchOption = props.matchOption ? props.matchOption : (option, another) => (option === another);
11
+ export const useDataViewSelection = ({ matchOption, initialSelected = [] }: UseDataViewSelectionProps) => {
12
+ const [ selected, setSelected ] = useState<any[]>(initialSelected);
14
13
 
15
14
  const onSelect = (isSelecting: boolean, items?: any[] | any) => {
16
- isSelecting ?
15
+ isSelecting && items ?
17
16
  setSelected(prev => {
18
17
  const newSelectedItems = [ ...prev ];
19
18
  (Array.isArray(items) ? items : [ items ]).forEach(newItem => !prev.some(prevItem => matchOption(prevItem, newItem)) && newSelectedItems.push(newItem));
@@ -22,7 +21,7 @@ export const useDataViewSelection = (props: UseDataViewSelectionProps) => {
22
21
  : setSelected(items ? prev => prev.filter(prevSelected => !(Array.isArray(items) ? items : [ items ]).some(item => matchOption(item, prevSelected))) : []);
23
22
  };
24
23
 
25
- const isSelected = (item: any): boolean => props?.matchOption ? Boolean(selected.find(selected => matchOption(selected, item))) : selected.includes(item);
24
+ const isSelected = (item: any): boolean => Boolean(selected.find(selected => matchOption(selected, item)));
26
25
 
27
26
  return {
28
27
  selected,
@@ -0,0 +1,88 @@
1
+ import React from 'react';
2
+ import { render, fireEvent } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
4
+ import { InternalContextProvider, useInternalContext, DataViewSelection } from './InternalContext';
5
+
6
+ describe('InternalContext', () => {
7
+ const mockSelection: DataViewSelection = {
8
+ onSelect: jest.fn(),
9
+ isSelected: jest.fn(),
10
+ isSelectDisabled: jest.fn(),
11
+ };
12
+
13
+ test('should provide context value and allow consuming it', () => {
14
+ const TestComponent = () => {
15
+ const { selection } = useInternalContext();
16
+
17
+ return (
18
+ <div>
19
+ <button onClick={() => selection?.onSelect(true, [ 'item1' ])}>Select item</button>
20
+ <span>{selection?.isSelected('item1') ? 'Selected' : 'Not selected'}</span>
21
+ </div>
22
+ );
23
+ };
24
+
25
+ const { getByText } = render(
26
+ <InternalContextProvider selection={mockSelection}>
27
+ <TestComponent />
28
+ </InternalContextProvider>
29
+ );
30
+
31
+ fireEvent.click(getByText('Select item'));
32
+ expect(mockSelection.onSelect).toHaveBeenCalledWith(true, [ 'item1' ]);
33
+ });
34
+
35
+ test('should handle selection state correctly', () => {
36
+ const mockSelectionState = {
37
+ ...mockSelection,
38
+ isSelected: jest.fn((item) => item === 'item1'),
39
+ };
40
+
41
+ const TestComponent = () => {
42
+ const { selection } = useInternalContext();
43
+
44
+ return (
45
+ <div>
46
+ <span>{selection?.isSelected('item1') ? 'Item 1 is selected' : 'Item 1 is not selected'}</span>
47
+ <span>{selection?.isSelected('item2') ? 'Item 2 is selected' : 'Item 2 is not selected'}</span>
48
+ </div>
49
+ );
50
+ };
51
+
52
+ const { getByText } = render(
53
+ <InternalContextProvider selection={mockSelectionState}>
54
+ <TestComponent />
55
+ </InternalContextProvider>
56
+ );
57
+
58
+ expect(getByText('Item 1 is selected')).toBeInTheDocument();
59
+ expect(getByText('Item 2 is not selected')).toBeInTheDocument();
60
+ });
61
+
62
+ test('should handle selection disabled correctly', () => {
63
+ const mockSelectionWithDisabled = {
64
+ ...mockSelection,
65
+ isSelectDisabled: jest.fn((item) => item === 'item3'),
66
+ };
67
+
68
+ const TestComponent = () => {
69
+ const { selection } = useInternalContext();
70
+
71
+ return (
72
+ <div>
73
+ <span>{selection?.isSelectDisabled?.('item3') ? 'Item 3 is disabled' : 'Item 3 is enabled'}</span>
74
+ <span>{selection?.isSelectDisabled?.('item1') ? 'Item 1 is disabled' : 'Item 1 is enabled'}</span>
75
+ </div>
76
+ );
77
+ };
78
+
79
+ const { getByText } = render(
80
+ <InternalContextProvider selection={mockSelectionWithDisabled}>
81
+ <TestComponent />
82
+ </InternalContextProvider>
83
+ );
84
+
85
+ expect(getByText('Item 3 is disabled')).toBeInTheDocument();
86
+ expect(getByText('Item 1 is enabled')).toBeInTheDocument();
87
+ });
88
+ });
@@ -0,0 +1,35 @@
1
+ import React, { createContext, PropsWithChildren, useContext } from 'react';
2
+
3
+ export interface DataViewSelection {
4
+ /** Called when the selection of items changes */
5
+ onSelect: (isSelecting: boolean, items?: any[] | any) => void; // eslint-disable-line @typescript-eslint/no-explicit-any
6
+ /** Checks if a specific item is currently selected */
7
+ isSelected: (item: any) => boolean; // eslint-disable-line @typescript-eslint/no-explicit-any
8
+ /** Determines if selection is disabled for a given item */
9
+ isSelectDisabled?: (item: any) => boolean; // eslint-disable-line @typescript-eslint/no-explicit-any
10
+ }
11
+
12
+ export interface InternalContextValue {
13
+ selection?: DataViewSelection;
14
+ }
15
+
16
+ export const InternalContext = createContext<InternalContextValue>({
17
+ selection: undefined
18
+ });
19
+
20
+ export type InternalProviderProps = PropsWithChildren<InternalContextValue>
21
+
22
+ export const InternalContextProvider: React.FC<InternalProviderProps> = ({
23
+ children,
24
+ selection
25
+ }) => (
26
+ <InternalContext.Provider
27
+ value={{ selection }}
28
+ >
29
+ {children}
30
+ </InternalContext.Provider>
31
+ );
32
+
33
+ export const useInternalContext = () => useContext(InternalContext);
34
+
35
+ export default InternalContext;
@@ -0,0 +1,2 @@
1
+ export { default } from './InternalContext';
2
+ export * from './InternalContext';
package/src/index.ts CHANGED
@@ -1,8 +1,17 @@
1
1
  // this file is autogenerated by generate-index.js, modifying it manually will have no effect
2
+
3
+ export { default as InternalContext } from './InternalContext';
4
+ export * from './InternalContext';
2
5
  export * from './Hooks';
3
6
 
4
7
  export { default as DataViewToolbar } from './DataViewToolbar';
5
8
  export * from './DataViewToolbar';
6
9
 
10
+ export { default as DataViewTable } from './DataViewTable';
11
+ export * from './DataViewTable';
12
+
13
+ export { default as DataViewEventsContext } from './DataViewEventsContext';
14
+ export * from './DataViewEventsContext';
15
+
7
16
  export { default as DataView } from './DataView';
8
17
  export * from './DataView';