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 gotcha about <Input disabled> usage and isDirty form state #9386

Merged
merged 2 commits into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
6 changes: 4 additions & 2 deletions docs/Features.md
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,7 @@ React-admin offers a **rich set of input components and form layouts** to build

For instance, here is how to build a tabbed form for editing a blog post:

{% raw %}
```jsx
import {
TabbedForm,
Expand All @@ -445,7 +446,7 @@ export const PostEdit = () => (
<Edit>
<TabbedForm>
<TabbedForm.Tab label="summary">
<TextInput disabled label="Id" source="id" />
<TextInput label="Id" source="id" InputProps={{ disabled: true }} />
<TextInput source="title" validate={required()} />
<TextInput multiline source="teaser" validate={required()} />
</TabbedForm.Tab>
Expand All @@ -457,7 +458,7 @@ export const PostEdit = () => (
<DateInput label="Publication date" source="published_at" />
<NumberInput source="average_note" validate={[ number(), minValue(0) ]} />
<BooleanInput label="Allow comments?" source="commentable" defaultValue />
<TextInput disabled label="Nb views" source="views" />
<TextInput label="Nb views" source="views" InputProps={{ disabled: true }} />
</TabbedForm.Tab>
<TabbedForm.Tab label="comments">
<ReferenceManyField reference="comments" target="post_id" label={false}>
Expand All @@ -472,6 +473,7 @@ export const PostEdit = () => (
</Edit>
);
```
{% endraw %}

<video controls autoplay playsinline muted loop>
<source src="./img/tabbed-form.webm" type="video/webm"/>
Expand Down
8 changes: 8 additions & 0 deletions docs/Inputs.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,14 @@ If `true`, the input is disabled and the user can't change the value.
<TextInput source="title" disabled />
```

**Tip**: The form framework used by react-admin, react-hook-form, [considers](https://github.com/react-hook-form/react-hook-form/pull/10805) that a `disabled` input shouldn't submit any value. So react-hook-form sets the value of all `disabled` inputs to `undefined`. As a consequence, a form with a `disabled` input is always considered `dirty` (i.e. react-hook-form considers that the form values and the initial record values are different), and it triggers [the `warnWhenUnsavedChanges` feature](./EditTutorial.md#warning-about-unsaved-changes) when leaving the form, even though the user changed nothing. The workaround is to set the `disabled` prop on the underlying input component, as follows:

{% raw %}
```jsx
<TextInput source="title" InputProps={{ disabled: true }} />
```
{% endraw %}

## `format`

The `format` prop accepts a callback taking the value from the form state, and returning the input value (which should be a string).
Expand Down
4 changes: 2 additions & 2 deletions docs/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,13 +82,13 @@ const PostTitle = () => {
export const PostEdit = () => (
<Edit title={<PostTitle />}>
<SimpleForm>
<TextInput disabled source="id" />
<TextInput source="id" InputProps={{ disabled: true }} />
<TextInput source="title" />
<TextInput source="teaser" options={{ multiline: true }} />
<TextInput multiline source="body" />
<DateInput label="Publication date" source="published_at" />
<TextInput source="average_note" />
<TextInput disabled label="Nb views" source="views" />
<TextInput label="Nb views" source="views" InputProps={{ disabled: true }}/>
</SimpleForm>
</Edit>
);
Expand Down
4 changes: 3 additions & 1 deletion docs/Tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -703,12 +703,13 @@ export const App = () => (

You can now adjust the `<PostEdit>` component to disable the edition of the primary key (`id`), place it first, and use a textarea for the `body` field, as follows:

{% raw %}
```diff
// in src/posts.tsx
export const PostEdit = () => (
<Edit>
<SimpleForm>
+ <TextInput source="id" disabled />
+ <TextInput source="id" InputProps={{ disabled: true }} />
<ReferenceInput source="userId" reference="users" link="show" />
- <TextInput source="id" />
<TextInput source="title" />
Expand All @@ -718,6 +719,7 @@ export const PostEdit = () => (
</Edit>
);
```
{% endraw %}

If you've understood the `<List>` component, the `<Edit>` component will be no surprise. It's responsible for fetching the record, and displaying the page title. It passes the record down to the `<SimpleForm>` component, which is responsible for the form layout, default values, and validation. Just like `<Datagrid>`, `<SimpleForm>` uses its children to determine the form inputs to display. It expects *input components* as children. `<TextInput>` and `<ReferenceInput>` are such inputs.

Expand Down
6 changes: 5 additions & 1 deletion examples/simple/src/comments/CommentEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,11 @@ const CommentEdit = props => {
onSubmit={save}
warnWhenUnsavedChanges
>
<TextInput disabled source="id" fullWidth />
<TextInput
source="id"
fullWidth
InputProps={{ disabled: true }}
/>
<ReferenceInput
source="post_id"
reference="posts"
Expand Down
7 changes: 5 additions & 2 deletions examples/simple/src/posts/PostEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,10 @@ const PostEdit = () => {
justifyContent="space-between"
fullWidth
>
<TextInput disabled source="id" />
<TextInput
InputProps={{ disabled: true }}
source="id"
/>
<TextInput
source="title"
validate={required()}
Expand Down Expand Up @@ -224,7 +227,7 @@ const PostEdit = () => {
validate={[required(), number(), minValue(0)]}
/>
<BooleanInput source="commentable" defaultValue />
<TextInput disabled source="views" />
<TextInput InputProps={{ disabled: true }} source="views" />
<ArrayInput source="pictures">
<SimpleFormIterator>
<TextInput source="url" defaultValue="" />
Expand Down
4 changes: 3 additions & 1 deletion examples/simple/src/users/UserEdit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ const UserEditForm = () => {
onSubmit={newSave}
>
<TabbedForm.Tab label="user.form.summary" path="">
{permissions === 'admin' && <TextInput disabled source="id" />}
{permissions === 'admin' && (
<TextInput source="id" InputProps={{ disabled: true }} />
)}
<TextInput
source="name"
defaultValue="slim shady"
Expand Down