@openedx/paragon 22.16.0 → 22.16.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/dist/DataTable/hooks.js +48 -2
- package/dist/DataTable/hooks.js.map +1 -1
- package/dist/DataTable/index.js +18 -9
- package/dist/DataTable/index.js.map +1 -1
- package/dist/DataTable/selection/ControlledSelectionStatus.js +7 -17
- package/dist/DataTable/selection/ControlledSelectionStatus.js.map +1 -1
- package/dist/DataTable/selection/data/actions.js +5 -0
- package/dist/DataTable/selection/data/reducer.js +12 -1
- package/dist/Form/_index.scss +2 -2
- package/dist/paragon.css +1 -1
- package/package.json +1 -1
- package/src/DataTable/README.md +111 -78
- package/src/DataTable/hooks.jsx +55 -2
- package/src/DataTable/index.jsx +28 -16
- package/src/DataTable/selection/ControlledSelectionStatus.jsx +8 -23
- package/src/DataTable/selection/data/actions.js +5 -0
- package/src/DataTable/selection/data/reducer.js +12 -1
- package/src/DataTable/selection/tests/ControlledSelectionStatus.test.jsx +4 -23
- package/src/DataTable/selection/tests/reducer.test.js +4 -0
- package/src/DataTable/tests/DataTable.test.jsx +99 -3
- package/src/Form/_index.scss +2 -2
package/package.json
CHANGED
package/src/DataTable/README.md
CHANGED
|
@@ -167,76 +167,104 @@ To enable proper selection behavior with backend pagination (i.e., when ``isSele
|
|
|
167
167
|
|
|
168
168
|
```jsx live
|
|
169
169
|
() => {
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
release_date: 1994,
|
|
229
|
-
rt_score: 78,
|
|
230
|
-
},
|
|
231
|
-
],
|
|
170
|
+
const DEFAULT_PAGE_SIZE = 3;
|
|
171
|
+
const DATA = [
|
|
172
|
+
{
|
|
173
|
+
id: '2baf70d1-42bb-4437-b551-e5fed5a87abe',
|
|
174
|
+
title: 'Castle in the Sky',
|
|
175
|
+
director: 'Hayao Miyazaki',
|
|
176
|
+
producer: 'Isao Takahata',
|
|
177
|
+
release_date: 1986,
|
|
178
|
+
rt_score: 95,
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
id: '12cfb892-aac0-4c5b-94af-521852e46d6a',
|
|
182
|
+
title: 'Grave of the Fireflies',
|
|
183
|
+
director: 'Isao Takahata',
|
|
184
|
+
producer: 'Toru Hara',
|
|
185
|
+
release_date: 1988,
|
|
186
|
+
rt_score: 97,
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
id: '58611129-2dbc-4a81-a72f-77ddfc1b1b49',
|
|
190
|
+
title: 'My Neighbor Totoro',
|
|
191
|
+
director: 'Hayao Miyazaki',
|
|
192
|
+
producer: 'Hayao Miyazaki',
|
|
193
|
+
release_date: 1988,
|
|
194
|
+
rt_score: 93,
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
id: 'ea660b10-85c4-4ae3-8a5f-41cea3648e3e',
|
|
198
|
+
title: 'Kiki\'s Delivery Service',
|
|
199
|
+
director: 'Hayao Miyazaki',
|
|
200
|
+
producer: 'Hayao Miyazaki',
|
|
201
|
+
release_date: 1989,
|
|
202
|
+
rt_score: 96,
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
id: '4e236f34-b981-41c3-8c65-f8c9000b94e7',
|
|
206
|
+
title: 'Only Yesterday',
|
|
207
|
+
director: 'Isao Takahata',
|
|
208
|
+
producer: 'Toshio Suzuki',
|
|
209
|
+
release_date: 1991,
|
|
210
|
+
rt_score: 100,
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
id: 'ebbb6b7c-945c-41ee-a792-de0e43191bd8',
|
|
214
|
+
title: 'Porco Rosso',
|
|
215
|
+
director: 'Hayao Miyazaki',
|
|
216
|
+
producer: 'Toshio Suzuki',
|
|
217
|
+
release_date: 1992,
|
|
218
|
+
rt_score: 94,
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
id: '1b67aa9a-2e4a-45af-ac98-64d6ad15b16c',
|
|
222
|
+
title: 'Pom Poko',
|
|
223
|
+
director: 'Isao Takahata',
|
|
224
|
+
producer: 'Toshio Suzuki',
|
|
225
|
+
release_date: 1994,
|
|
226
|
+
rt_score: 78,
|
|
227
|
+
},
|
|
232
228
|
];
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
(
|
|
229
|
+
|
|
230
|
+
function paginateData(data, pageSize = DEFAULT_PAGE_SIZE) {
|
|
231
|
+
if (pageSize <= 0) {
|
|
232
|
+
throw new Error('Invalid page size');
|
|
233
|
+
}
|
|
234
|
+
const pages = [];
|
|
235
|
+
for (let i = 0; i < data.length; i += pageSize) {
|
|
236
|
+
pages.push(data.slice(i, i + pageSize));
|
|
237
|
+
}
|
|
238
|
+
return pages;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function filterData(data, filters) {
|
|
242
|
+
return data.filter((item) => filters.every(filter => (
|
|
243
|
+
String(item[filter.id]).toLowerCase().includes(String(filter.value).toLowerCase())
|
|
244
|
+
)));
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const useDebouncedFetchData = (setData, setTotalItems, setTotalPages, setIsLoading) => useCallback(
|
|
248
|
+
debounce((args) => {
|
|
249
|
+
if (!args) { return; }
|
|
250
|
+
setIsLoading(true);
|
|
251
|
+
|
|
236
252
|
setTimeout(() => {
|
|
237
|
-
|
|
253
|
+
// Filter the data based on the current filters
|
|
254
|
+
const filteredData = filterData(DATA, args.filters);
|
|
255
|
+
|
|
256
|
+
// Paginate the filtered data
|
|
257
|
+
const paginatedData = paginateData(filteredData, args.pageSize);
|
|
258
|
+
const currentPageData = paginatedData[args.pageIndex] || [];
|
|
259
|
+
|
|
260
|
+
// Update the state with the paginated data, total items, and total pages
|
|
261
|
+
setData(currentPageData);
|
|
262
|
+
setTotalItems(filteredData.length);
|
|
263
|
+
setTotalPages(paginatedData.length);
|
|
264
|
+
|
|
265
|
+
setIsLoading(false);
|
|
238
266
|
}, 1000);
|
|
239
|
-
},
|
|
267
|
+
}, 300),
|
|
240
268
|
[],
|
|
241
269
|
);
|
|
242
270
|
|
|
@@ -247,12 +275,18 @@ To enable proper selection behavior with backend pagination (i.e., when ``isSele
|
|
|
247
275
|
disableSortBy: true,
|
|
248
276
|
};
|
|
249
277
|
|
|
278
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
279
|
+
const [data, setData] = useState([]);
|
|
280
|
+
const [totalItems, setTotalItems] = useState(0);
|
|
281
|
+
const [totalPages, setTotalPages] = useState(1);
|
|
282
|
+
|
|
283
|
+
const fetchData = useDebouncedFetchData(setData, setTotalItems, setTotalPages, setIsLoading);
|
|
284
|
+
|
|
250
285
|
const DownloadCSVAction = ({ as: Component, selectedFlatRows, ...rest }) => (
|
|
251
286
|
<Component onClick={() => console.log('Download CSV', selectedFlatRows, rest)}>
|
|
252
287
|
Download CSV
|
|
253
288
|
</Component>
|
|
254
289
|
);
|
|
255
|
-
|
|
256
290
|
const ClearAction = ({ as: Component, tableInstance }) => (
|
|
257
291
|
<Component
|
|
258
292
|
variant="danger"
|
|
@@ -264,9 +298,14 @@ To enable proper selection behavior with backend pagination (i.e., when ``isSele
|
|
|
264
298
|
Clear Selection
|
|
265
299
|
</Component>
|
|
266
300
|
);
|
|
267
|
-
|
|
301
|
+
|
|
268
302
|
return (
|
|
269
303
|
<DataTable
|
|
304
|
+
isLoading={isLoading}
|
|
305
|
+
bulkActions={[
|
|
306
|
+
<DownloadCSVAction />,
|
|
307
|
+
<ClearAction />,
|
|
308
|
+
]}
|
|
270
309
|
isSelectable
|
|
271
310
|
manualSelectColumn={selectColumn}
|
|
272
311
|
SelectionStatusComponent={DataTable.ControlledSelectionStatus}
|
|
@@ -275,17 +314,15 @@ To enable proper selection behavior with backend pagination (i.e., when ``isSele
|
|
|
275
314
|
defaultColumnValues={{ Filter: TextFilter }}
|
|
276
315
|
isPaginated
|
|
277
316
|
manualPagination
|
|
278
|
-
isSortable
|
|
279
|
-
manualSortBy
|
|
280
317
|
initialState={{
|
|
281
318
|
pageSize: 3,
|
|
282
|
-
pageIndex: 0
|
|
319
|
+
pageIndex: 0,
|
|
283
320
|
}}
|
|
284
321
|
initialTableOptions={{
|
|
285
322
|
getRowId: row => row.id,
|
|
286
323
|
}}
|
|
287
|
-
itemCount={
|
|
288
|
-
pageCount={
|
|
324
|
+
itemCount={totalItems}
|
|
325
|
+
pageCount={totalPages}
|
|
289
326
|
fetchData={fetchData}
|
|
290
327
|
data={data}
|
|
291
328
|
columns={[
|
|
@@ -302,10 +339,6 @@ To enable proper selection behavior with backend pagination (i.e., when ``isSele
|
|
|
302
339
|
accessor: 'release_date',
|
|
303
340
|
},
|
|
304
341
|
]}
|
|
305
|
-
bulkActions={[
|
|
306
|
-
<DownloadCSVAction />,
|
|
307
|
-
<ClearAction />,
|
|
308
|
-
]}
|
|
309
342
|
/>
|
|
310
343
|
);
|
|
311
344
|
}
|
package/src/DataTable/hooks.jsx
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { useContext } from 'react';
|
|
1
|
+
import { useContext, useEffect } from 'react';
|
|
2
2
|
import DataTableContext from './DataTableContext';
|
|
3
|
-
import { clearSelectionAction } from './selection/data/actions';
|
|
3
|
+
import { clearSelectionAction, setSelectedRowsAction, toggleIsEntireTableSelected } from './selection/data/actions';
|
|
4
|
+
import { getRowIds, getUnselectedPageRows } from './selection/data/helpers';
|
|
4
5
|
|
|
5
6
|
export const useRows = () => {
|
|
6
7
|
const {
|
|
@@ -39,3 +40,55 @@ export const useSelectionActions = (
|
|
|
39
40
|
clearSelection,
|
|
40
41
|
};
|
|
41
42
|
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Provides business logic around managing selection state, notably for controlled
|
|
46
|
+
* selections with API-driven data.
|
|
47
|
+
*
|
|
48
|
+
*/
|
|
49
|
+
export const useDataTableSelections = ({
|
|
50
|
+
selections,
|
|
51
|
+
selectionsDispatch,
|
|
52
|
+
itemCount,
|
|
53
|
+
selectedRows,
|
|
54
|
+
page,
|
|
55
|
+
isAllPageRowsSelected,
|
|
56
|
+
}) => {
|
|
57
|
+
// If "Select all" is explicitly opted in by the user, ensure that all unselected rows are selected
|
|
58
|
+
// when the user navigates to a new page.
|
|
59
|
+
useEffect(
|
|
60
|
+
() => {
|
|
61
|
+
if (selections.isSelectAllEnabled && (itemCount > selectedRows.length || !isAllPageRowsSelected)) {
|
|
62
|
+
const selectedRowIds = getRowIds(selectedRows);
|
|
63
|
+
const unselectedPageRows = getUnselectedPageRows(selectedRowIds, page);
|
|
64
|
+
if (unselectedPageRows.length) {
|
|
65
|
+
selectionsDispatch(setSelectedRowsAction(unselectedPageRows, itemCount));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
[selectedRows, itemCount, page, selectionsDispatch, selections.isSelectAllEnabled, isAllPageRowsSelected],
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
// When `selections.isSelectAllEnabled` is true, ensure `selections.isEntireTableSelected` is true.
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
if (selections.isSelectAllEnabled && !selections.isEntireTableSelected) {
|
|
75
|
+
selectionsDispatch(toggleIsEntireTableSelected());
|
|
76
|
+
}
|
|
77
|
+
}, [selectionsDispatch, selections.isEntireTableSelected, selections.isSelectAllEnabled]);
|
|
78
|
+
|
|
79
|
+
// When `selections.isSelectAllEnabled` differs from `selections.isEntireTableSelected` and
|
|
80
|
+
// `isAllPageRowsSelected` matches `selections.isSelectAllEnabled`, toggle `selections.isEntireTableSelected`.
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (!selections.isSelectAllEnabled && selections.isEntireTableSelected && !isAllPageRowsSelected) {
|
|
83
|
+
selectionsDispatch(toggleIsEntireTableSelected());
|
|
84
|
+
}
|
|
85
|
+
if (selections.isSelectAllEnabled && !selections.isEntireTableSelected && isAllPageRowsSelected) {
|
|
86
|
+
selectionsDispatch(toggleIsEntireTableSelected());
|
|
87
|
+
}
|
|
88
|
+
}, [
|
|
89
|
+
selectionsDispatch,
|
|
90
|
+
isAllPageRowsSelected,
|
|
91
|
+
selections.isEntireTableSelected,
|
|
92
|
+
selections.isSelectAllEnabled,
|
|
93
|
+
]);
|
|
94
|
+
};
|
package/src/DataTable/index.jsx
CHANGED
|
@@ -33,8 +33,10 @@ import DataTableLayout from './DataTableLayout';
|
|
|
33
33
|
import ExpandAll from './ExpandAll';
|
|
34
34
|
import ExpandRow from './ExpandRow';
|
|
35
35
|
|
|
36
|
-
import { useSelectionActions } from './hooks';
|
|
37
|
-
import selectionsReducer, {
|
|
36
|
+
import { useDataTableSelections, useSelectionActions } from './hooks';
|
|
37
|
+
import selectionsReducer, {
|
|
38
|
+
initialState as initialSelectionsState,
|
|
39
|
+
} from './selection/data/reducer';
|
|
38
40
|
|
|
39
41
|
function DataTable({
|
|
40
42
|
columns, data, defaultColumnValues, additionalColumns, isSelectable,
|
|
@@ -79,12 +81,13 @@ function DataTable({
|
|
|
79
81
|
selectedRowIds: {},
|
|
80
82
|
};
|
|
81
83
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Note: We override the `toggleRowSelected` action from react-table
|
|
86
|
+
* because we need to preserve the order of the selected rows.
|
|
87
|
+
* while `selectedRowIds` is an object that contains the selected rows as key-value pairs,
|
|
88
|
+
* it does not maintain the order of selection. Therefore, we have added the `selectedRowsOrdered` property
|
|
89
|
+
* to keep track of the order in which the rows were selected.
|
|
90
|
+
*/
|
|
88
91
|
case 'toggleRowSelected': {
|
|
89
92
|
const rowIndex = parseInt(action.id, 10);
|
|
90
93
|
const { selectedRowsOrdered = [] } = previousState;
|
|
@@ -185,6 +188,15 @@ function DataTable({
|
|
|
185
188
|
|
|
186
189
|
const selectionActions = useSelectionActions(instance, controlledTableSelections);
|
|
187
190
|
|
|
191
|
+
useDataTableSelections({
|
|
192
|
+
selections,
|
|
193
|
+
selectionsDispatch,
|
|
194
|
+
itemCount,
|
|
195
|
+
selectedRows,
|
|
196
|
+
page: instance.page,
|
|
197
|
+
isAllPageRowsSelected: instance.isAllPageRowsSelected,
|
|
198
|
+
});
|
|
199
|
+
|
|
188
200
|
const enhancedInstance = {
|
|
189
201
|
...instance,
|
|
190
202
|
manualFilters,
|
|
@@ -216,12 +228,12 @@ function DataTable({
|
|
|
216
228
|
})}
|
|
217
229
|
>
|
|
218
230
|
{children || (
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
231
|
+
<>
|
|
232
|
+
<TableControlBar />
|
|
233
|
+
<Table />
|
|
234
|
+
<EmptyTableComponent content="No results found" />
|
|
235
|
+
<TableFooter />
|
|
236
|
+
</>
|
|
225
237
|
)}
|
|
226
238
|
</div>
|
|
227
239
|
</DataTableLayout>
|
|
@@ -328,7 +340,7 @@ DataTable.propTypes = {
|
|
|
328
340
|
Cell: PropTypes.oneOfType([PropTypes.elementType, PropTypes.node]),
|
|
329
341
|
})),
|
|
330
342
|
/** Function that will fetch table data. Called when page size, page index or filters change.
|
|
331
|
-
|
|
343
|
+
* Meant to be used with manual filters and pagination */
|
|
332
344
|
fetchData: PropTypes.func,
|
|
333
345
|
/** Initial state passed to react-table's documentation https://github.com/TanStack/table/blob/v7/docs/src/pages/docs/api/useTable.md */
|
|
334
346
|
initialState: PropTypes.shape({
|
|
@@ -340,7 +352,7 @@ DataTable.propTypes = {
|
|
|
340
352
|
selectedRowsOrdered: PropTypes.arrayOf(PropTypes.number),
|
|
341
353
|
}),
|
|
342
354
|
/** Table options passed to react-table's useTable hook. Will override some options passed in to DataTable, such
|
|
343
|
-
|
|
355
|
+
as: data, columns, defaultColumn, manualFilters, manualPagination, manualSortBy, and initialState */
|
|
344
356
|
initialTableOptions: PropTypes.shape({}),
|
|
345
357
|
/** Actions to be performed on the table. Called with the table instance. Not displayed if rows are selected. */
|
|
346
358
|
itemCount: PropTypes.number.isRequired,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useContext
|
|
1
|
+
import React, { useContext } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
|
|
4
4
|
import DataTableContext from '../DataTableContext';
|
|
@@ -7,34 +7,19 @@ import BaseSelectionStatus from './BaseSelectionStatus';
|
|
|
7
7
|
import {
|
|
8
8
|
clearSelectionAction,
|
|
9
9
|
setSelectAllRowsAllPagesAction,
|
|
10
|
-
setSelectedRowsAction,
|
|
11
10
|
} from './data/actions';
|
|
12
|
-
import {
|
|
13
|
-
getUnselectedPageRows,
|
|
14
|
-
getRowIds,
|
|
15
|
-
} from './data/helpers';
|
|
16
11
|
|
|
17
12
|
function ControlledSelectionStatus({ className, clearSelectionText }) {
|
|
18
13
|
const {
|
|
19
14
|
itemCount,
|
|
20
15
|
page,
|
|
21
|
-
controlledTableSelections: [
|
|
16
|
+
controlledTableSelections: [
|
|
17
|
+
{ selectedRows, isSelectAllEnabled },
|
|
18
|
+
selectionsDispatch,
|
|
19
|
+
],
|
|
22
20
|
} = useContext(DataTableContext);
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
() => {
|
|
26
|
-
if (isEntireTableSelected) {
|
|
27
|
-
const selectedRowIds = getRowIds(selectedRows);
|
|
28
|
-
const unselectedPageRows = getUnselectedPageRows(selectedRowIds, page);
|
|
29
|
-
if (unselectedPageRows.length) {
|
|
30
|
-
dispatch(setSelectedRowsAction(unselectedPageRows, itemCount));
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
},
|
|
34
|
-
[isEntireTableSelected, selectedRows, itemCount, page, dispatch],
|
|
35
|
-
);
|
|
36
|
-
|
|
37
|
-
const numSelectedRows = isEntireTableSelected ? itemCount : selectedRows.length;
|
|
22
|
+
const numSelectedRows = itemCount === selectedRows.length || isSelectAllEnabled ? itemCount : selectedRows.length;
|
|
38
23
|
const numSelectedRowsOnPage = (page || []).filter(r => r.isSelected).length;
|
|
39
24
|
|
|
40
25
|
const selectionStatusProps = {
|
|
@@ -42,8 +27,8 @@ function ControlledSelectionStatus({ className, clearSelectionText }) {
|
|
|
42
27
|
numSelectedRows,
|
|
43
28
|
numSelectedRowsOnPage,
|
|
44
29
|
clearSelectionText,
|
|
45
|
-
onSelectAll: () =>
|
|
46
|
-
onClear: () =>
|
|
30
|
+
onSelectAll: () => selectionsDispatch(setSelectAllRowsAllPagesAction()),
|
|
31
|
+
onClear: () => selectionsDispatch(clearSelectionAction()),
|
|
47
32
|
};
|
|
48
33
|
return <BaseSelectionStatus {...selectionStatusProps} />;
|
|
49
34
|
}
|
|
@@ -33,3 +33,8 @@ export const clearPageSelectionAction = (rowIds) => ({
|
|
|
33
33
|
type: CLEAR_PAGE_SELECTION,
|
|
34
34
|
rowIds,
|
|
35
35
|
});
|
|
36
|
+
|
|
37
|
+
export const TOGGLE_IS_ENTIRE_TABLE_SELECTED = 'TOGGLE IS ENTIRE TABLE SELECTED';
|
|
38
|
+
export const toggleIsEntireTableSelected = () => ({
|
|
39
|
+
type: TOGGLE_IS_ENTIRE_TABLE_SELECTED,
|
|
40
|
+
});
|
|
@@ -6,11 +6,13 @@ import {
|
|
|
6
6
|
CLEAR_SELECTION,
|
|
7
7
|
CLEAR_PAGE_SELECTION,
|
|
8
8
|
SET_SELECT_ALL_ROWS_ALL_PAGES,
|
|
9
|
+
TOGGLE_IS_ENTIRE_TABLE_SELECTED,
|
|
9
10
|
} from './actions';
|
|
10
11
|
|
|
11
12
|
export const initialState = {
|
|
12
13
|
selectedRows: [],
|
|
13
14
|
isEntireTableSelected: false,
|
|
15
|
+
isSelectAllEnabled: false,
|
|
14
16
|
};
|
|
15
17
|
|
|
16
18
|
const selectionsReducer = (state = initialState, action = {}) => {
|
|
@@ -21,7 +23,7 @@ const selectionsReducer = (state = initialState, action = {}) => {
|
|
|
21
23
|
...state,
|
|
22
24
|
selectedRows,
|
|
23
25
|
};
|
|
24
|
-
if (selectedRows.length === action.itemCount) {
|
|
26
|
+
if (state.isSelectAllEnabled || selectedRows.length === action.itemCount) {
|
|
25
27
|
updatedState.isEntireTableSelected = true;
|
|
26
28
|
}
|
|
27
29
|
return updatedState;
|
|
@@ -30,16 +32,19 @@ const selectionsReducer = (state = initialState, action = {}) => {
|
|
|
30
32
|
return {
|
|
31
33
|
...state,
|
|
32
34
|
isEntireTableSelected: true,
|
|
35
|
+
isSelectAllEnabled: true,
|
|
33
36
|
};
|
|
34
37
|
case DELETE_ROW:
|
|
35
38
|
return {
|
|
36
39
|
selectedRows: state.selectedRows.filter((row) => row.id !== action.rowId),
|
|
37
40
|
isEntireTableSelected: false,
|
|
41
|
+
isSelectAllEnabled: false,
|
|
38
42
|
};
|
|
39
43
|
case ADD_ROW: {
|
|
40
44
|
const selectedRows = uniqBy([...state.selectedRows, action.row], row => row.id);
|
|
41
45
|
const isEntireTableSelected = selectedRows.length === action.itemCount;
|
|
42
46
|
return {
|
|
47
|
+
...state,
|
|
43
48
|
selectedRows,
|
|
44
49
|
isEntireTableSelected,
|
|
45
50
|
};
|
|
@@ -50,6 +55,12 @@ const selectionsReducer = (state = initialState, action = {}) => {
|
|
|
50
55
|
return {
|
|
51
56
|
isEntireTableSelected: false,
|
|
52
57
|
selectedRows: state.selectedRows.filter(row => !action.rowIds.includes(row.id)),
|
|
58
|
+
isSelectAllEnabled: false,
|
|
59
|
+
};
|
|
60
|
+
case TOGGLE_IS_ENTIRE_TABLE_SELECTED:
|
|
61
|
+
return {
|
|
62
|
+
...state,
|
|
63
|
+
isEntireTableSelected: !state.isEntireTableSelected,
|
|
53
64
|
};
|
|
54
65
|
default:
|
|
55
66
|
return state;
|
|
@@ -4,7 +4,10 @@ import { IntlProvider } from 'react-intl';
|
|
|
4
4
|
import userEvent from '@testing-library/user-event';
|
|
5
5
|
|
|
6
6
|
import ControlledSelectionStatus from '../ControlledSelectionStatus';
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
clearSelectionAction,
|
|
9
|
+
setSelectAllRowsAllPagesAction,
|
|
10
|
+
} from '../data/actions';
|
|
8
11
|
import DataTableContext from '../../DataTableContext';
|
|
9
12
|
import {
|
|
10
13
|
SELECT_ALL_TEST_ID,
|
|
@@ -88,28 +91,6 @@ describe('<ControlledSelectionStatus />', () => {
|
|
|
88
91
|
const selectAllButton = screen.queryByTestId(SELECT_ALL_TEST_ID);
|
|
89
92
|
expect(selectAllButton).not.toBeInTheDocument();
|
|
90
93
|
});
|
|
91
|
-
|
|
92
|
-
it('selects any unselected page rows', () => {
|
|
93
|
-
const selectedRows = Array(instance.itemCount).map((item, index) => ({ id: index + 1 }));
|
|
94
|
-
const dispatchSpy = jest.fn();
|
|
95
|
-
render(
|
|
96
|
-
<ControlledSelectionStatusWrapper
|
|
97
|
-
value={{
|
|
98
|
-
...instance,
|
|
99
|
-
controlledTableSelections: [
|
|
100
|
-
{
|
|
101
|
-
selectedRows,
|
|
102
|
-
isEntireTableSelected: true,
|
|
103
|
-
},
|
|
104
|
-
dispatchSpy,
|
|
105
|
-
],
|
|
106
|
-
}}
|
|
107
|
-
/>,
|
|
108
|
-
);
|
|
109
|
-
expect(dispatchSpy).toHaveBeenCalledTimes(1);
|
|
110
|
-
const action = setSelectedRowsAction(instance.page, instance.itemCount);
|
|
111
|
-
expect(dispatchSpy).toHaveBeenCalledWith(action);
|
|
112
|
-
});
|
|
113
94
|
});
|
|
114
95
|
|
|
115
96
|
describe('individual rows selected', () => {
|
|
@@ -25,6 +25,7 @@ describe('DataTable selections reducer', () => {
|
|
|
25
25
|
const action = setSelectedRowsAction(rows, itemCount);
|
|
26
26
|
const updatedState = selectionsReducer(defaultInitialState, action);
|
|
27
27
|
expect(updatedState).toEqual({
|
|
28
|
+
...defaultInitialState,
|
|
28
29
|
isEntireTableSelected: true,
|
|
29
30
|
selectedRows: rows,
|
|
30
31
|
});
|
|
@@ -38,6 +39,7 @@ describe('DataTable selections reducer', () => {
|
|
|
38
39
|
const updatedState = selectionsReducer(initialState, action);
|
|
39
40
|
expect(updatedState).toEqual({
|
|
40
41
|
isEntireTableSelected: true,
|
|
42
|
+
isSelectAllEnabled: true,
|
|
41
43
|
selectedRows: initialState.selectedRows,
|
|
42
44
|
});
|
|
43
45
|
});
|
|
@@ -70,6 +72,7 @@ describe('DataTable selections reducer', () => {
|
|
|
70
72
|
const action = addSelectedRowAction(row, itemCount);
|
|
71
73
|
const updatedState = selectionsReducer(defaultInitialState, action);
|
|
72
74
|
expect(updatedState).toEqual({
|
|
75
|
+
...defaultInitialState,
|
|
73
76
|
selectedRows: [row],
|
|
74
77
|
isEntireTableSelected: true,
|
|
75
78
|
});
|
|
@@ -88,6 +91,7 @@ describe('DataTable selections reducer', () => {
|
|
|
88
91
|
const rows = [{ id: 1 }, { id: 2 }, { id: 3 }];
|
|
89
92
|
const initialState = {
|
|
90
93
|
...defaultInitialState,
|
|
94
|
+
isSelectAllEnabled: false,
|
|
91
95
|
selectedRows: rows,
|
|
92
96
|
};
|
|
93
97
|
const action = clearPageSelectionAction([1, 2]);
|