Skip to content

Commit 8869bdc

Browse files
authored
Merge pull request #11078 from marmelab/doc/ra-core-ee-fefa2aa0f16ef7c2cbe90a222f1af2aac464c0e7
[Doc] Update RA Core EE documentation
2 parents 619c987 + ecccf27 commit 8869bdc

File tree

6 files changed

+746
-0
lines changed

6 files changed

+746
-0
lines changed

docs_headless/astro.config.mjs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ export default defineConfig({
120120
'recordsiterator',
121121
'filterliveform',
122122
'withlistcontext',
123+
enterpriseEntry(
124+
'bulkupdateformbase',
125+
'<BulkUpdateFormBase>'
126+
),
123127
'uselist',
124128
'uselistcontext',
125129
'uselistcontroller',
@@ -135,6 +139,11 @@ export default defineConfig({
135139
'createbase',
136140
'editbase',
137141
'form',
142+
enterpriseEntry('autosavebase', '<AutoSaveBase>'),
143+
enterpriseEntry(
144+
'autopersistinstorebase',
145+
'<AutoPersistInStoreBase>'
146+
),
138147
'usecreatecontext',
139148
'usecreatecontroller',
140149
'useeditcontext',
@@ -143,6 +152,11 @@ export default defineConfig({
143152
'useregistermutationmiddleware',
144153
'usesavecontext',
145154
'useunique',
155+
enterpriseEntry('useautosave', 'useAutoSave'),
156+
enterpriseEntry(
157+
'useautopersistinstore',
158+
'useAutoPersistInStore'
159+
),
146160
],
147161
},
148162
{
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
---
2+
title: "<AutoPersistInStoreBase>"
3+
---
4+
5+
A component that saves a form data in the store on change and reapplies it on mount.
6+
It's ideal to ensure users don't lose their already filled data in an edit or a create form when they navigate to another page.
7+
8+
This component prevents data loss in forms by automatically saving the form data in the store when users update it. When users return to the page, it reapplies the saved data to the form.
9+
10+
<video controls autoplay playsinline muted loop>
11+
<source src="https://react-admin-ee.marmelab.com/assets/AutoPersistInStore.mp4" type="video/mp4"/>
12+
Your browser does not support the video tag.
13+
</video>
14+
15+
The temporary form data is only saved when the user navigates away from the page, and it is removed when the user submits the form or closes the tab. Users can opt out of the prefilling by clicking the "Cancel" button in the notification.
16+
17+
Saved data is not sent to the server. It is only persisted using the [store](https://marmelab.com/ra-core/store/) and is removed when the user logs out.
18+
19+
This feature requires a valid is an [Enterprise Edition](https://marmelab.com/ra-enterprise/) subscription.
20+
21+
### Usage
22+
23+
Add `<AutoPersistInStoreBase>` inside a react-admin form:
24+
25+
```tsx
26+
import { AutoPersistInStoreBase, useAutoPersistInStoreContext } from '@react-admin/ra-core-ee';
27+
import { EditBase, Form, Translate, useEvent, useCloseNotification } from 'ra-core';
28+
import { Button, TextInput } from 'my-react-admin-ui-library';
29+
30+
const PostEdit = () => (
31+
<EditBase>
32+
<Form>
33+
<TextInput source="title" />
34+
<TextInput source="teaser" />
35+
<AutoPersistInStoreBase notification={<AutoPersistNotification />} />
36+
</Form>
37+
</EditBase>
38+
);
39+
40+
const AutoPersistNotification = () => {
41+
const closeNotification = useCloseNotification();
42+
const { reset } = useAutoPersistInStoreContext();
43+
44+
const cancel = useEvent((event: React.MouseEvent) => {
45+
event.preventDefault();
46+
reset();
47+
closeNotification();
48+
});
49+
50+
return (
51+
<div>
52+
<Translate i18nKey="ra-form-layout.auto_persist_in_store.applied_changes" />
53+
<Button label="ra.action.cancel" onClick={cancel} />
54+
</div>
55+
);
56+
};
57+
```
58+
59+
The component will automatically save the form data in the store on change and reapply it when the form is mounted again.
60+
61+
It works both on create and edit forms.
62+
63+
### Props
64+
65+
| Prop | Required | Type | Default | Description |
66+
| --------------------- | -------- | ----------- | ------------------------------------ | ------------------------------------ |
67+
| `notification` | Required | `ReactNode` | - | A Notification element. |
68+
| `getStoreKey` | - | `function` | - | Function to use your own store key. |
69+
70+
### `getStoreKey`
71+
72+
To save the current form data in the [store](https://marmelab.com/ra-core/usestorecontext/), `<AutoPersistInStore>` uses the following store key:
73+
74+
`ra-persist-[RESOURCE_NAME]-[RECORD_ID]`
75+
76+
For example, if you are editing a `posts` resource with the ID `123`, the store key will be: `ra-persist-posts-123`. In case of a create form, the record ID is replaced by `"create"`
77+
78+
You can override this key by passing a custom function as the `getStoreKey` prop. It expects two parameters:
79+
80+
- `resource`: The current resource.
81+
- `record`: The current record if you are in an [edit context](https://marmelab.com/ra-core/useeditcontext/).
82+
83+
```tsx
84+
<AutoPersistInStoreBase
85+
getStoreKey={
86+
(resource: ResourceContextValue, record: RaRecord<Identifier> | undefined) =>
87+
`my-custom-persist-key-${resource}-${record && record.hasOwnProperty('id') ? record.id : 'create'}`
88+
}
89+
notification={<AutoPersistNotification />}
90+
/>
91+
```
92+
93+
### `notification`
94+
95+
When `<AutoPersistInStoreBase>` component applies the changes from the store to a form, react-admin informs users with a notification.
96+
The notification element provided will be passed to the `notify` function of the [`useNotify` hook](https://marmelab.com/ra-core/usenotify/).
97+
98+
```tsx
99+
import { AutoPersistInStoreBase, useAutoPersistInStoreContext } from '@react-admin/ra-core-ee';
100+
import { EditBase, Form, Translate, useEvent, useCloseNotification } from 'ra-core';
101+
import { Button, TextInput } from 'my-react-admin-ui-library';
102+
103+
const PostEdit = () => (
104+
<EditBase>
105+
<Form>
106+
<TextInput source="title" />
107+
<TextInput source="teaser" />
108+
<AutoPersistInStoreBase notification={<AutoPersistNotification />} />
109+
</Form>
110+
</EditBase>
111+
);
112+
113+
const AutoPersistNotification = () => {
114+
const closeNotification = useCloseNotification();
115+
const { reset } = useAutoPersistInStoreContext();
116+
117+
// Let users cancel the persisted changes and reset the form to its default values
118+
const cancel = useEvent((event: React.MouseEvent) => {
119+
event.preventDefault();
120+
reset();
121+
closeNotification();
122+
});
123+
124+
return (
125+
<div>
126+
<Translate i18nKey="ra-form-layout.auto_persist_in_store.applied_changes" />
127+
<Button label="ra.action.cancel" onClick={cancel} />
128+
</div>
129+
);
130+
};
131+
```
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
---
2+
title: "<AutoSaveBase>"
3+
---
4+
5+
A component that enables autosaving of the form. It's ideal for long data entry tasks, and reduces the risk of data loss.
6+
7+
<video controls autoplay playsinline muted loop>
8+
<source src="https://react-admin-ee.marmelab.com/assets/AutoSave.mp4" type="video/mp4"/>
9+
Your browser does not support the video tag.
10+
</video>
11+
12+
This feature requires a valid is an [Enterprise Edition](https://marmelab.com/ra-enterprise/) subscription.
13+
14+
## Usage
15+
16+
Put `<AutoSaveBase>` inside a form built with ra-core [`<Form>`](https://marmelab.com/ra-core/form/):
17+
18+
```tsx
19+
import { AutoSaveBase } from '@react-admin/ra-core-ee';
20+
import { EditBase, Form } from 'ra-core';
21+
import { TextInput } from 'my-react-admin-ui-library';
22+
23+
const PostEdit = () => (
24+
<EditBase mutationMode="optimistic">
25+
<Form resetOptions={{ keepDirtyValues: true }}>
26+
<TextInput source="title" />
27+
<TextInput source="teaser" />
28+
<button type="submit">Save</button>
29+
<AutoSaveBase
30+
render={({ error, isSaving, lastSaveAt }) => {
31+
if (error) {
32+
return <span>Error: {error}</span>;
33+
}
34+
if (isSaving) {
35+
return <span>Saving...</span>;
36+
}
37+
if (lastSaveAt) {
38+
return (
39+
<span>
40+
Last saved at{' '}
41+
{new Intl.DateTimeFormat(undefined, {
42+
hour: '2-digit',
43+
minute: '2-digit',
44+
second: '2-digit',
45+
}).format(new Date(lastSaveAt))}
46+
</span>
47+
);
48+
}
49+
}}
50+
/>
51+
</Form>
52+
</EditBase>
53+
);
54+
```
55+
56+
The app will save the current form values after 3 seconds of inactivity.
57+
58+
`<AutoSaveBase>` imposes a few limitations:
59+
60+
- 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.
61+
- In an `<EditBase>` page, you must set [`mutationMode`](https://marmelab.com/ra-core/editbase/#mutationmode) to `pessimistic` or `optimistic` (`<AutoSaveBase>` doesn't work with the default `mutationMode="undoable"`).
62+
- You can't use `<Form warnWhenUnsavedChanges>` with this component. `<AutoSaveBase>` implements its own similar mechanism, and it's enabled by default. You can disable it with the [`disableWarnWhenUnsavedChanges` prop](#disablewarnwhenunsavedchanges).
63+
- It requires that you use a Data Router. This is the default for react-admin apps, but if you're using a custom router, you may need to adjust your configuration. Check the react-router documentation about [Using a Data Router with react-router v6](https://reactrouter.com/6.22.3/routers/picking-a-router) or [Using a Data Router with react-router v7](https://reactrouter.com/7.2.0/start/framework/custom).
64+
65+
## Props
66+
67+
| Prop | Required | Type | Default | Description |
68+
| ------------------------------- | -------- | --------------- | --------- | -------------------------------------------------------------------------- |
69+
| `children` | - | Element | | The content to display by leveraging `AutoSaveContext` |
70+
| `render` | - | Function | | A function to render the content. |
71+
| `debounce` | - | number | 3000 (3s) | The interval in milliseconds between two autosaves. |
72+
| `onSuccess` | - | function | | A callback to call when the save request succeeds. |
73+
| `onError` | - | function | | A callback to call when the save request fails. |
74+
| `transform` | - | function | | A function to transform the data before saving. |
75+
| `disableWarnWhenUnsavedChanges` | - | boolean | false | A boolean indicating whether users should be warned when they close the browser tab or navigate away from the application if they have unsaved changes. |
76+
77+
## `children`
78+
79+
You can pass a children to `<AutoSaveBase>` and leverage its `AutoSaveContext` with the `useAutoSaveContext` hook:
80+
81+
```tsx
82+
import { AutoSaveBase, useAutoSaveContext } from '@react-admin/ra-core-ee';
83+
import { EditBase, Form } from 'ra-core';
84+
import { TextInput } from 'my-react-admin-ui-library';
85+
86+
const AutoSaveContent = () => {
87+
const { error, isSaving, lastSaveAt } = useAutoSaveContext();
88+
89+
if (error) {
90+
return <span>Error: {error}</span>;
91+
}
92+
if (isSaving) {
93+
return <span>Saving...</span>;
94+
}
95+
if (lastSaveAt) {
96+
return (
97+
<span>
98+
Last saved at{' '}
99+
{new Intl.DateTimeFormat(undefined, {
100+
hour: '2-digit',
101+
minute: '2-digit',
102+
second: '2-digit',
103+
}).format(new Date(lastSaveAt))}
104+
</span>
105+
);
106+
}
107+
108+
return null;
109+
}
110+
111+
const PostEdit = () => (
112+
<EditBase mutationMode="optimistic">
113+
<Form resetOptions={{ keepDirtyValues: true }}>
114+
<TextInput source="title" />
115+
<TextInput source="teaser" />
116+
<button type="submit">Save</button>
117+
<AutoSaveBase>
118+
<AutoSaveContent />
119+
</AutoSaveBase>
120+
</Form>
121+
</EditBase>
122+
);
123+
```
124+
125+
## `debounce`
126+
127+
The interval in milliseconds between two autosaves. Defaults to 3000 (3s).
128+
129+
```tsx
130+
<AutoSaveBase debounce={5000} />
131+
```
132+
133+
## `onSuccess`
134+
135+
A callback to call when the save request succeeds.
136+
137+
```tsx
138+
const [lastSave, setLastSave] = useState();
139+
140+
<AutoSaveBase
141+
onSuccess={() => setLastSave(new Date())}
142+
/>
143+
```
144+
145+
## `onError`
146+
147+
A callback to call when the save request fails.
148+
149+
```tsx
150+
const [error, setError] = useState();
151+
152+
<AutoSaveBase
153+
onError={error => setError(error)}
154+
/>
155+
```
156+
157+
## `transform`
158+
159+
A function to transform the data before saving.
160+
161+
```tsx
162+
<AutoSaveBase
163+
transform={data => ({
164+
...data,
165+
fullName: `${data.firstName} ${data.lastName}`
166+
})}
167+
/>
168+
```
169+
170+
## `disableWarnWhenUnsavedChanges`
171+
172+
A boolean indicating whether users should be warned when they close the browser tab or navigate away from the application if they have unsaved changes.
173+
174+
```tsx
175+
<AutoSaveBase disableWarnWhenUnsavedChanges />
176+
```
177+
178+
## `render`
179+
180+
You can pass a `render` prop instead of [`children`](#children) to render a UI for the auto save feature:
181+
182+
```tsx
183+
import { AutoSaveBase } from '@react-admin/ra-core-ee';
184+
185+
const AutoSave = () => (
186+
<AutoSaveBase
187+
render={({ error, isSaving, lastSaveAt }) => {
188+
if (error) {
189+
return <span>Error: {error}</span>;
190+
}
191+
if (isSaving) {
192+
return <span>Saving...</span>;
193+
}
194+
if (lastSaveAt) {
195+
return (
196+
<span>
197+
Last saved at{' '}
198+
{new Intl.DateTimeFormat(undefined, {
199+
hour: '2-digit',
200+
minute: '2-digit',
201+
second: '2-digit',
202+
}).format(new Date(lastSaveAt))}
203+
</span>
204+
);
205+
}
206+
}}
207+
/>
208+
);
209+
```

0 commit comments

Comments
 (0)