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

[Doc] Add <AutoSave> documentation #8969

Merged
merged 3 commits into from
Jun 5, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
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
462 changes: 454 additions & 8 deletions docs/AccordionForm.md

Large diffs are not rendered by default.

141 changes: 141 additions & 0 deletions docs/AutoSave.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
---
layout: default
title: "The AutoSave Component"
---

# `<AutoSave>`

This [Enterprise Edition](https://marmelab.com/ra-enterprise)<img class="icon" src="./img/premium.svg" /> component enables autosaving of the form. Alternative to [`<SaveButton>`](./SaveButton.md), it's ideal for long data entry tasks, and reduces the risk of data loss.

<video controls autoplay playsinline muted loop>
<source src="./img/AutoSave.webm" type="video/webm"/>
<source src="./img/AutoSave.mp4" type="video/mp4"/>
Your browser does not support the video tag.
</video>

Test it live on [the Enterprise Edition Storybook](https://react-admin.github.io/ra-enterprise/?path=/story/ra-form-layout-autosave-optimistic--in-simple-form).

## Usage

Put `<AutoSave>` inside a react-admin form ([`<SimpleForm>`](./SimpleForm.md), [`<TabbedForm>`](./TabbedForm.md), [`<LongForm>`](./LongForm.md), etc.), for instance in a custom toolbar. The component renders nothing by default. It will save the current form values 3 seconds after the last change, and render a message when the save succeeds or fails.

Note that you **must** set the `<Form resetOptions>` prop to `{ keepDirtyValues: true }`. If you forget that prop, any change entered by the end user after the autosave but before its acknowledgement by the server will be lost.

If you're using it in an `<Edit>` page, you must also use a `pessimistic` or `optimistic` [`mutationMode`](https://marmelab.com/react-admin/Edit.html#mutationmode) - `<AutoSave>` doesn't work with the default `mutationMode="undoable"`.

{% raw %}
```tsx
import { AutoSave } from '@react-admin/ra-form-layout';
import { Edit, SimpleForm, TextInput, DateInput, SelectInput, Toolbar } from 'react-admin';

const AutoSaveToolbar = () => (
<Toolbar>
<AutoSave />
</Toolbar>
);

const PersonEdit = () => (
<Edit mutationMode="optimistic">
<SimpleForm
resetOptions={{ keepDirtyValues: true }}
toolbar={AutoSaveToolbar}
>
<TextInput source="first_name" />
<TextInput source="last_name" />
<DateInput source="dob" />
<SelectInput source="sex" choices={[
{ id: 'male', name: 'Male' },
{ id: 'female', name: 'Female' },
]}/>
</SimpleForm>
</Edit>
);
```
{% endraw %}

The app will save the current form values after 3 seconds of inactivity.

You can use a toolbar containing both a `<SaveButton>` and an `<AutoSave>` component. The `<SaveButton>` will let the user save the form immediately, while the `<AutoSave>` will save the form after 3 seconds of inactivity.

```tsx
const AutoSaveToolbar = () => (
<Toolbar>
<SaveButton />
<AutoSave />
</Toolbar>
);
```

## Props

| Prop | Required | Type | Default | Description |
| ----------- | -------- | -------------- | ------- | ----------------------------------------- |
| `debounce` | Optional | `number` | 3000 | The interval in ms between two saves |
| `onSuccess` | Optional | `function` | - | A callback to call when the save succeeds |
| `onError` | Optional | `function` | - | A callback to call when the save fails |
| `transform` | Optional | `function` | - | A function to transform the data before saving |

## `useAutoSave`

If you want an autosave feature with another user interface, you can leverage the `useAutoSave` hook. It's used internally by `<AutoSave>`, and has the same constraints (it works for the `pessimistic` and `optimistic` [`mutationMode`](https://marmelab.com/react-admin/Edit.html#mutationmode) but not for the `undoable`).

`useAutoSave` expects an options argument with the following fields, all optional:

- `debounce`: The interval in ms between two saves. Defaults to 3000 (3s).
- `onSuccess`: A callback to call when the save request succeeds.
- `onError`: A callback to call when the save request fails.
- `transform`: A function to transform the data before saving.

Note that you **must** add the `resetOptions` prop with `{ keepDirtyValues: true }` to avoid having the user changes overridden by the latest update operation result.

{% raw %}
```tsx
import { useAutoSave } from '@react-admin/ra-form-layout';
import { Edit, SaveButton, SimpleForm, TextInput, Toolbar } from 'react-admin';

const AutoSave = () => {
const [lastSave, setLastSave] = useState();
const [error, setError] = useState();
useAutoSave({
interval: 5000,
onSuccess: () => setLastSave(new Date()),
onError: error => setError(error),
});
return (
<div>
{lastSave && <p>Saved at {lastSave.toLocaleString()}</p>}
{error && <p>Error: {error}</p>}
</div>
);
};

const AutoSaveToolbar = () => (
<Toolbar>
<SaveButton />
<AutoSave />
</Toolbar>
);

const PostEdit = () => (
<Edit mutationMode="optimistic">
<SimpleForm
resetOptions={{ keepDirtyValues: true }}
toolbar={AutoSaveToolbar}
>
<TextInput source="title" />
<TextInput source="teaser" />
</SimpleForm>
</Edit>
);
```
{% endraw %}

`usaAutoSave` returns a boolean indicating whether the form is currently being saved.

```jsx
const isSaving = useAutoSave({
interval: 5000,
onSuccess: () => setLastSave(new Date()),
onError: error => setError(error),
});
```
55 changes: 55 additions & 0 deletions docs/EditTutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -545,6 +545,15 @@ export const PostCreate = () => (

## Validating User Input

React-admin supports the most common validation strategies:

* [per field validators](./Validation.md#per-input-validation-built-in-field-validators),
* [form validation](./Validation.md#global-validation),
* [validation schema powered by yup or zod](./Validation.html#schema-validation),
* [server-side validation](./Validation.md#server-side-validation).

![Validation example](./img/validation.png)

Form validation deserves a section of its own ; check [the Validation chapter](./Validation.md) for more details.

## Altering the Form Values Before Submitting
Expand Down Expand Up @@ -751,6 +760,52 @@ export const PostEdit = () => (
);
```

## AutoSave

In forms where users may spend a lot of time, it's a good idea to save the form automatically after a few seconds of inactivity. You can auto save the form content by using [the `<AutoSave>` component](./AutoSave.md).

<video controls autoplay playsinline muted loop>
<source src="./img/AutoSave.webm" type="video/webm"/>
<source src="./img/AutoSave.mp4" type="video/mp4"/>
Your browser does not support the video tag.
</video>

{% raw %}
```tsx
import { AutoSave } from '@react-admin/ra-form-layout';
import { Edit, SimpleForm, TextInput, DateInput, SelectInput, Toolbar } from 'react-admin';

const AutoSaveToolbar = () => (
<Toolbar>
<AutoSave />
</Toolbar>
);

const PersonEdit = () => (
<Edit mutationMode="optimistic">
<SimpleForm
resetOptions={{ keepDirtyValues: true }}
toolbar={AutoSaveToolbar}
>
<TextInput source="first_name" />
<TextInput source="last_name" />
<DateInput source="dob" />
<SelectInput source="sex" choices={[
{ id: 'male', name: 'Male' },
{ id: 'female', name: 'Female' },
]}/>
</SimpleForm>
</Edit>
);
```
{% endraw %}

Note that you **must** set the `<SimpleForm resetOptions>` prop to `{ keepDirtyValues: true }`. If you forget that prop, any change entered by the end user after the autosave but before its acknowledgement by the server will be lost.

If you're using it in an `<Edit>` page, you must also use a `pessimistic` or `optimistic` [`mutationMode`](https://marmelab.com/react-admin/Edit.html#mutationmode) - `<AutoSave>` doesn't work with the default `mutationMode="undoable"`.

Check [the `<AutoSave>` component](./AutoSave.md) documentation for more details.

## Adding Fields With Labels

All react-admin inputs handle the display of their label by wrapping their content inside a `<Labeled>` component.
Expand Down
88 changes: 58 additions & 30 deletions docs/Features.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,21 +228,19 @@ const BookList = () => (
Your browser does not support the video tag.
</video>

React-admin supports **one-to-many**, **many-to-one**, **one-to-one**, and **many-to-many relationships**. Check the following components to learn more about relationships:

- [`ReferenceField`](./ReferenceField.md)
- [`ReferenceArrayField`](./ReferenceArrayField.md)
- [`ReferenceManyField`](./ReferenceManyField.md)
- [`ReferenceManyCount`](./ReferenceManyCount.md)
- [`ReferenceManyToManyField`](./ReferenceManyToManyField.md)
- [`ReferenceOneField`](./ReferenceOneField.md)
- [`ReferenceInput`](./ReferenceInput.md)
- [`ReferenceArrayInput`](./ReferenceArrayInput.md)
- [`ReferenceManyInput`](./ReferenceManyInput.md)
- [`ReferenceManyToManyInput`](./ReferenceManyToManyInput.md)
- [`ReferenceOneInput`](./ReferenceOneInput.md)

The [Fields For Relationships](./FieldsForRelationships.md) page lists all reference fields together with their common usage.
React-admin supports **one-to-many**, **many-to-one**, **one-to-one**, and **many-to-many relationships**. The [Fields For Relationships](./FieldsForRelationships.md) page lists all reference fields together with their common usage. Check the following components to learn more about relationships:

- [`<ReferenceField>`](./ReferenceField.md)
- [`<ReferenceArrayField>`](./ReferenceArrayField.md)
- [`<ReferenceManyField>`](./ReferenceManyField.md)
- [`<ReferenceManyCount>`](./ReferenceManyCount.md)
- [`<ReferenceManyToManyField>`](./ReferenceManyToManyField.md)
- [`<ReferenceOneField>`](./ReferenceOneField.md)
- [`<ReferenceInput>`](./ReferenceInput.md)
- [`<ReferenceArrayInput>`](./ReferenceArrayInput.md)
- [`<ReferenceManyInput>`](./ReferenceManyInput.md)
- [`<ReferenceManyToManyInput>`](./ReferenceManyToManyInput.md)
- [`<ReferenceOneInput>`](./ReferenceOneInput.md)

Reference components are a tremendous development accelerator for complex frontend features. They also liberate the backend developers from the burden of implementing complex joins.

Expand Down Expand Up @@ -293,9 +291,9 @@ Guesser components start by fetching data from the API, analyzing the shape of t

Check the following components to learn more about guessers:

- [`ListGuesser`](./ListGuesser.md)
- [`EditGuesser`](./EditGuesser.md)
- [`ShowGuesser`](./ShowGuesser.md)
- [`<ListGuesser>`](./ListGuesser.md)
- [`<EditGuesser>`](./EditGuesser.md)
- [`<ShowGuesser>`](./ShowGuesser.md)

## Search & Filtering

Expand Down Expand Up @@ -422,9 +420,9 @@ Check the [Building A Custom Filter Tutorial](./FilteringTutorial.md#building-a-

Many admin apps let users perform complex tasks implying the update of many fields and records. To allow such complex workflows, developers must be able to build sophisticated forms, with elaborate validation rules.

React-admin offers a **rich set of input components** to build forms, powered by [Material UI](https://mui.com/material-ui/getting-started/overview/) and [react-hook-form](https://react-hook-form.com/). React-admin's form components also take care of binding the form values to the record being edited and validating the form inputs.
React-admin offers a **rich set of input components and form layouts** to build forms, powered by [Material UI](https://mui.com/material-ui/getting-started/overview/) and [react-hook-form](https://react-hook-form.com/). React-admin's form components also take care of binding the form values to the record being edited and validating the form inputs.

For instance, here is how to group inputs into tabs using `<TabbedForm>`:
For instance, here is how to build a tabbed form for editing a blog post:

```jsx
import {
Expand Down Expand Up @@ -479,18 +477,21 @@ export const PostEdit = () => (
Your browser does not support the video tag.
</video>

### Form Layouts

React-admin offers, out of the box, several **form layouts**:
React-admin offers, out of the box, several form layouts:

- [`SimpleForm`](./SimpleForm.md) for a single-column layout
- [`TabbedForm`](./TabbedForm.md) for a tabbed layout
- [`AccordionForm`](./AccordionForm.md) for long forms with collapsible sections
- [`LongForm`](./LongForm.md) for long forms with a navigation sidebar
- [`WizardForm`](./WizardForm.md) for multi-step forms
- [`EditInDialog`](./EditInDialog.md) for sub-forms in a modal dialog
- [`<SimpleForm>`](./SimpleForm.md) for a single-column layout
- [`<TabbedForm>`](./TabbedForm.md) for a tabbed layout
- [`<AccordionForm>`](./AccordionForm.md) for long forms with collapsible sections
- [`<LongForm>`](./LongForm.md) for long forms with a navigation sidebar
- [`<WizardForm>`](./WizardForm.md) for multi-step forms
- [`<EditInDialog>`](./EditInDialog.md) for sub-forms in a modal dialog
- and [`Form`](./Form.md), a headless component to use as a base for your custom layouts

Inside forms, you can use [react-admin input components](./Inputs.md), designed for many types of data:
### Input Components

Inside forms, you can use specialize [input components](./Inputs.md), designed for many types of data:

| Data Type | Example value | Input Components |
|-----------------------|--------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
Expand All @@ -514,7 +515,9 @@ Inside forms, you can use [react-admin input components](./Inputs.md), designed
| Translations | `{ en: 'Hello', fr: 'Bonjour' }` | [`<TranslatableInputs>`](./TranslatableInputs.md) |
| Related records | `[{ id: 42, title: 'Hello' }, { id: 43, title: 'World' }]` | [`<ReferenceManyInput>`](./ReferenceManyInput.md), [`<ReferenceManyToManyInput>`](./ReferenceManyToManyInput.md), [`<ReferenceOneInput>`](./ReferenceOneInput.md) |

You can build **dependent inputs**, using the [react-hook-form's `useWatch` hook](https://react-hook-form.com/api/usewatch). For instance, here is a `CityInput` that displays the cities of the selected country:
### Dependent Inputs

You can build dependent inputs, using the [react-hook-form's `useWatch` hook](https://react-hook-form.com/api/usewatch). For instance, here is a `CityInput` that displays the cities of the selected country:

```jsx
import * as React from 'react';
Expand Down Expand Up @@ -553,7 +556,20 @@ const OrderEdit = () => (
export default OrderEdit;
```

For validation, you can use react-admin's [per field validators](https://marmelab.com/react-admin/Validation.html#per-input-validation-built-in-field-validators), or a [validation schema powered by yup or zod](https://marmelab.com/react-admin/Validation.html#schema-validation). Here is an example of per-field validation:
### Validation

React-admin ships with a powerful and versatile validation engine.

![Validation example](./img/validation.png)

React-admin forms support the most common validation strategies:

* [per field validators](./Validation.md#per-input-validation-built-in-field-validators),
* [form validation](./Validation.md#global-validation),
* [validation schema powered by yup or zod](./Validation.html#schema-validation),
* [server-side validation](./Validation.md#server-side-validation).

Here is an example of per-field validation:

```jsx
import {
Expand Down Expand Up @@ -591,6 +607,18 @@ export const UserCreate = () => (
);
```

### AutoSave

React-admin lets you build forms saving changes automatically with [`<AutoSave>`](./AutoSave.md), so that users never lose their changes.

<video controls autoplay playsinline muted loop>
<source src="./img/AutoSave.webm" type="video/webm"/>
<source src="./img/AutoSave.mp4" type="video/mp4"/>
Your browser does not support the video tag.
</video>

### JSON Schema Forms

Finally, you can generate entire **forms based on a JSON schema**, using the [`<JsonSchemaForm>` component](./JsonSchemaForm.md).

{% raw %}
Expand Down
Loading
Loading