Skip to content

Commit 2ca98dd

Browse files
authored
Merge pull request #9145 from marmelab/doc-bulk-update-form-button
[Doc] Add documentation about <BulkUpdateFormButton> and <InputSelectorForm>
2 parents 507109b + 269bfdc commit 2ca98dd

6 files changed

+316
-3
lines changed

docs/Buttons.md

Lines changed: 310 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,10 @@ export const PostList = () => (
261261

262262
Partially updates the selected rows. To be used inside [the `<Datagrid bulkActionButtons>` prop](./Datagrid.md#bulkactionbuttons).
263263

264+
![Bulk Update button](./img/bulk-update-button.png)
265+
266+
#### Usage
267+
264268
{% raw %}
265269
```jsx
266270
import * as React from 'react';
@@ -285,7 +289,7 @@ export const PostList = () => (
285289
```
286290
{% endraw %}
287291

288-
![Bulk Update button](./img/bulk-update-button.png)
292+
#### Props
289293

290294
| Prop | Required | Type | Default | Description |
291295
|-------------------|----------|----------------|--------------------|---------------------------------------------------------------------------------------------------------------------|
@@ -299,6 +303,311 @@ export const PostList = () => (
299303

300304
**Tip:** If you choose the `'pessimistic'` or `'optimistic'` mutation mode, a confirm dialog will be displayed to the user before the mutation is executed.
301305

306+
### `<BulkUpdateFormButton>`
307+
308+
This component, part of the [enterprise edition](https://marmelab.com/ra-enterprise/modules/ra-form-layout)<img class="icon" src="./img/premium.svg" />, lets users edit multiple records at once. To be used inside [the `<Datagrid bulkActionButtons>` prop](./Datagrid.md#bulkactionbuttons).
309+
310+
The button opens a dialog containing the form passed as children. When the form is submitted, it will call the dataProvider's `updateMany` method with the ids of the selected records.
311+
312+
<video controls autoplay playsinline muted loop>
313+
<source src="./img/BulkUpdateButton-SimpleForm.webm" type="video/webm"/>
314+
<source src="./img/BulkUpdateButton-SimpleForm.mp4" type="video/mp4"/>
315+
Your browser does not support the video tag.
316+
</video>
317+
318+
#### Usage
319+
320+
`<BulkUpdateFormButton>` can be used inside `<Datagrid>`'s `bulkActionButtons`.
321+
322+
```tsx
323+
import * as React from 'react';
324+
import {
325+
Admin,
326+
BooleanField,
327+
BooleanInput,
328+
Datagrid,
329+
DateField,
330+
DateInput,
331+
List,
332+
Resource,
333+
SimpleForm,
334+
TextField,
335+
} from 'react-admin';
336+
import { BulkUpdateFormButton } from '@react-admin/ra-form-layout';
337+
338+
import { dataProvider } from './dataProvider';
339+
import { i18nProvider } from './i18nProvider';
340+
341+
export const App = () => (
342+
<Admin dataProvider={dataProvider} i18nProvider={i18nProvider}>
343+
<Resource name="posts" list={PostList} />
344+
</Admin>
345+
);
346+
347+
const PostBulkUpdateButton = () => (
348+
<BulkUpdateFormButton>
349+
<SimpleForm>
350+
<DateInput source="published_at" />
351+
<BooleanInput source="is_public" />
352+
</SimpleForm>
353+
</BulkUpdateFormButton>
354+
);
355+
356+
const PostList = () => (
357+
<List>
358+
<Datagrid bulkActionButtons={<PostBulkUpdateButton />}>
359+
<TextField source="id" />
360+
<TextField source="title" />
361+
<DateField source="published_at" />
362+
<BooleanField source="is_public" />
363+
</Datagrid>
364+
</List>
365+
);
366+
```
367+
368+
**Tip:** You are not limited to using a `<SimpleForm>` as children. You can for instance use an `<InputSelectorForm>`, which allows to select the fields to update. Check out the [`<InputSelectorForm>`](#usage-with-inputselectorform) below for more information.
369+
370+
#### Props
371+
372+
| Prop | Required | Type | Default | Description |
373+
|-------------------|--------------|----------|-----------------|------------------------------------------------------------------------------------------------------------------------------------|
374+
| `children` | Required (*) | Element | - | A form component to render inside the Dialog |
375+
| `DialogProps` | - | Object | - | Additional props to pass to the [MUI Dialog](https://mui.com/material-ui/react-dialog/) |
376+
| `mutationMode` | - | `string` | `'pessimistic'` | The mutation mode (`'undoable'`, `'pessimistic'` or `'optimistic'`) |
377+
| `mutationOptions` | - | Object | - | Mutation options passed to [react-query](https://tanstack.com/query/v3/docs/react/reference/useMutation) when calling `updateMany` |
378+
379+
380+
#### `children`
381+
382+
`<BulkUpdateFormButton>` expects a form component as children, such as `<SimpleForm>` or `<InputSelectorForm>`.
383+
384+
```tsx
385+
import { BulkUpdateFormButton } from '@react-admin/ra-form-layout';
386+
import * as React from 'react';
387+
import { BooleanInput, DateInput, SimpleForm } from 'react-admin';
388+
389+
const PostBulkUpdateButton = () => (
390+
<BulkUpdateFormButton>
391+
<SimpleForm>
392+
<DateInput source="published_at" />
393+
<BooleanInput source="is_public" />
394+
</SimpleForm>
395+
</BulkUpdateFormButton>
396+
);
397+
```
398+
399+
#### `DialogProps`
400+
401+
The `DialogProps` prop can be used to pass additional props to the [MUI Dialog](https://mui.com/material-ui/react-dialog/).
402+
{% raw %}
403+
```tsx
404+
import { Slide } from '@mui/material';
405+
import { TransitionProps } from '@mui/material/transitions';
406+
import { BulkUpdateFormButton } from '@react-admin/ra-form-layout';
407+
import * as React from 'react';
408+
import { BooleanInput, DateInput, SimpleForm } from 'react-admin';
409+
410+
const Transition = React.forwardRef(function Transition(
411+
props: TransitionProps & {
412+
children: React.ReactElement<any, any>;
413+
},
414+
ref: React.Ref<unknown>
415+
) {
416+
return <Slide direction="left" ref={ref} {...props} />;
417+
});
418+
419+
const PostBulkUpdateButtonWithTransition = () => (
420+
<BulkUpdateFormButton DialogProps={{ TransitionComponent: Transition }}>
421+
<SimpleForm>
422+
<DateInput source="published_at" />
423+
<BooleanInput source="is_public" />
424+
</SimpleForm>
425+
</BulkUpdateFormButton>
426+
);
427+
```
428+
{% endraw %}
429+
430+
#### `mutationMode`
431+
432+
Use the `mutationMode` prop to specify the [mutation mode](https://marmelab.com/react-admin/Edit.html#mutationmode).
433+
434+
```tsx
435+
import { BulkUpdateFormButton } from '@react-admin/ra-form-layout';
436+
import * as React from 'react';
437+
import { BooleanInput, DateInput, SimpleForm } from 'react-admin';
438+
439+
const PostBulkUpdateButton = () => (
440+
<BulkUpdateFormButton mutationMode="undoable">
441+
<SimpleForm>
442+
<DateInput source="published_at" />
443+
<BooleanInput source="is_public" />
444+
</SimpleForm>
445+
</BulkUpdateFormButton>
446+
);
447+
```
448+
449+
#### `mutationOptions` and `meta`
450+
451+
The `mutationOptions` prop can be used to pass options to the [react-query mutation](https://react-query.tanstack.com/reference/useMutation#options) used to call the dataProvider's `updateMany` method.
452+
453+
{% raw %}
454+
```tsx
455+
import { BulkUpdateFormButton } from '@react-admin/ra-form-layout';
456+
import * as React from 'react';
457+
import { BooleanInput, DateInput, SimpleForm } from 'react-admin';
458+
459+
const PostBulkUpdateButton = () => (
460+
<BulkUpdateFormButton mutationOptions={{ retry: false }}>
461+
<SimpleForm>
462+
<DateInput source="published_at" />
463+
<BooleanInput source="is_public" />
464+
</SimpleForm>
465+
</BulkUpdateFormButton>
466+
);
467+
```
468+
{% endraw %}
469+
470+
You can also use this prop to pass a `meta` object, that will be passed to the dataProvider when calling `updateMany`.
471+
{% raw %}
472+
```tsx
473+
import { BulkUpdateFormButton } from '@react-admin/ra-form-layout';
474+
import * as React from 'react';
475+
import { BooleanInput, DateInput, SimpleForm } from 'react-admin';
476+
477+
const PostBulkUpdateButton = () => (
478+
<BulkUpdateFormButton mutationOptions={{ meta: { foo: 'bar' } }}>
479+
<SimpleForm>
480+
<DateInput source="published_at" />
481+
<BooleanInput source="is_public" />
482+
</SimpleForm>
483+
</BulkUpdateFormButton>
484+
);
485+
```
486+
{% endraw %}
487+
488+
#### Usage with `<TabbedForm>` or other location based form layouts
489+
490+
`<BulkUpdateFormButton>` can be used with any form layout. However, for form layouts that are based on location by default, such as [`<TabbedForm>`](https://marmelab.com/react-admin/TabbedForm.html), you will need to disable the location syncing feature, as it may conflict with the Edit route declared by React Admin (`/<resource>/<id>`).
491+
492+
For instance, with `<TabbedForm>`, you can use the `syncWithLocation` prop to disable it:
493+
494+
```tsx
495+
import { BulkUpdateFormButton } from '@react-admin/ra-form-layout';
496+
import * as React from 'react';
497+
import { BooleanInput, DateInput, TabbedForm } from 'react-admin';
498+
499+
const PostBulkUpdateButton = () => (
500+
<BulkUpdateFormButton>
501+
<TabbedForm syncWithLocation={false}>
502+
<TabbedForm.Tab label="Publication">
503+
<DateInput source="published_at" />
504+
</TabbedForm.Tab>
505+
<TabbedForm.Tab label="Visibility">
506+
<BooleanInput source="is_public" />
507+
</TabbedForm.Tab>
508+
</TabbedForm>
509+
</BulkUpdateFormButton>
510+
);
511+
```
512+
513+
#### Usage With `<InputSelectorForm>`
514+
515+
`<BulkUpdateFormButton>` works best with `<InputSelectorForm>`, which component renders a form allowing to select the fields to update in a record.
516+
517+
<video controls autoplay playsinline muted loop>
518+
<source src="./img/BulkUpdateButton-InputSelectorForm.webm" type="video/webm"/>
519+
<source src="./img/BulkUpdateButton-InputSelectorForm.mp4" type="video/mp4"/>
520+
Your browser does not support the video tag.
521+
</video>
522+
523+
`<InputSelectorForm>` expects a list of inputs passed in the `inputs` prop. Each input must have a `label` and an `element`.
524+
525+
```tsx
526+
import {
527+
BulkUpdateFormButton,
528+
InputSelectorForm,
529+
} from '@react-admin/ra-form-layout';
530+
import * as React from 'react';
531+
import { BooleanInput, DateInput } from 'react-admin';
532+
533+
const PostBulkUpdateButton = () => (
534+
<BulkUpdateFormButton>
535+
<InputSelectorForm
536+
inputs={[
537+
{
538+
label: 'Published at',
539+
element: <DateInput source="published_at" />,
540+
},
541+
{
542+
label: 'Is public',
543+
element: <BooleanInput source="is_public" />,
544+
},
545+
]}
546+
/>
547+
</BulkUpdateFormButton>
548+
);
549+
```
550+
551+
Use the `inputs` prop to specify the list of inputs from which the user can pick. Each input must have a `label` and an `element`.
552+
553+
```tsx
554+
import { InputSelectorForm } from '@react-admin/ra-form-layout';
555+
import * as React from 'react';
556+
import {
557+
BooleanInput,
558+
DateInput,
559+
SelectArrayInput,
560+
TextInput,
561+
} from 'react-admin';
562+
563+
const PostEdit = () => (
564+
<InputSelectorForm
565+
inputs={[
566+
{
567+
label: 'Title',
568+
element: <TextInput source="title" />,
569+
},
570+
{
571+
label: 'Body',
572+
element: <TextInput source="body" multiline />,
573+
},
574+
{
575+
label: 'Published at',
576+
element: <DateInput source="published_at" />,
577+
},
578+
{
579+
label: 'Is public',
580+
element: <BooleanInput source="is_public" />,
581+
},
582+
{
583+
label: 'Tags',
584+
element: (
585+
<SelectArrayInput
586+
source="tags"
587+
choices={[
588+
{ id: 'react', name: 'React' },
589+
{ id: 'vue', name: 'Vue' },
590+
{ id: 'solid', name: 'Solid' },
591+
{ id: 'programming', name: 'Programming' },
592+
]}
593+
/>
594+
),
595+
},
596+
]}
597+
/>
598+
);
599+
```
600+
601+
#### Limitations
602+
603+
If you look under the hood, you will see that `<BulkUpdateFormButton>` provides a `<SaveContext>` to its children, which allows them to call `updateMany` with the ids of the selected records.
604+
605+
However since we are in the context of a list, there is no `<RecordContext>` available. Hence, the following inputs cannot work inside a `<BulkUpdateFormButton>`:
606+
607+
- `<ReferenceOneInput>`
608+
- `<ReferenceManyInput>`
609+
- `<ReferenceManyToManyInput>`
610+
302611
### `<FilterButton>`
303612

304613
This button is an internal component used by react-admin in [the Filter button/form combo](./FilteringTutorial.md#the-filter-buttonform-combo).

docs/Datagrid.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,6 @@ Finally, `<Datagrid>` inspects children for props that indicate how it should be
147147
Your browser does not support the video tag.
148148
</video>
149149

150-
151150
Bulk action buttons are buttons that affect several records at once, like mass deletion for instance. In the `<Datagrid>` component, the bulk actions toolbar appears when a user ticks the checkboxes in the first column of the table. The user can then choose a button from the bulk actions toolbar. By default, all Datagrids have a single bulk action button, the bulk delete button. You can add other bulk action buttons by passing a custom element as the `bulkActionButtons` prop of the `<Datagrid>` component:
152151

153152
```jsx
@@ -173,7 +172,12 @@ export const PostList = () => (
173172
);
174173
```
175174

176-
**Tip**: React-admin provides three components that you can use in `bulkActionButtons`: [`<BulkDeleteButton>`](./Buttons.md#bulkdeletebutton), [`<BulkUpdateButton>`](./Buttons.md#bulkupdatebutton), and [`<BulkExportButton>`](./Buttons.md#bulkexportbutton).
175+
**Tip**: React-admin provides four components that you can use in `bulkActionButtons`:
176+
177+
- [`<BulkDeleteButton>`](./Buttons.md#bulkdeletebutton) (enabled by default)
178+
- [`<BulkExportButton>`](./Buttons.md#bulkexportbutton) to export only the selection
179+
- [`<BulkUpdateButton>`](./Buttons.md#bulkupdatebutton) to immediately update the selection
180+
- [`<BulkUpdateFormButton>`](./Buttons.md#bulkupdateformbutton) to display a form allowing to update the selection
177181

178182
**Tip**: You can also disable bulk actions altogether by passing `false` to the `bulkActionButtons` prop. In this case, the checkboxes column doesn't show up.
179183

1.12 MB
Binary file not shown.
1020 KB
Binary file not shown.
387 KB
Binary file not shown.
378 KB
Binary file not shown.

0 commit comments

Comments
 (0)