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

Added BulkUpdateButton component #6072

Merged
merged 21 commits into from
May 31, 2021
Merged
Changes from 1 commit
Commits
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
Prev Previous commit
Next Next commit
Fix documentation
asvarcas committed Apr 13, 2021
commit 561f1055b61a7f23fc35cca3e25170e5eaba3863
131 changes: 117 additions & 14 deletions docs/List.md
Original file line number Diff line number Diff line change
@@ -297,11 +297,11 @@ Bulk action button components receive several props allowing them to perform the
* `filterValues`: the filter values. This can be useful if you want to apply your action on all items matching the filter.
* `selectedIds`: the identifiers of the currently selected items.

Here is an example of `BulkUpdateButton` usage, which sets the `views` property of all posts to `0`:
Here is an example of `BulkUpdateButton` usage, which sets the `views` property of all posts to `0` optimistically:

```jsx
// in ./ResetViewsButton.js
import * as React from "react";
import * as React from 'react';
import { VisibilityOff } from '@material-ui/icons';
import { BulkUpdateButton } from 'react-admin';

@@ -319,9 +319,9 @@ const ResetViewsButton = (props) => (
export default ResetViewsButton;
```

But most of the time, bulk actions are mini-applications with a standalone user interface (in a Dialog). Here is the same `ResetViewsButton` implemented behind a confirmation dialog:
You can also implement the same `ResetViewsButton` behind a confirmation dialog by using the [`mutationMode`](./CreateEdit.md#mutationmode) prop:

```jsx
```diff
// in ./ResetViewsButton.js
import * as React from 'react';
import { VisibilityOff } from '@material-ui/icons';
@@ -335,25 +335,128 @@ const ResetViewsButton = (props) => (
label="Reset Views"
data={views}
icon={VisibilityOff}
undoable={false}
+ mutationMode="pessimistic"
/>
);

export default ResetViewsButton;
```

**Tip**: You can pass `onSuccess` and `onFailure` props as functions to customize side effects used by the underlying `useUpdateMany` hook.
But let's say you need a customized bulkAction button, here is an example leveraging the `useUpdateMany` hook, which sets the `views` property of all posts to `0`:

```jsx
// in ./CustomResetViewsButton.js
import * as React from "react";
import {
Button,
useUpdateMany,
useRefresh,
useNotify,
useUnselectAll,
} from 'react-admin';
import { VisibilityOff } from '@material-ui/icons';

const CustomResetViewsButton = ({ selectedIds }) => {
const refresh = useRefresh();
const notify = useNotify();
const unselectAll = useUnselectAll();
const [updateMany, { loading }] = useUpdateMany(
'posts',
selectedIds,
{ views: 0 },
{
onSuccess: () => {
refresh();
notify('Posts updated');
unselectAll('posts');
},
onFailure: error => notify('Error: posts not updated', 'warning'),
}
);

return (
<Button
label="simple.action.resetViews"
disabled={loading}
onClick={updateMany}
>
<VisibilityOff />
</Button>
);
};

export default CustomResetViewsButton;
```

But most of the time, bulk actions are mini-applications with a standalone user interface (in a Dialog). Here is the same `CustomResetViewsAction` implemented behind a confirmation dialog:

**Tip**: When `undoable={false}` prop is passed to `BulkUpdateButton`, it uses `react-admin` `<Confirm>` component leveraging material-ui's `<Dialog>` component to implement a confirmation popup. Feel free to use it in your admins!
```jsx
// in ./CustomResetViewsButton.js
import * as React from 'react';
import { Fragment, useState } from 'react';
import {
Button,
Confirm,
useUpdateMany,
useRefresh,
useNotify,
useUnselectAll,
} from 'react-admin';

**Tip**: `<Confirm>` text props such as `title` (`confirmTitle` passed from `BulkUpdateButton`) and `content` (`confirmContent` passed from `BulkUpdateButton`) are translatable. You can pass translation keys in these props. Note: `content` is only translateable when value is `string`, otherwise it renders the content as a `ReactNode`.
const CustomResetViewsButton = ({ selectedIds }) => {
const [open, setOpen] = useState(false);
const refresh = useRefresh();
const notify = useNotify();
const unselectAll = useUnselectAll();
const [updateMany, { loading }] = useUpdateMany(
'posts',
selectedIds,
{ views: 0 },
{
onSuccess: () => {
refresh();
notify('Posts updated');
unselectAll('posts');
},
onFailure: error => notify('Error: posts not updated', 'warning'),
}
);
const handleClick = () => setOpen(true);
const handleDialogClose = () => setOpen(false);

const handleConfirm = () => {
updateMany();
setOpen(false);
};

return (
<Fragment>
<Button label="Reset Views" onClick={handleClick} />
<Confirm
isOpen={open}
loading={loading}
title="Update View Count"
content="Are you sure you want to reset the views for these items?"
onConfirm={handleConfirm}
onClose={handleDialogClose}
/>
</Fragment>
);
}

export default CustomResetViewsButton;
```

**Tip**: `<Confirm>` leverages material-ui's `<Dialog>` component to implement a confirmation popup. Feel free to use it in your admins!

**Tip**: `<Confirm>` text props such as `title` and `content` are translatable. You can pass translation keys in these props. Note: `content` is only translateable when value is `string`, otherwise it renders the content as a `ReactNode`.

**Tip**: You can customize the text of the two `<Confirm>` component buttons using the `cancel` and `confirm` props which accept translation keys. You can customize the icons by setting the `ConfirmIcon` and `CancelIcon` props, which accept a SvgIcon type.

**Tip**: React-admin doesn't use the `<Confirm>` component internally, because deletes and updates are applied locally immediately, then dispatched to the server after a few seconds, unless the user chooses to undo the modification. That's what we call optimistic rendering. You can do the same for the `ResetViewsButton` by setting `undoable: true` in the last argument of `useUpdateMany()`, as follows:

```diff
// in ./ResetViewsButton.js
// in ./CustomResetViewsButton.js
import * as React from "react";
import {
Button,
@@ -365,7 +468,7 @@ import {
} from 'react-admin';
import { VisibilityOff } from '@material-ui/icons';

const ResetViewsButton = ({ selectedIds }) => {
const CustomResetViewsButton = ({ selectedIds }) => {
const refresh = useRefresh();
const notify = useNotify();
const unselectAll = useUnselectAll();
@@ -381,7 +484,7 @@ const ResetViewsButton = ({ selectedIds }) => {
unselectAll('posts');
},
onFailure: error => notify('Error: posts not updated', 'warning'),
+ undoable: true
+ mutationMode: 'undoable'
}
);

@@ -675,7 +778,7 @@ The default value for the `component` prop is `Card`.

### Synchronize With URL

When a List based component (eg: `PostList`) is passed to the `list` prop of a `<Resource>`, it will automatically synchronize its parameters with the browser URL (using react-router location). However, when used anywhere outside of a `<Resource>`, it won't synchronize, which can be useful when you have multiple lists on a single page for example.
When a List based component (eg: `PostList`) is passed to the `list` prop of a `<Resource>`, it will automatically synchronize its parameters with the browser URL (using react-router location). However, when used anywhere outside a `<Resource>`, it won't synchronize, which can be useful when you have multiple lists on a single page for example.

In order to enable the synchronization with the URL, you can set the `syncWithLocation` prop. For example, adding a `List` to an `Edit` page:

@@ -1045,7 +1148,7 @@ const SegmentFilter = () => (

#### Placing Filters In A Sidebar

You can place these `<FilterList>` anywhere inside a `<List>`. The most common case is to put them in a sidebar that is on the left hand side of the datagrid. You can use the `aside` property for that:
You can place these `<FilterList>` anywhere inside a `<List>`. The most common case is to put them in a sidebar that is on the left-hand side of the `Datagrid`. You can use the `aside` property for that:

```jsx
import * as React from 'react';
@@ -1912,7 +2015,7 @@ As you can see, the controller part of the List view is handled by a hook called

![The `<Datagrid>` component](./img/tutorial_post_list_less_columns.png)

The `Datagrid` component renders a list of records as a table. It is usually used as a descendant of the [`<List>`](#the-list-component) and [`<ReferenceManyField>`](./Fields.md#referencemanyfield) components. Outside of these components, it must be used inside a `ListContext`.
The `Datagrid` component renders a list of records as a table. It is usually used as a descendant of the [`<List>`](#the-list-component) and [`<ReferenceManyField>`](./Fields.md#referencemanyfield) components. Outside these components, it must be used inside a `ListContext`.

Here are all the props accepted by the component: