Skip to content

Commit

Permalink
Merge pull request #10423 from marmelab/merge-master
Browse files Browse the repository at this point in the history
Merge 5.4.3 into next
  • Loading branch information
djhi authored Dec 19, 2024
2 parents a1b7be0 + ad6bf18 commit 0b3eec7
Show file tree
Hide file tree
Showing 44 changed files with 1,876 additions and 420 deletions.
1 change: 1 addition & 0 deletions docs/ArrayInput.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ To edit arrays of data embedded inside a record, `<ArrayInput>` creates a list o
}
```

**Tip**: If you need to edit an array of *strings*, like a list of email addresses or a list of tags, you should use a [`<TextArrayInput>`](./TextArrayInput.md) instead.

`<ArrayInput>` expects a single child, which must be a *form iterator* component. A form iterator is a component rendering a field array (the object returned by react-hook-form's [`useFieldArray`](https://react-hook-form.com/docs/usefieldarray)). For instance, [the `<SimpleFormIterator>` component](./SimpleFormIterator.md) displays an array of react-admin Inputs in an unordered list (`<ul>`), one sub-form by list item (`<li>`). It also provides controls for adding and removing a sub-record.

Expand Down
2 changes: 1 addition & 1 deletion docs/AutocompleteArrayInput.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@ It renders using Material UI [Autocomplete](https://mui.com/material-ui/react-au
Your browser does not support the video tag.
</video>


This input allows editing values that are arrays of scalar values, e.g. `[123, 456]`.

**Tip**: React-admin includes other components allowing the edition of such values:

- [`<TextArrayInput>`](./TextArrayInput.md) lets you edit an array of strings
- [`<SelectArrayInput>`](./SelectArrayInput.md) renders a dropdown list of choices
- [`<CheckboxGroupInput>`](./CheckboxGroupInput.md) renders a list of checkbox options
- [`<DualListInput>`](./DualListInput.md) renders a list of choices that can be moved from one list to another
Expand Down
1 change: 0 additions & 1 deletion docs/AutocompleteInput.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ It renders using [Material UI's `<Autocomplete>`](https://mui.com/material-ui/re
Your browser does not support the video tag.
</video>


This input allows editing record fields that are scalar values, e.g. `123`, `'admin'`, etc.

## Usage
Expand Down
1 change: 1 addition & 0 deletions docs/CheckboxGroupInput.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ This input allows editing values that are arrays of scalar values, e.g. `[123, 4

**Tip**: React-admin includes other components allowing the edition of such values:

- [`<TextArrayInput>`](./TextArrayInput.md) lets you edit an array of strings
- [`<SelectArrayInput>`](./SelectArrayInput.md) renders a dropdown list of choices
- [`<AutocompleteArrayInput>`](./AutocompleteArrayInput.md) renders an autocomplete input of choices
- [`<DualListInput>`](./DualListInput.md) renders a list of choices that can be moved from one list to another
Expand Down
2 changes: 1 addition & 1 deletion docs/Community.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ On our [Youtube channel](https://www.youtube.com/@react-admin), you can find som

<div class="iframe-wrapper">
<iframe
src="https://www.youtube.com/embed?listType=playlist&list=UUx-g3k3eDFyy-3E7vxWVN-Q">
src="https://www.youtube.com/embed/videoseries?si=-m56_m4W9dEcJjw5&amp;list=PLBCskKfEEuGvgjdpfvH4ZAIGx4UJAUNwP">
</iframe>
</div>

Expand Down
2 changes: 1 addition & 1 deletion docs/Create.md
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ You can do the same for error notifications, by passing a custom `onError` call

You sometimes need to pre-populate a record based on a *related* record. For instance, to create a comment related to an existing post.

By default, the `<Create>` view starts with an empty `record`. However, if the `location` object (injected by [react-router-dom](https://reacttraining.com/react-router/web/api/location)) contains a `record` in its `state`, the `<Create>` view uses that `record` instead of the empty object. That's how the `<CloneButton>` works under the hood.
By default, the `<Create>` view starts with an empty `record`. However, if the `location` object (injected by [react-router-dom](https://reactrouter.com/6.28.0/start/concepts#locations)) contains a `record` in its `state`, the `<Create>` view uses that `record` instead of the empty object. That's how the `<CloneButton>` works under the hood.

That means that if you want to create a link to a creation form, presetting *some* values, all you have to do is to set the `state` prop of the `<CreateButton>`:

Expand Down
1 change: 1 addition & 0 deletions docs/DualListInput.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ This input allows editing values that are arrays of scalar values, e.g. `[123, 4

**Tip**: React-admin includes other components allowing the edition of such values:

- [`<TextArrayInput>`](./TextArrayInput.md) lets you edit an array of strings
- [`<AutocompleteArrayInput>`](./AutocompleteArrayInput.md) renders an Autocomplete
- [`<SelectArrayInput>`](./SelectArrayInput.md) renders a dropdown list of choices
- [`<CheckboxGroupInput>`](./CheckboxGroupInput.md) renders a list of checkbox options
Expand Down
53 changes: 53 additions & 0 deletions docs/Edit.md
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,59 @@ You can do the same for error notifications, by passing a custom `onError` call

**Tip**: The notification message will be translated.

## Prefilling the Form

You sometimes need to pre-populate the form changes to a record. For instance, to revert a record to a previous version, or to make some changes while letting users modify others fields as well.

By default, the `<Edit>` view starts with the current `record`. However, if the `location` object (injected by [react-router-dom](https://reactrouter.com/6.28.0/start/concepts#locations)) contains a `record` in its `state`, the `<Edit>` view uses that `record` to prefill the form.

That means that if you want to create a link to an edition view, modifying immediately *some* values, all you have to do is to set the `state` prop of the `<EditButton>`:

{% raw %}
```jsx
import * as React from 'react';
import { EditButton, Datagrid, List } from 'react-admin';

const ApproveButton = () => {
return (
<EditButton
state={{ record: { status: 'approved' } }}
/>
);
};

export default PostList = () => (
<List>
<Datagrid>
...
<ApproveButton />
</Datagrid>
</List>
)
```
{% endraw %}

**Tip**: The `<Edit>` component also watches the "source" parameter of `location.search` (the query string in the URL) in addition to `location.state` (a cross-page message hidden in the router memory). So the `ApproveButton` could also be written as:

{% raw %}
```jsx
import * as React from 'react';
import { EditButton } from 'react-admin';

const ApproveButton = () => {
return (
<EditButton
to={{
search: `?source=${JSON.stringify({ status: 'approved' })}`,
}}
/>
);
};
```
{% endraw %}

Should you use the location `state` or the location `search`? The latter modifies the URL, so it's only necessary if you want to build cross-application links (e.g. from one admin to the other). In general, using the location `state` is a safe bet.

## Editing A Record In A Modal

`<Edit>` is designed to be a page component, passed to the `edit` prop of the `<Resource>` component. But you may want to let users edit a record from another page.
Expand Down
2 changes: 1 addition & 1 deletion docs/Inputs.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ React-admin provides a set of Input components, each one designed for a specific
| Tree node | `42` | [`<TreeInput>`](./TreeInput.md) |
| Foreign key | `42` | [`<ReferenceInput>`](./ReferenceInput.md) |
| Array of objects | `[{ item: 'jeans', qty: 3 }, { item: 'shirt', qty: 1 }]` | [`<ArrayInput>`](./ArrayInput.md) |
| Array of Enums | `['foo', 'bar']` | [`<SelectArrayInput>`](./SelectArrayInput.md), [`<AutocompleteArrayInput>`](./AutocompleteArrayInput.md), [`<CheckboxGroupInput>`](./CheckboxGroupInput.md), [`<DualListInput>`](./DualListInput.md) |
| Array of Enums | `['foo', 'bar']` | [`<TextArrayInput>`](./TextArrayinput.md), [`<SelectArrayInput>`](./SelectArrayInput.md), [`<AutocompleteArrayInput>`](./AutocompleteArrayInput.md), [`<CheckboxGroupInput>`](./CheckboxGroupInput.md), [`<DualListInput>`](./DualListInput.md) |
| Array of foreign keys | `[42, 43]` | [`<ReferenceArrayInput>`](./ReferenceArrayInput.md) |
| Translations | `{ en: 'Hello', fr: 'Bonjour' }` | [`<TranslatableInputs>`](./TranslatableInputs.md) |
| Related records | `[{ id: 42, title: 'Hello' }, { id: 43, title: 'World' }]` | [`<ReferenceManyInput>`](./ReferenceManyInput.md), [`<ReferenceManyToManyInput>`](./ReferenceManyToManyInput.md), [`<ReferenceNodeInput>`](./ReferenceNodeInput.md), [`<ReferenceOneInput>`](./ReferenceOneInput.md) |
Expand Down
2 changes: 2 additions & 0 deletions docs/Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ title: "Index"
* [`<TabbedForm>`](./TabbedForm.md)
* [`<TabbedFormWithRevision>`](./TabbedForm.md#versioning)<img class="icon" src="./img/premium.svg" />
* [`<TabbedShowLayout>`](./TabbedShowLayout.md)
* [`<TextArrayInput>`](./TextArrayInput.md)
* [`<TextField>`](./TextField.md)
* [`<TextInput>`](./TextInput.md)
* [`<TimeInput>`](./TimeInput.md)
Expand Down Expand Up @@ -296,6 +297,7 @@ title: "Index"

**- R -**
* [`useRecordContext`](./useRecordContext.md)
* [`useRecordFromLocation`](./useRecordFromLocation.md)
* [`useRedirect`](./useRedirect.md)
* [`useReference`](./useGetOne.md#aggregating-getone-calls)
* [`useRefresh`](./useRefresh.md)
Expand Down
1 change: 1 addition & 0 deletions docs/SelectArrayInput.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ This input allows editing values that are arrays of scalar values, e.g. `[123, 4

**Tip**: React-admin includes other components allowing the edition of such values:

- [`<TextArrayInput>`](./TextArrayInput.md) lets you edit an array of strings
- [`<AutocompleteArrayInput>`](./AutocompleteArrayInput.md) renders an Autocomplete
- [`<CheckboxGroupInput>`](./CheckboxGroupInput.md) renders a list of checkbox options
- [`<DualListInput>`](./DualListInput.md) renders a list of choices that can be moved from one list to another
Expand Down
58 changes: 30 additions & 28 deletions docs/SimpleList.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const PostList = () => (
primaryText={record => record.title}
secondaryText={record => `${record.views} views`}
tertiaryText={record => new Date(record.published_at).toLocaleDateString()}
linkType={record => record.canEdit ? "edit" : "show"}
rowClick={(id, resource, record) => record.canEdit ? "edit" : "show"}
rowSx={record => ({ backgroundColor: record.nb_views >= 500 ? '#efe' : 'white' })}
/>
</List>
Expand All @@ -44,7 +44,7 @@ export const PostList = () => (
| `primaryText` | Optional | mixed | record representation | The primary text to display. |
| `secondaryText` | Optional | mixed | | The secondary text to display. |
| `tertiaryText` | Optional | mixed | | The tertiary text to display. |
| `linkType` | Optional |mixed | `"edit"` | The target of each item click. |
| `rowClick` | Optional |mixed | `"edit"` | The action to trigger when the user clicks on a row. |
| `leftAvatar` | Optional | function | | A function returning an `<Avatar>` component to display before the primary text. |
| `leftIcon` | Optional | function | | A function returning an `<Icon>` component to display before the primary text. |
| `rightAvatar` | Optional | function | | A function returning an `<Avatar>` component to display after the primary text. |
Expand Down Expand Up @@ -80,31 +80,6 @@ This prop should be a function returning an `<Avatar>` component. When present,

This prop should be a function returning an `<Icon>` component. When present, the `<ListItem>` renders a `<ListIcon>` before the `<ListItemText>`

## `linkType`

The `<SimpleList>` items link to the edition page by default. You can also set the `linkType` prop to `show` directly to link to the `<Show>` page instead.

```jsx
import { List, SimpleList } from 'react-admin';

export const PostList = () => (
<List>
<SimpleList
primaryText={record => record.title}
secondaryText={record => `${record.views} views`}
tertiaryText={record => new Date(record.published_at).toLocaleDateString()}
linkType="show"
/>
</List>
);
```

`linkType` accepts the following values:

* `linkType="edit"`: links to the edit page. This is the default behavior.
* `linkType="show"`: links to the show page.
* `linkType={false}`: does not create any link.

## `primaryText`

The `primaryText`, `secondaryText` and `tertiaryText` props can accept 4 types of values:
Expand Down Expand Up @@ -190,6 +165,33 @@ This prop should be a function returning an `<Avatar>` component. When present,

This prop should be a function returning an `<Icon>` component. When present, the `<ListItem>` renders a `<ListIcon>` after the `<ListItemText>`.

## `rowClick`

The `<SimpleList>` items link to the edition page by default. You can also set the `rowClick` prop to `show` directly to link to the `<Show>` page instead.

```jsx
import { List, SimpleList } from 'react-admin';

export const PostList = () => (
<List>
<SimpleList
primaryText={record => record.title}
secondaryText={record => `${record.views} views`}
tertiaryText={record => new Date(record.published_at).toLocaleDateString()}
rowClick="show"
/>
</List>
);
```

`rowClick` accepts the following values:

* `rowClick="edit"`: links to the edit page. This is the default behavior.
* `rowClick="show"`: links to the show page.
* `rowClick={false}`: does not link to anything.
* `rowClick="/custom"`: links to a custom path.
* `rowClick={(id, resource, record) => path}`: path can be any of the above values

## `rowStyle`

*Deprecated - use [`rowSx`](#rowsx) instead.*
Expand Down Expand Up @@ -254,7 +256,7 @@ export const PostList = () => {
primaryText={record => record.title}
secondaryText={record => `${record.views} views`}
tertiaryText={record => new Date(record.published_at).toLocaleDateString()}
linkType={record => record.canEdit ? "edit" : "show"}
rowClick={(id, resource, record) => record.canEdit ? "edit" : "show"}
/>
) : (
<Datagrid>
Expand Down
110 changes: 110 additions & 0 deletions docs/TextArrayInput.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
---
layout: default
title: "The TextArrayInput Component"
---

# `<TextArrayInput>`

`<TextArrayInput>` lets you edit an array of strings, like a list of email addresses or a list of tags. It renders as an input where the current values are represented as chips. Users can add or delete new values.

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


## Usage

Use `<TextArrayInput>` to edit an array of strings:

```jsx
import { Create, SimpleForm, TextArrayInput, TextInput } from 'react-admin';

export const EmailCreate = () => (
<Create>
<SimpleForm>
<TextArrayInput source="to" />
<TextInput source="subject" />
<TextInput source="body" multiline minRows={5} />
</SimpleForm>
</Create>
);
```

This form will allow users to input multiple email addresses in the `to` field. The resulting email will look like this:

```jsx
{
"to": ["[email protected]", "[email protected]"],
"subject": "Request for a quote",
"body": "Hi,\n\nI would like to know if you can provide a quote for the following items:\n\n- 100 units of product A\n- 50 units of product B\n- 25 units of product C\n\nBest regards,\n\nJulie\n",
"id": 123,
"date": "2024-11-26T11:37:22.564Z",
"from": "[email protected]",
}
```

`<TextArrayInput>` is designed for simple string arrays. For more complex use cases, consider the following alternatives:

- [`<SelectArrayInput>`](./SelectArrayInput.md) or [`<AutocompleteArrayInput>`](./AutocompleteArrayInput.md) if the possible values are limited to a predefined list.
- [`<ReferenceArrayInput>`](./ReferenceArrayInput.md) if the possible values are stored in another resource.
- [`<ArrayInput>`](./ArrayInput.md) if the stored value is an array of *objects* instead of an array of strings.

## Props

| Prop | Required | Type | Default | Description |
| ------------ | -------- | --------- | ------- | -------------------------------------------------------------------- |
| `options` | Optional | `string[]` | | Optional list of possible values for the input. If provided, the input will suggest these values as the user types. |
| `renderTags` | Optional | `(value, getTagProps) => ReactNode` | | A function to render selected value. |

`<TextArrayInput>` also accepts the [common input props](./Inputs.md#common-input-props).

Additional props are passed down to the underlying Material UI [`<Autocomplete>`](https://mui.com/material-ui/react-autocomplete/) component.

## `options`

You can make show a list of suggestions to the user by setting the `options` prop:

```jsx
<TextArrayInput
source="to"
options={[
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
'[email protected]',
]}
/>
```

## `renderTags`

To customize the rendering of the chips, use the `renderTags` prop. This prop is a function that takes two arguments:

- `value`: The input value (an array of strings)
- `getTagProps`: A props getter for an individual tag.

```tsx
<TextArrayInput
source="to"
renderTags={(value: readonly string[], getTagProps) =>
value.map((option: string, index: number) => {
const { key, ...tagProps } = getTagProps({ index });
return (
<Chip
variant="outlined"
label={option}
key={key}
{...tagProps}
/>
);
})
}
/>
```
Binary file added docs/img/TextArrayInput.mp4
Binary file not shown.
Loading

0 comments on commit 0b3eec7

Please sign in to comment.