diff --git a/docs/Buttons.md b/docs/Buttons.md
index e847274f67f..3108ff23478 100644
--- a/docs/Buttons.md
+++ b/docs/Buttons.md
@@ -154,16 +154,99 @@ The following buttons are designed to be used in List views.
Exports the current list, with filter applied, but without pagination. It relies on [the `exporter` function](./List.md#exporter) passed to the `` component, via the `ListContext`. It's disabled for empty lists.
+By default, the `` is included in the List actions.
+
+```jsx
+import { CreateButton, ExportButton, TopToolbar } from 'react-admin';
+
+const PostListActions = ({ basePath }) => (
+
+
+
+
+
+);
+
+export const PostList = (props) => (
+
} {...props}>
+ ...
+
+);
+```
+
+
+
| Prop | Required | Type | Default | Description |
| ------------ | -------- | --------------- | ------------------ | ----------------------------------- |
| `maxResults` | Optional | `number` | 1000 | Maximum number of records to export |
| `label` | Optional | `string` | 'ra.action.export' | label or translation message to use |
-| `icon` | Optional | `React.element` | - | iconElement, e.g. `` |
+| `icon` | Optional | `React.element` | `` | iconElement, e.g. `` |
| `exporter` | Optional | `function` | - | Override the List exporter function |
+### ``
+
+Same as ``, except it only exports the selected rows instead of the entire list. To be used inside [the `` prop](./List.md#bulkactionbuttons).
+
+```jsx
+import * as React from 'react';
+import { Fragment } from 'react';
+import { BulkDeleteButton, BulkExportButton } from 'react-admin';
+
+const PostBulkActionButtons = ({ basePath }) => (
+
+
+
+
+);
+
+export const PostList = (props) => (
+
}>
+ ...
+
+);
+```
+
+
+
+| Prop | Required | Type | Default | Description |
+| ------------ | -------- | --------------- | ------------------ | ----------------------------------- |
+| `label` | Optional | `string` | 'ra.action.export' | label or translation message to use |
+| `icon` | Optional | `React.element` | `` | iconElement, e.g. `` |
+| `exporter` | Optional | `function` | - | Override the List exporter function |
### ``
-### ``
+
+Deletes the selected rows. To be used inside [the `` prop](./List.md#bulkactionbuttons) (where it's enabled by default).
+
+```jsx
+import * as React from 'react';
+import { Fragment } from 'react';
+import { BulkDeleteButton, BulkExportButton } from 'react-admin';
+
+const PostBulkActionButtons = ({ basePath }) => (
+
+
+
+
+);
+
+export const PostList = (props) => (
+
}>
+ ...
+
+);
+```
+
+
+
+| Prop | Required | Type | Default | Description |
+| ------------ | -------- | --------------- | ------------------ | ----------------------------------- |
+| `label` | Optional | `string` | 'ra.action.delete' | label or translation message to use |
+| `icon` | Optional | `React.element` | `` | iconElement, e.g. `` |
+| `exporter` | Optional | `function` | - | Override the List exporter function |
+| `undoable` | Optional | `boolean` | true | Allow users to cancel the deletion |
+
+### ``
### ``
## Record Buttons
diff --git a/docs/img/bulk-delete-button.png b/docs/img/bulk-delete-button.png
new file mode 100644
index 00000000000..aa01e5853fe
Binary files /dev/null and b/docs/img/bulk-delete-button.png differ
diff --git a/docs/img/bulk-export-button.png b/docs/img/bulk-export-button.png
new file mode 100644
index 00000000000..eecbf3f76b6
Binary files /dev/null and b/docs/img/bulk-export-button.png differ
diff --git a/docs/img/create-button-fab.png b/docs/img/create-button-fab.png
index 5bb45fe4126..d5c32190b2b 100644
Binary files a/docs/img/create-button-fab.png and b/docs/img/create-button-fab.png differ
diff --git a/docs/img/export-button.png b/docs/img/export-button.png
new file mode 100644
index 00000000000..6d960822505
Binary files /dev/null and b/docs/img/export-button.png differ
diff --git a/packages/ra-core/src/controller/useListContext.ts b/packages/ra-core/src/controller/useListContext.ts
index 4e7b8601ecc..9c951c577e9 100644
--- a/packages/ra-core/src/controller/useListContext.ts
+++ b/packages/ra-core/src/controller/useListContext.ts
@@ -109,6 +109,65 @@ const useListContext = (
);
};
+/**
+ * Extract only the list controller props
+ *
+ * @param {Object} props Props passed to the useListContext hook
+ *
+ * @returns {ListControllerProps} List controller props
+ */
+const extractListContextProps = ({
+ basePath,
+ currentSort,
+ data,
+ defaultTitle,
+ displayedFilters,
+ filterValues,
+ hasCreate,
+ hideFilter,
+ ids,
+ loaded,
+ loading,
+ onSelect,
+ onToggleItem,
+ onUnselectItems,
+ page,
+ perPage,
+ resource,
+ selectedIds,
+ setFilters,
+ setPage,
+ setPerPage,
+ setSort,
+ showFilter,
+ total,
+}) => ({
+ basePath,
+ currentSort,
+ data,
+ defaultTitle,
+ displayedFilters,
+ filterValues,
+ hasCreate,
+ hideFilter,
+ ids,
+ loaded,
+ loading,
+ onSelect,
+ onToggleItem,
+ onUnselectItems,
+ page,
+ perPage,
+ resource,
+ selectedIds,
+ setFilters,
+ setPage,
+ setPerPage,
+ setSort,
+ showFilter,
+ total,
+});
+
export default useListContext;
/**
diff --git a/packages/ra-ui-materialui/src/button/BulkDeleteButton.tsx b/packages/ra-ui-materialui/src/button/BulkDeleteButton.tsx
index 4317a69b443..9708cadcb8b 100644
--- a/packages/ra-ui-materialui/src/button/BulkDeleteButton.tsx
+++ b/packages/ra-ui-materialui/src/button/BulkDeleteButton.tsx
@@ -8,6 +8,29 @@ import BulkDeleteWithUndoButton, {
BulkDeleteWithUndoButtonProps,
} from './BulkDeleteWithUndoButton';
+/**
+ * Deletes the selected rows.
+ *
+ * To be used inside the prop (where it's enabled by default).
+ *
+ * @example // basic usage
+ * import * as React from 'react';
+ * import { Fragment } from 'react';
+ * import { BulkDeleteButton, BulkExportButton } from 'react-admin';
+ *
+ * const PostBulkActionButtons = ({ basePath }) => (
+ *
+ *
+ *
+ *
+ * );
+ *
+ * export const PostList = (props) => (
+ *
}>
+ * ...
+ *
+ * );
+ */
const BulkDeleteButton: FC = ({ undoable, ...props }) =>
undoable ? (
diff --git a/packages/ra-ui-materialui/src/button/BulkDeleteWithUndoButton.tsx b/packages/ra-ui-materialui/src/button/BulkDeleteWithUndoButton.tsx
index f5d28ee8b41..df3a68a2137 100644
--- a/packages/ra-ui-materialui/src/button/BulkDeleteWithUndoButton.tsx
+++ b/packages/ra-ui-materialui/src/button/BulkDeleteWithUndoButton.tsx
@@ -11,6 +11,7 @@ import {
useUnselectAll,
CRUD_DELETE_MANY,
useResourceContext,
+ useListContext,
} from 'ra-core';
import Button, { ButtonProps } from './Button';
@@ -39,9 +40,9 @@ const BulkDeleteWithUndoButton: FC = props => {
icon,
label,
onClick,
- selectedIds,
...rest
} = props;
+ const { selectedIds } = useListContext(props);
const classes = useStyles(props);
const notify = useNotify();
const unselectAll = useUnselectAll();
diff --git a/packages/ra-ui-materialui/src/button/BulkExportButton.tsx b/packages/ra-ui-materialui/src/button/BulkExportButton.tsx
index bf09101352b..af73e405a54 100644
--- a/packages/ra-ui-materialui/src/button/BulkExportButton.tsx
+++ b/packages/ra-ui-materialui/src/button/BulkExportButton.tsx
@@ -13,6 +13,29 @@ import {
import Button, { ButtonProps } from './Button';
+/**
+ * Export the selected rows
+ *
+ * To be used inside the prop.
+ *
+ * @example // basic usage
+ * import * as React from 'react';
+ * import { Fragment } from 'react';
+ * import { BulkDeleteButton, BulkExportButton } from 'react-admin';
+ *
+ * const PostBulkActionButtons = ({ basePath }) => (
+ *
+ *
+ *
+ *
+ * );
+ *
+ * export const PostList = (props) => (
+ *
}>
+ * ...
+ *
+ * );
+ */
const BulkExportButton: FunctionComponent = props => {
const {
onClick,
diff --git a/packages/ra-ui-materialui/src/list/BulkActionsToolbar.tsx b/packages/ra-ui-materialui/src/list/BulkActionsToolbar.tsx
index 3b007009dda..72c22831d72 100644
--- a/packages/ra-ui-materialui/src/list/BulkActionsToolbar.tsx
+++ b/packages/ra-ui-materialui/src/list/BulkActionsToolbar.tsx
@@ -33,6 +33,9 @@ const useStyles = makeStyles(
'height'
)}, ${theme.transitions.create('min-height')}`,
},
+ topToolbar: {
+ paddingTop: theme.spacing(2),
+ },
buttons: {},
collapsed: {
minHeight: 0,
@@ -93,7 +96,7 @@ const BulkActionsToolbar: FC = props => {
})}
-
+
{Children.map(children, child =>
isValidElement(child)
? cloneElement(child, {
diff --git a/packages/ra-ui-materialui/src/list/filter/FilterButton.tsx b/packages/ra-ui-materialui/src/list/filter/FilterButton.tsx
index 7931a9fb2b8..203d18fde4f 100644
--- a/packages/ra-ui-materialui/src/list/filter/FilterButton.tsx
+++ b/packages/ra-ui-materialui/src/list/filter/FilterButton.tsx
@@ -12,6 +12,7 @@ import { makeStyles } from '@material-ui/core/styles';
import ContentFilter from '@material-ui/icons/FilterList';
import classnames from 'classnames';
import lodashGet from 'lodash/get';
+import { useListContext, useResourceContext } from 'ra-core';
import { FilterButtonMenuItem } from './FilterButtonMenuItem';
import Button from '../../button/Button';
@@ -25,16 +26,11 @@ const useStyles = makeStyles(
);
const FilterButton = (props: FilterButtonProps): JSX.Element => {
- const {
- filters,
- displayedFilters = {},
- filterValues,
- showFilter,
- classes: classesOverride,
- className,
- resource,
- ...rest
- } = props;
+ const { filters, classes: classesOverride, className, ...rest } = props;
+ const resource = useResourceContext(props);
+ const { displayedFilters = {}, filterValues, showFilter } = useListContext(
+ props
+ );
const [open, setOpen] = useState(false);
const anchorEl = useRef();
const classes = useStyles(props);