Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a *SELECT ALL* button in the <BulkActionsToolbar> #10367

Merged
merged 147 commits into from
Jan 3, 2025
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
147 commits
Select commit Hold shift + click to select a range
972e9dd
Add a "Select all" button + make it customizable + document jsDoc
erwanMarmelab Nov 20, 2024
9656803
add logic in useList
erwanMarmelab Nov 20, 2024
91b874f
wrap function with a useCallback
erwanMarmelab Nov 20, 2024
4cd44ea
add more data in the story to be able to test the "SELECT ALL" button
erwanMarmelab Nov 20, 2024
6178ee5
simplify the areAllSelected check
erwanMarmelab Nov 21, 2024
9e26228
add select all button on ReferenceManyField
erwanMarmelab Nov 21, 2024
dc18571
add a full app story with ReferenceManyField
erwanMarmelab Nov 21, 2024
426c9f5
remove the useless selectAllLimit prop from useList
erwanMarmelab Nov 21, 2024
5721ae9
add a SELECT ALL button for ReferenceArrayField
erwanMarmelab Nov 21, 2024
02eef0b
add SELECT ALL button on infiniteList + story + selectAllLimit
erwanMarmelab Nov 21, 2024
60b0b9a
change call on mount to mutation on callback
erwanMarmelab Nov 21, 2024
651ac4b
display error + fix getManyReference call
erwanMarmelab Nov 21, 2024
73f8e7e
add stories for ReferenceManyField for Pagination and selectAllLimit
erwanMarmelab Nov 21, 2024
d214481
add stories for List with selectAllLimit
erwanMarmelab Nov 21, 2024
76d9214
remove useless modification
erwanMarmelab Nov 21, 2024
1885e48
Fix Datagrid and ExpandAllButton tests
erwanMarmelab Nov 21, 2024
4c73b9c
Fix BulkUpdateButton test
erwanMarmelab Nov 21, 2024
548968d
add selectAllLimit to UseReferenceArrayFieldControllerParams to fix b…
erwanMarmelab Nov 21, 2024
dd7cf02
rename `areAllSelected` to `displaySelectAllButton`
erwanMarmelab Nov 22, 2024
9eb0f1c
test useListController
erwanMarmelab Nov 22, 2024
4546b6e
test useInfiniteListController
erwanMarmelab Nov 22, 2024
a374f05
test useReferenceArrayFieldController
erwanMarmelab Nov 22, 2024
38261a7
test useReferenceManyFieldController
erwanMarmelab Nov 22, 2024
7d73c0d
test useList
erwanMarmelab Nov 22, 2024
4182bb6
do not display "Select all" button if nb selected items > selectAllLimit
erwanMarmelab Nov 22, 2024
4f28a99
test `<List>`
erwanMarmelab Nov 22, 2024
c49c28c
test `<ReferenceManyField>` + fix ts issues
erwanMarmelab Nov 22, 2024
56db339
test `<ReferenceArrayField>` + fix ts issues
erwanMarmelab Nov 22, 2024
f82dd52
fix useInfiniteListController tests
erwanMarmelab Nov 25, 2024
db66a99
document `<List selectAllLimit>`
erwanMarmelab Nov 25, 2024
473a9f9
document `<ReferenceManyField selectAllLimit>`
erwanMarmelab Nov 25, 2024
0a6ff63
fix List tests
erwanMarmelab Nov 25, 2024
9162761
fix typos
erwanMarmelab Nov 26, 2024
cadc723
Simplify tests
erwanMarmelab Nov 26, 2024
538076c
simplify spacings in bulkActionToolbar
erwanMarmelab Nov 26, 2024
5b30150
rename displaySelectAllButton to areAllItemsSelected
erwanMarmelab Nov 26, 2024
a30dd17
create the `useSelectAll` hook
erwanMarmelab Nov 27, 2024
59024b4
fix ReferenceArrayInput
erwanMarmelab Nov 27, 2024
0846132
change `useMutation` to `queryClient.fetchQuery`
erwanMarmelab Nov 27, 2024
a3a9b8b
Isolate test components in stories-> `useReferenceArrayField`
erwanMarmelab Dec 2, 2024
30c1d28
Isolate test components in stories-> `useReferenceManyField`
erwanMarmelab Dec 2, 2024
7e0f92d
Isolate test components in stories-> `useInfiniteListController`
erwanMarmelab Dec 2, 2024
d9f1a59
Isolate test components in stories-> `useListController`
erwanMarmelab Dec 2, 2024
04ddc21
change `useMutation` to `queryClient.fetchQuery` in `useReferenceMany…
erwanMarmelab Dec 2, 2024
31c996b
change the structure
erwanMarmelab Dec 5, 2024
2b6e1d8
remove useless tests
erwanMarmelab Dec 5, 2024
f27f69b
remove useless stories
erwanMarmelab Dec 5, 2024
1b94c81
remove useless props + improve comments
erwanMarmelab Dec 6, 2024
af1427b
Add stories for SelectAllButton
erwanMarmelab Dec 6, 2024
52bcdd1
test SelectAllButton
erwanMarmelab Dec 6, 2024
1b0e974
add a name + support onClick
erwanMarmelab Dec 6, 2024
eb73fbd
document select all
erwanMarmelab Dec 6, 2024
e8f64e3
Fix SelectAllButton tests
erwanMarmelab Dec 6, 2024
97a338e
remove old structure with `selectAllLimit`
erwanMarmelab Dec 6, 2024
472b81b
change (again) the structure to have a button and logic in hooks
erwanMarmelab Dec 8, 2024
447d793
ReferenceArrayFieldController -> tests + stories
erwanMarmelab Dec 8, 2024
588767c
make the callback stronger
erwanMarmelab Dec 8, 2024
4573ae9
ReferenceManyFieldController -> tests + stories
erwanMarmelab Dec 8, 2024
98c48c7
moove useRecordSelection and useResourceContext logic
erwanMarmelab Dec 8, 2024
2f1f7db
InfiniteListController -> tests + stories
erwanMarmelab Dec 8, 2024
ad0a805
eListController -> tests + stories
erwanMarmelab Dec 8, 2024
c658335
test useList
erwanMarmelab Dec 8, 2024
420cfad
ReferenceManyField -> tests + stories
erwanMarmelab Dec 8, 2024
7401122
InfiniteList -> limit -> stories
erwanMarmelab Dec 8, 2024
1e41bc3
List -> tests + stories
erwanMarmelab Dec 8, 2024
c18a232
Improve documentation sentences
erwanMarmelab Dec 11, 2024
a4c7a82
Improve jsDoc of selectionModifiers
erwanMarmelab Dec 11, 2024
03d9afc
remove the duplicated `resource` from the query
erwanMarmelab Dec 11, 2024
f1b4091
add the missing frenc translation + reorder alphabetically the messages
erwanMarmelab Dec 11, 2024
6f8e99b
rename message `too_many_elements` into `select_all_limit_reached`
erwanMarmelab Dec 11, 2024
4e86cd8
Remove the `Warning` from the `select_all_limit_reached` message
erwanMarmelab Dec 11, 2024
755571e
Remove the `Warning` from the `select_all_limit_reached` message in t…
erwanMarmelab Dec 11, 2024
b8f8413
remove useless `console.error`
erwanMarmelab Dec 11, 2024
f1d4000
create a translation for the error message + add a type to the notif …
erwanMarmelab Dec 11, 2024
400d85f
rename `onSelectAllProps` into `onSelectAllParams`
erwanMarmelab Dec 11, 2024
bf47b5e
remove `selectionModifiers` from props to import it with a hook
erwanMarmelab Dec 11, 2024
8916730
apply the deletion of the `selectionMofifiers` prop
erwanMarmelab Dec 11, 2024
b56f600
apply the PREFIX to the CSS in JS instead of HTML attribute
erwanMarmelab Dec 11, 2024
7e7a05c
add a transated message dedicated for the select all button + fix Tra…
erwanMarmelab Dec 11, 2024
05dcd17
rename `onSelectAll` into `handleSelectAll` in `useSelectAll` (not in…
erwanMarmelab Dec 11, 2024
570170b
fix ts error
erwanMarmelab Dec 11, 2024
11b0422
use `useEvent` instead of `useCallback`
erwanMarmelab Dec 11, 2024
3da72ef
revert useless style changes
erwanMarmelab Dec 11, 2024
f02863b
group type imports
erwanMarmelab Dec 11, 2024
2f315dd
make data mare plausible
erwanMarmelab Dec 11, 2024
592824f
fix typo in `ListWithCheckboxes` title
erwanMarmelab Dec 11, 2024
14c3aa3
pass `{children}` as child instead of as prop
erwanMarmelab Dec 11, 2024
9dc0e64
stop using `act` in tests
erwanMarmelab Dec 13, 2024
4b53460
rename `spiedChildren` into `callback`
erwanMarmelab Dec 13, 2024
3e8272a
do write twice the spied func -> just spy it
erwanMarmelab Dec 13, 2024
b9142ae
pass children as child instead of prop
erwanMarmelab Dec 13, 2024
17b4b30
improve test names
erwanMarmelab Dec 13, 2024
0c25426
improve story label
erwanMarmelab Dec 13, 2024
8a647e5
isolate type imports
erwanMarmelab Dec 13, 2024
7f119de
adapt test with the new button label
erwanMarmelab Dec 13, 2024
605f303
moove the `SelectAllButton`
erwanMarmelab Dec 13, 2024
3563517
test the new `bulkActionsToolbar` Datagrid's prop
erwanMarmelab Dec 13, 2024
0f0b22e
Adapt SelectAllButton doc to the new place
erwanMarmelab Dec 13, 2024
4cfc3a2
Update Datagrid doc
erwanMarmelab Dec 13, 2024
92893c4
update screenshot
erwanMarmelab Dec 13, 2024
b2ed592
fix typo
erwanMarmelab Dec 13, 2024
aae3600
useSelectAll -> test + stories
erwanMarmelab Dec 13, 2024
7cf12a8
improve jsDoc
erwanMarmelab Dec 13, 2024
3f3cbfe
improve `SelectAllButton` introduction's doc
erwanMarmelab Dec 17, 2024
361153e
update the selection button name
erwanMarmelab Dec 17, 2024
68adcf9
rename stories
erwanMarmelab Dec 17, 2024
0231d03
fix ts errors + use story in tests
erwanMarmelab Dec 17, 2024
c8d447b
isolate tests into stories
erwanMarmelab Dec 17, 2024
ea989da
simplify tests (afterEach of Select All tests)
erwanMarmelab Dec 17, 2024
56f48a5
don't pass children to the tests -> `useReferenceArrayFieldController`
erwanMarmelab Dec 18, 2024
cb70247
don't pass children to the tests -> `useReferenceManyFieldController`
erwanMarmelab Dec 18, 2024
db74e83
don't pass children to the tests -> `useInfiniteListController`
erwanMarmelab Dec 18, 2024
8787f5b
don't pass children to the tests -> `useList`
erwanMarmelab Dec 18, 2024
122dae6
simplify tests
erwanMarmelab Dec 18, 2024
2abf955
don't pass children to the tests -> `useListController`
erwanMarmelab Dec 18, 2024
54c8e6e
clean file
erwanMarmelab Dec 18, 2024
ec85bf9
clean file
erwanMarmelab Dec 18, 2024
fbb290c
display SelectAllButton if all data are selected
erwanMarmelab Dec 18, 2024
4f4feff
adapt tests `SelectAllButton` + add a test on the new display condition
erwanMarmelab Dec 18, 2024
e5fdcb7
adapt tests `Datagrid`
erwanMarmelab Dec 18, 2024
2d9822d
simplify stories
erwanMarmelab Dec 18, 2024
1bd2dcb
adapt tests `List`
erwanMarmelab Dec 18, 2024
f5144e8
adapt tests `ReferenceManyField`
erwanMarmelab Dec 18, 2024
b300053
adapt tests `ReferenceArrayField`
erwanMarmelab Dec 18, 2024
65ad280
Merge branch 'next' into feat/next/select_all
erwanMarmelab Dec 18, 2024
614f4d1
improve buttons color
erwanMarmelab Dec 18, 2024
9df60b1
Adapt ExpandAllButton's test with the new story
erwanMarmelab Dec 19, 2024
969fd83
fix linter warning
erwanMarmelab Jan 2, 2025
832e4ba
add a usage section
erwanMarmelab Jan 2, 2025
3309535
remove the `bulkActionsToolbar` prop and create `selectAllButton`'s prop
erwanMarmelab Jan 2, 2025
75f21ef
improve jsDoc example
erwanMarmelab Jan 2, 2025
0a2399a
wrap tests in some `describe`
erwanMarmelab Jan 2, 2025
c5d2722
rename `handleSelectAll` into `handleClick`
erwanMarmelab Jan 2, 2025
781ab5a
fix typo
erwanMarmelab Jan 2, 2025
0bbb005
Rename `List` into `ListView`
erwanMarmelab Jan 2, 2025
ed4713a
rename story files
erwanMarmelab Jan 2, 2025
315b9a9
add `target` and `id` to the queryKey
erwanMarmelab Jan 2, 2025
6aac661
test the right spot for useSelectAll
erwanMarmelab Jan 2, 2025
d8c0992
use http_error instead of simple_error
erwanMarmelab Jan 2, 2025
b27fe7c
make test more logic
erwanMarmelab Jan 2, 2025
2f3ed8a
rename tests
erwanMarmelab Jan 2, 2025
002f485
fix tests
erwanMarmelab Jan 2, 2025
420ab1d
update the bulkActionsToolbar implementation
erwanMarmelab Jan 3, 2025
68fe92c
adapt stories with the last implem to fix stories
erwanMarmelab Jan 3, 2025
2ebbcad
Misc adjustments
fzaninotto Jan 3, 2025
b4c0f8d
Capitalize type name
fzaninotto Jan 3, 2025
31b3bd5
Fix TS compilation error
fzaninotto Jan 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/ra-core/src/controller/list/ListContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ import { ListControllerResult } from './useListController';
* @prop {Function} showFilter a callback to show one of the filters, e.g. showFilter('title', defaultValue)
* @prop {Function} hideFilter a callback to hide one of the filters, e.g. hideFilter('title')
* @prop {Array} selectedIds an array listing the ids of the selected rows, e.g. [123, 456]
* @prop {boolean} areAllSelected boolean to indicate if the list is already fully selected
* @prop {Function} onSelect callback to change the list of selected rows, e.g. onSelect([456, 789])
* @prop {Function} onSelectAll callback to select all the records, e.g. onSelectAll()
* @prop {Function} onToggleItem callback to toggle the selection of a given record based on its id, e.g. onToggleItem(456)
* @prop {Function} onUnselectItems callback to clear the selection, e.g. onUnselectItems();
* @prop {string} defaultTitle the translated title based on the resource, e.g. 'Posts'
Expand Down
19 changes: 19 additions & 0 deletions packages/ra-core/src/controller/list/useList.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { useCallback, useEffect, useRef, useState } from 'react';
import get from 'lodash/get';
import isEqual from 'lodash/isEqual';

import { useNotify } from '../../notification';
import { removeEmpty } from '../../util';
import { FilterPayload, RaRecord, SortPayload } from '../../types';
import { useResourceContext } from '../../core';
Expand Down Expand Up @@ -50,6 +52,7 @@
* @param {Number} props.perPage: Optional. The initial page size
* @param {SortPayload} props.sort: Optional. The initial sort (field and order)
* @param {filterCallback} prop.filterCallback Optional. A function that allows you to make a custom filter
* @param {Number} props.selectAllLimit: Optional. The number of items selected by the "SELECT ALL" button of the bulk actions toolbar
*/
export const useList = <RecordType extends RaRecord = any>(
props: UseListOptions<RecordType>
Expand All @@ -65,8 +68,10 @@
perPage: initialPerPage = 1000,
sort: initialSort,
filterCallback = (record: RecordType) => Boolean(record),
selectAllLimit = 250,
} = props;
const resource = useResourceContext(props);
const notify = useNotify();

const [fetchingState, setFetchingState] = useState<boolean>(isFetching) as [
boolean,
Expand Down Expand Up @@ -162,8 +167,19 @@
},
[setDisplayedFilters, setFilterValues, setPage]
);
const onSelectAll = useCallback(() => {
const allIds = data?.map(({ id }) => id) || [];
selectionModifiers.select(allIds);
if (allIds.length === selectAllLimit) {
notify('ra.message.too_many_elements', {
messageArgs: { max: selectAllLimit },
type: 'warning',
});
}
}, [data, notify, selectAllLimit, selectionModifiers]);

// handle filter prop change
useEffect(() => {

Check warning on line 182 in packages/ra-core/src/controller/list/useList.ts

View workflow job for this annotation

GitHub Actions / typecheck

React Hook useEffect contains a call to 'setFilterValues'. Without a list of dependencies, this can lead to an infinite chain of updates. To fix this, pass [filter] as a second argument to the useEffect Hook
if (!isEqual(filter, filterRef.current)) {
filterRef.current = filter;
setFilterValues(filter);
Expand Down Expand Up @@ -283,13 +299,15 @@
isLoading: loadingState,
isPending: pendingState,
onSelect: selectionModifiers.select,
onSelectAll,
onToggleItem: selectionModifiers.toggle,
onUnselectItems: selectionModifiers.clearSelection,
page,
perPage,
resource: '',
refetch,
selectedIds,
areAllSelected: data?.length !== selectedIds.length,
setFilters,
setPage,
setPerPage,
Expand All @@ -311,6 +329,7 @@
sort?: SortPayload;
resource?: string;
filterCallback?: (record: RecordType) => boolean;
selectAllLimit?: number;
}

export type UseListValue<RecordType extends RaRecord = any> =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ import { RaRecord } from '../../types';
* @prop {Function} showFilter a callback to show one of the filters, e.g. showFilter('title', defaultValue)
* @prop {Function} hideFilter a callback to hide one of the filters, e.g. hideFilter('title')
* @prop {Array} selectedIds an array listing the ids of the selected rows, e.g. [123, 456]
* @prop {boolean} areAllSelected boolean to indicate if the list is already fully selected
* @prop {Function} onSelect callback to change the list of selected rows, e.g. onSelect([456, 789])
* @prop {Function} onSelectAll callback to select all the records, e.g. onSelectAll()
* @prop {Function} onToggleItem callback to toggle the selection of a given record based on its id, e.g. onToggleItem(456)
* @prop {Function} onUnselectItems callback to clear the selection, e.g. onUnselectItems();
* @prop {string} defaultTitle the translated title based on the resource, e.g. 'Posts'
Expand Down Expand Up @@ -81,13 +83,15 @@ const extractListContextProps = <RecordType extends RaRecord = any>({
isLoading,
isPending,
onSelect,
onSelectAll,
onToggleItem,
onUnselectItems,
page,
perPage,
refetch,
resource,
selectedIds,
areAllSelected,
setFilters,
setPage,
setPerPage,
Expand All @@ -107,13 +111,15 @@ const extractListContextProps = <RecordType extends RaRecord = any>({
isLoading,
isPending,
onSelect,
onSelectAll,
onToggleItem,
onUnselectItems,
page,
perPage,
refetch,
resource,
selectedIds,
areAllSelected,
setFilters,
setPage,
setPerPage,
Expand Down
51 changes: 50 additions & 1 deletion packages/ra-core/src/controller/list/useListController.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isValidElement, useEffect, useMemo } from 'react';
import { isValidElement, useCallback, useEffect, useMemo } from 'react';

import { useAuthenticated, useRequireAccess } from '../../auth';
import { useTranslate } from '../../i18n';
Expand Down Expand Up @@ -46,6 +46,7 @@ export const useListController = <RecordType extends RaRecord = any>(
queryOptions = {},
sort = defaultSort,
storeKey,
selectAllLimit = 250,
} = props;
const resource = useResourceContext(props);
const { meta, ...otherQueryOptions } = queryOptions;
Expand Down Expand Up @@ -168,6 +169,37 @@ export const useListController = <RecordType extends RaRecord = any>(
name: getResourceLabel(resource, 2),
});

const { data: allData } = useGetList<RecordType>(
resource,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'd better use the data provider directly in the callback, or using react-query's useMutation. useGetList is really designed for queries triggered on mount.

{
pagination: {
page: 1,
perPage: selectAllLimit,
},
sort: { field: query.sort, order: query.order },
filter: { ...query.filter, ...filter },
meta,
},
{
enabled:
(!isPendingAuthenticated && !isPendingCanAccess) ||
disableAuthentication,
retry: false,
...otherQueryOptions,
}
);

const onSelectAll = useCallback(() => {
const allIds = allData?.map(({ id }) => id) || [];
selectionModifiers.select(allIds);
if (allIds.length === selectAllLimit) {
notify('ra.message.too_many_elements', {
messageArgs: { max: selectAllLimit },
type: 'warning',
});
}
}, [allData, notify, selectAllLimit, selectionModifiers]);

return {
sort: currentSort,
data,
Expand All @@ -183,13 +215,15 @@ export const useListController = <RecordType extends RaRecord = any>(
isLoading,
isPending,
onSelect: selectionModifiers.select,
onSelectAll,
onToggleItem: selectionModifiers.toggle,
onUnselectItems: selectionModifiers.clearSelection,
page: query.page,
perPage: query.perPage,
refetch,
resource,
selectedIds,
areAllSelected: allData?.length !== selectedIds.length,
setFilters: queryModifiers.setFilters,
setPage: queryModifiers.setPage,
setPerPage: queryModifiers.setPerPage,
Expand Down Expand Up @@ -409,6 +443,19 @@ export interface ListControllerProps<RecordType extends RaRecord = any> {
* );
*/
storeKey?: string | false;

/**
* The number of items selected by the "SELECT ALL" button of the bulk actions toolbar.
*
* @see https://marmelab.com/react-admin/List.html#selectalllimit
* @example
* export const PostList = () => (
* <List selectAllLimit={500}>
* ...
* </List>
* );
*/
selectAllLimit?: number;
}

const defaultSort = {
Expand Down Expand Up @@ -475,13 +522,15 @@ export interface ListControllerBaseResult<RecordType extends RaRecord = any> {
filterValues: any;
hideFilter: (filterName: string) => void;
onSelect: (ids: RecordType['id'][]) => void;
onSelectAll: () => void;
onToggleItem: (id: RecordType['id']) => void;
onUnselectItems: () => void;
page: number;
perPage: number;
refetch: (() => void) | UseGetListHookValue<RecordType>['refetch'];
resource: string;
selectedIds: RecordType['id'][];
areAllSelected: boolean;
setFilters: (
filters: any,
displayedFilters?: any,
Expand Down
13 changes: 7 additions & 6 deletions packages/ra-language-english/src/index.ts
fzaninotto marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,11 @@ const englishMessages: TranslationMessages = {
},
message: {
about: 'About',
access_denied:
"You don't have the right permissions to access this page",
are_you_sure: 'Are you sure?',
authentication_error:
'The authentication server returned an error and your credentials could not be checked.',
auth_error:
'An error occurred while validating the authentication token.',
bulk_delete_content:
Expand All @@ -103,19 +107,16 @@ const englishMessages: TranslationMessages = {
delete_title: 'Delete %{name} #%{id}',
details: 'Details',
error: "A client error occurred and your request couldn't be completed.",

invalid_form: 'The form is not valid. Please check for errors',
loading: 'Please wait',
no: 'No',
not_found:
'Either you typed a wrong URL, or you followed a bad link.',
yes: 'Yes',
too_many_elements:
fzaninotto marked this conversation as resolved.
Show resolved Hide resolved
'Warning: There are too many elements to select them all. Only the first %{max} elements were selected.',
fzaninotto marked this conversation as resolved.
Show resolved Hide resolved
unsaved_changes:
"Some of your changes weren't saved. Are you sure you want to ignore them?",
access_denied:
"You don't have the right permissions to access this page",
authentication_error:
'The authentication server returned an error and your credentials could not be checked.',
yes: 'Yes',
},
navigation: {
clear_filters: 'Clear filters',
Expand Down
20 changes: 19 additions & 1 deletion packages/ra-ui-materialui/src/list/BulkActionsToolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import CloseIcon from '@mui/icons-material/Close';
import { useTranslate, sanitizeListRestProps, useListContext } from 'ra-core';

import TopToolbar from '../layout/TopToolbar';
import { Button } from '../button';

export const BulkActionsToolbar = (props: BulkActionsToolbarProps) => {
const {
Expand All @@ -18,14 +19,23 @@ export const BulkActionsToolbar = (props: BulkActionsToolbarProps) => {
className,
...rest
} = props;
const { selectedIds = [], onUnselectItems } = useListContext();
const {
selectedIds = [],
onUnselectItems,
onSelectAll,
areAllSelected,
} = useListContext();

const translate = useTranslate();

const handleUnselectAllClick = useCallback(() => {
onUnselectItems();
}, [onUnselectItems]);

const handleSelectAll = useCallback(() => {
onSelectAll();
}, [onSelectAll]);

return (
<Root className={className}>
<Toolbar
Expand All @@ -52,6 +62,13 @@ export const BulkActionsToolbar = (props: BulkActionsToolbarProps) => {
smart_count: selectedIds.length,
})}
</Typography>
{areAllSelected && (
<Button
label={translate('ra.action.select_all')}
onClick={handleSelectAll}
sx={{ ml: 1 }}
fzaninotto marked this conversation as resolved.
Show resolved Hide resolved
/>
)}
</div>
<TopToolbar className={BulkActionsToolbarClasses.topToolbar}>
{children}
Expand All @@ -65,6 +82,7 @@ export interface BulkActionsToolbarProps {
children?: ReactNode;
label?: string;
className?: string;
selectAllLimit?: number;
}

const PREFIX = 'RaBulkActionsToolbar';
Expand Down
3 changes: 3 additions & 0 deletions packages/ra-ui-materialui/src/list/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { Loading } from '../layout';
* - perPage: Pagination Size
* - queryOptions
* - sort: Default Sort Field & Order
* - selectAllLimit: The number of items selected by the "SELECT ALL" button of the bulk actions toolbar
* - title
* - sx: CSS API
*
Expand Down Expand Up @@ -68,6 +69,7 @@ export const List = <RecordType extends RaRecord = any>({
resource,
sort,
storeKey,
selectAllLimit = 250,
...rest
}: ListProps<RecordType>): ReactElement => (
<ListBase<RecordType>
Expand All @@ -83,6 +85,7 @@ export const List = <RecordType extends RaRecord = any>({
resource={resource}
sort={sort}
storeKey={storeKey}
selectAllLimit={selectAllLimit}
>
<ListView<RecordType> {...rest} />
</ListBase>
Expand Down
Loading