Skip to content

Commit ede0683

Browse files
authored
Merge pull request #9866 from marmelab/merge-master
Backport master to next
2 parents d30c4da + 1b0ca9f commit ede0683

37 files changed

+513
-123
lines changed

CHANGELOG.md

+14-1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,19 @@
6161
* [TypeScript] Make types more strict in ra-core, part II ([#9743](https://github.com/marmelab/react-admin/pull/9743)) ([fzaninotto](https://github.com/fzaninotto))
6262
* [TypeScript] Make types more strict in ra-core ([#9741](https://github.com/marmelab/react-admin/pull/9741)) ([fzaninotto](https://github.com/fzaninotto))
6363

64+
## v4.16.18
65+
66+
* Fix `<Datagrid>` uses wrong element for "Select All" label ([#9826](https://github.com/marmelab/react-admin/pull/9826)) ([fzaninotto](https://github.com/fzaninotto))
67+
* Fix `<ListView>` crashes when inside a tab ([#9824](https://github.com/marmelab/react-admin/pull/9824)) ([fzaninotto](https://github.com/fzaninotto))
68+
* Fix warning about `defaultProps` in React 18.3 ([#9832](https://github.com/marmelab/react-admin/pull/9832)) ([djhi](https://github.com/djhi))
69+
* Bump ejs from 3.1.8 to 3.1.10 ([#9814](https://github.com/marmelab/react-admin/pull/9814)) ([dependabot bot](https://github.com/dependabot))
70+
* [Doc] Improve doc for `<Autocomplete onCreate>` and similar props ([#9858](https://github.com/marmelab/react-admin/pull/9858)) ([fzaninotto](https://github.com/fzaninotto))
71+
* [Doc] Add missing `fetchUtils` import to make custom httpClient snippet clearer in TypeScript ([#9843](https://github.com/marmelab/react-admin/pull/9843)) ([adguernier](https://github.com/adguernier))
72+
* [Doc] update italian locale reference ([#9830](https://github.com/marmelab/react-admin/pull/9830)) ([christianascone](https://github.com/christianascone))
73+
* [Doc] Fix Input usage mentions `disabled` instead of `readOnly` ([#9825](https://github.com/marmelab/react-admin/pull/9825)) ([fzaninotto](https://github.com/fzaninotto))
74+
* [Typescript] Fix compilation error in `<MenuItemLink>`, `<ResettableTextField>` and `<InspectorButton>` with latest `@types/react` ([#9853](https://github.com/marmelab/react-admin/pull/9853)) ([ilia-os](https://github.com/ilia-os))
75+
* [Storybook] Fix `<TitlePortal>` stories ([#9834](https://github.com/marmelab/react-admin/pull/9834)) ([djhi](https://github.com/djhi))
76+
6477
## v4.16.17
6578

6679
* Fix combineDataProviders doesn't work when returned by an async function ([#9798](https://github.com/marmelab/react-admin/pull/9798)) ([fzaninotto](https://github.com/fzaninotto))
@@ -72,7 +85,7 @@
7285
* [Doc] Update third-party Inputs to add link to Google Places AutocompleteInput ([#9771](https://github.com/marmelab/react-admin/pull/9771)) ([quentin-decre](https://github.com/quentin-decre))
7386
* [Doc] Update `<Search>` and `<SearchWithResult>` to introduce `queryOptions` ([#9779](https://github.com/marmelab/react-admin/pull/9779)) ([erwanMarmelab](https://github.com/erwanMarmelab))
7487
* [Doc] Update RBAC to better explain the difference between the built-in actions ([#9766](https://github.com/marmelab/react-admin/pull/9766)) ([slax57](https://github.com/slax57))
75-
* [Doc] Fix `<SimpleForm>` has wrong import for `<RichTextInput>` ([#9775](https://github.com/marmelab/react-admin/pull/9775)) ([anthonycmain](https://github.com/anthonycmain))
88+
* [Doc] Fix `<SimpleForm>` has wrong import for `<RichTextInput>` ([#9775](https://github.com/marmelab/react-admin/pull/9775)) ([anthonycmain](https://github.com/anthonycmain))
7689
* [Doc] Fix `<RichTextInput>` typo on TipTap ([#9759](https://github.com/marmelab/react-admin/pull/9759)) ([adguernier](https://github.com/adguernier))
7790
* [Doc] Update `<JsonSchemaForm>` to add details about available widgets ([#9758](https://github.com/marmelab/react-admin/pull/9758)) ([adguernier](https://github.com/adguernier))
7891
* [TypeScript] Fix warning in `create-react-admin` ([#9728](https://github.com/marmelab/react-admin/pull/9728)) ([hbendev](https://github.com/hbendev))

docs/AutocompleteInput.md

+67-16
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,48 @@ const CreateCategory = () => {
219219
```
220220
{% endraw %}
221221

222+
If you want to customize the label of the "Create XXX" option, use [the `createItemLabel` prop](#createitemlabel).
223+
222224
If you just need to ask users for a single string to create the new option, you can use [the `onCreate` prop](#oncreate) instead.
223225

226+
## `createLabel`
227+
228+
When you set the `create` or `onCreate` prop, `<AutocompleteInput>` lets users create new options. By default, it renders a "Create" menu item at the bottom of the list. You can customize the label of that menu item by setting a custom translation for the `ra.action.create` key in the translation files.
229+
230+
![Create Label](./img/AutocompleteInput-createLabel.png)
231+
232+
Or, if you want to customize it just for this `<AutocompleteInput>`, use the `createLabel` prop:
233+
234+
You can customize the label of that menu item by setting a custom translation for the `ra.action.create` key in the translation files.
235+
236+
```jsx
237+
<AutocompleteInput
238+
source="author"
239+
choices={authors}
240+
onCreate={onCreate}
241+
createLabel="Start typing to create a new item"
242+
/>
243+
```
244+
245+
## `createItemLabel`
246+
247+
If you set the `create` or `onCreate` prop, `<AutocompleteInput>` lets users create new options. When the text entered by the user doesn't match any option, the input renders a "Create XXX" menu item at the bottom of the list.
248+
249+
![Create Item Label](./img/AutocompleteInput-createItemLabel.png)
250+
251+
You can customize the label of that menu item by setting a custom translation for the `ra.action.create_item` key in the translation files.
252+
253+
Or, if you want to customize it just for this `<AutocompleteInput>`, use the `createItemLabel` prop:
254+
255+
```jsx
256+
<AutocompleteInput
257+
source="author"
258+
choices={authors}
259+
onCreate={onCreate}
260+
createItemLabel="Add a new author: %{item}"
261+
/>
262+
```
263+
224264
## `debounce`
225265

226266
When used inside a [`<ReferenceInput>`](./ReferenceInput.md), `<AutocompleteInput>` will call `dataProvider.getList()` with the current input value as filter after a delay of 250ms. This is to avoid calling the API too often while users are typing their query.
@@ -367,39 +407,50 @@ const BookCreate = () => (
367407

368408
## `onCreate`
369409

370-
Use the `onCreate` prop to allow users to create new options on-the-fly. Its value must be a function. This lets you render a `prompt` to ask users about the new value. You can return either the new choice directly or a Promise resolving to the new choice.
410+
Use the `onCreate` prop to allow users to create new options on the fly. This is equivalent to MUI's `<AutoComplete freeSolo>` prop.
411+
412+
<video controls playsinline muted>
413+
<source src="./img/AutocompleteInput-onCreate.mp4" type="video/mp4"/>
414+
Your browser does not support the video tag.
415+
</video>
416+
417+
`onCreate` must be a function that adds a new choice and returns it. This function can be async. The added choice must use the same format as the other choices (usually `{ id, name }`).
418+
419+
In the following example, users can create a new company by typing its name in the `<AutocompleteInput>`:
371420

372-
{% raw %}
373421
```js
374422
import { AutocompleteInput, Create, SimpleForm, TextInput } from 'react-admin';
375423

376-
const PostCreate = () => {
377-
const categories = [
378-
{ name: 'Tech', id: 'tech' },
379-
{ name: 'Lifestyle', id: 'lifestyle' },
424+
const ContactCreate = () => {
425+
const companies = [
426+
{ id: 1, name: 'Globex Corp.' },
427+
{ id: 2, name: 'Soylent Inc.' },
380428
];
381429
return (
382430
<Create>
383431
<SimpleForm>
384-
<TextInput source="title" />
432+
<TextInput source="first_name" />
433+
<TextInput source="last_name" />
385434
<AutocompleteInput
386-
onCreate={() => {
387-
const newCategoryName = prompt('Enter a new category');
388-
const newCategory = { id: newCategoryName.toLowerCase(), name: newCategoryName };
389-
categories.push(newCategory);
390-
return newCategory;
435+
source="company"
436+
choices={companies}
437+
onCreate={companyName => {
438+
const newCompany = { id: companies.length + 1, name: companyName };
439+
companies.push(newCompany);
440+
return newCompany;
391441
}}
392-
source="category"
393-
choices={categories}
394442
/>
395443
</SimpleForm>
396444
</Create>
397445
);
398446
}
399447
```
400-
{% endraw %}
401448

402-
If a prompt is not enough, you can use [the `create` prop](#create) to render a custom component instead.
449+
If you want to customize the label of the "Create XXX" option, use [the `createItemLabel` prop](#createitemlabel).
450+
451+
When used inside a `<ReferenceInput>`, the `onCreate` prop should create a new record in the reference resource, and return it. See [Creating a New Reference](./ReferenceInput.md#creating-a-new-reference) for more details.
452+
453+
If a function is not enough, you can use [the `create` prop](#create) to render a custom component instead.
403454

404455
## `optionText`
405456

docs/CreateReactAdmin.md

+2
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ title: "The create-react-admin CLI"
77

88
`create-react-admin` is a package that generates a react-admin app scaffolding using [Vite](https://vitejs.dev/). It is designed to work on most setups and produces predictable and consistent results. It's the preferred way to create a new react-admin application.
99

10+
<iframe src="https://www.youtube-nocookie.com/embed/i_TbS7quzww" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen style="aspect-ratio: 16 / 9;width:100%;margin-bottom:1em;"></iframe>
11+
1012
## Usage
1113

1214
Use it by running the following command:

docs/DataProviders.md

+2
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,8 @@ const App = () => (
250250
**Tip**: For TypeScript users, here is a typed version of the `fetchJson` function:
251251

252252
```ts
253+
import { fetchUtils } from "react-admin";
254+
253255
const fetchJson = (url: string, options: fetchUtils.Options = {}) => {
254256
const customHeaders = (options.headers ||
255257
new Headers({

docs/Fields.md

+12-14
Original file line numberDiff line numberDiff line change
@@ -288,18 +288,19 @@ And see [the Material UI system documentation](https://mui.com/system/the-sx-pro
288288

289289
This prop defines the text alignment of the field when rendered inside a `<Datagrid>` cell. By default, datagrid values are left-aligned ; for numeric values, it's often better to right-align them. Set `textAlign` to `right` for that.
290290

291-
[`<NumberField>`](./NumberField.md) already uses `textAlign="right"`. Set the default value for this prop if you create a custom numeric field.
292-
293291
```jsx
294-
const BasketTotal = () => {
295-
const record = useRecordContext();
296-
if (!record) return null;
297-
const total = record.items.reduce((total, item) => total + item.price, 0);
298-
return <span>{total}</span>;
299-
}
300-
BasketTotal.defaultProps = {
301-
textAlign: 'right',
302-
};
292+
import { List, Datagrid, TextField } from 'react-admin';
293+
294+
const PostList = () => (
295+
<List>
296+
<Datagrid>
297+
<TextField source="id" />
298+
<TextField source="title" />
299+
<TextField source="author" />
300+
<TextField source="year" textAlign="right" />
301+
</Datagrid>
302+
</List>
303+
);
303304
```
304305

305306
## Deep Field Source
@@ -387,9 +388,6 @@ const FormattedNumberField = ({ source }) => {
387388
const record = useRecordContext();
388389
return <NumberField sx={{ color: record && record[source] < 0 ? 'red' : '' }} source={source} />;
389390
};
390-
FormattedNumberField.defaultProps = {
391-
textAlign: 'right',
392-
};
393391
```
394392
{% endraw %}
395393

docs/Inputs.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { Edit, SimpleForm, ReferenceInput, SelectInput, TextInput, required } fr
2323
export const PostEdit = () => (
2424
<Edit>
2525
<SimpleForm>
26-
<TextInput disabled source="id" />
26+
<TextInput readOnly source="id" />
2727
<ReferenceInput label="User" source="userId" reference="users" validate={[required()]}>
2828
<SelectInput optionText="name" />
2929
</ReferenceInput>

docs/ReferenceInput.md

+84-26
Original file line numberDiff line numberDiff line change
@@ -166,32 +166,6 @@ You can filter the query used to populate the possible values. Use the `filter`
166166

167167
**Note**: When users type a search term in the `<AutocompleteInput>`, this doesn't affect the `filter` prop. Check the [Customizing the filter query](#customizing-the-filter-query) section below for details on how that filter works.
168168

169-
## `format`
170-
171-
By default, children of `<ReferenceInput>` transform `null` values from the `dataProvider` into empty strings.
172-
173-
If you want to change this behavior, you have to pass a custom `format` prop to the `<ReferenceInput>` *child component*, because **`<ReferenceInput>` doesn't have a `format` prop**. It is the responsibility of the child component to format the input value.
174-
175-
For instance, if you want to transform an option value before rendering, and the selection control is an `<AutocompleteInput>` (the default), set [the `<AutocompleteInput format>` prop](./Inputs.md#format) as follows:
176-
177-
```jsx
178-
import { ReferenceInput, AutocompleteInput } from 'react-admin';
179-
180-
<ReferenceInput source="company_id" reference="companies">
181-
<AutocompleteInput format={value => value == null ? 'not defined' : value} />
182-
</ReferenceInput>
183-
```
184-
185-
The same goes if the child is a `<SelectInput>`:
186-
187-
```jsx
188-
import { ReferenceInput, SelectInput } from 'react-admin';
189-
190-
<ReferenceInput source="company_id" reference="companies">
191-
<SelectInput format={value => value === undefined ? 'not defined' : null} />
192-
</ReferenceInput>
193-
```
194-
195169
## `label`
196170

197171
In an `<Edit>` or `<Create>` view, the `label` prop has no effect. `<ReferenceInput>` has no label, it simply renders its child (an `<AutocompleteInput>` by default). If you need to customize the label, set the `label` prop on the child element:
@@ -327,6 +301,32 @@ Then to display a selector for the contact company, you should call `<ReferenceI
327301
<ReferenceInput source="company_id" reference="companies" />
328302
```
329303

304+
## Transforming The Input Value
305+
306+
By default, children of `<ReferenceInput>` transform `null` values from the `dataProvider` into empty strings.
307+
308+
If you want to change this behavior, you have to pass a custom `format` prop to the `<ReferenceInput>` *child component*, because `<ReferenceInput>` doesn't have a `format` prop. It is the responsibility of the child component to format the input value.
309+
310+
For instance, if you want to transform an option value before rendering, and the selection control is an `<AutocompleteInput>` (the default), set [the `<AutocompleteInput format>` prop](./Inputs.md#format) as follows:
311+
312+
```jsx
313+
import { ReferenceInput, AutocompleteInput } from 'react-admin';
314+
315+
<ReferenceInput source="company_id" reference="companies">
316+
<AutocompleteInput format={value => value == null ? 'not defined' : value} />
317+
</ReferenceInput>
318+
```
319+
320+
The same goes if the child is a `<SelectInput>`:
321+
322+
```jsx
323+
import { ReferenceInput, SelectInput } from 'react-admin';
324+
325+
<ReferenceInput source="company_id" reference="companies">
326+
<SelectInput format={value => value === undefined ? 'not defined' : null} />
327+
</ReferenceInput>
328+
```
329+
330330
## Customizing The Filter Query
331331

332332
By default, `<ReferenceInput>` renders an `<AutocompleteInput>`, which lets users type a search term to filter the possible values. `<ReferenceInput>` calls `dataProvider.getList()` using the search term as filter, using the format `filter: { q: [search term] }`.
@@ -341,6 +341,64 @@ const filterToQuery = searchText => ({ name_ilike: `%${searchText}%` });
341341
</ReferenceInput>
342342
```
343343

344+
## Creating a New Reference
345+
346+
When users don't find the reference they are looking for in the list of possible values, they need to create a new reference. If they have to quit the current form to create the reference, they may lose the data they have already entered. So a common feature for `<ReferenceInput>` is to let users create a new reference on the fly.
347+
348+
<iframe src="https://www.youtube-nocookie.com/embed/CIUp5MF6A1M" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen style="aspect-ratio: 16 / 9;width:100%;margin-bottom:1em;"></iframe>
349+
350+
Children of `<ReferenceInput>` (`<AutocompleteInput>`, `<SelectInput>`, etc.) allow the creation of new choices via the `onCreate` prop. This displays a new "Create new" option in the list of choices. You can leverage this capability to create a new reference record.
351+
352+
The following example is a contact edition form using a `<ReferenceInput>` to select the contact company. Its child `<AutocompleteInput onCreate>` allows to create a new company on the fly if it doesn't exist yet.
353+
354+
```tsx
355+
export const ContactEdit = () => {
356+
const [create] = useCreate();
357+
const notify = useNotify();
358+
const handleCreateCompany = async (companyName?: string) => {
359+
if (!companyName) return;
360+
try {
361+
const newCompany = await create(
362+
'companies',
363+
{ data: { name: companyName } },
364+
{ returnPromise: true }
365+
);
366+
return newCompany;
367+
} catch (error) {
368+
notify('An error occurred while creating the company', {
369+
type: 'error',
370+
});
371+
throw(error);
372+
}
373+
};
374+
return (
375+
<Edit>
376+
<SimpleForm>
377+
<TextInput source="first_name" />
378+
<TextInput source="last_name" />
379+
<ReferenceInput source="company_id" reference="companies">
380+
<AutocompleteInput onCreate={handleCreateCompany} />
381+
</ReferenceInput>
382+
</SimpleForm>
383+
</Edit>
384+
);
385+
};
386+
```
387+
388+
In the example above, the `handleCreateCompany` function creates a new company with the name provided by the user, and returns it so that `<AutocompleteInput>` selects it.
389+
390+
You can learn more about the `onCreate` prop in the documentation of the selection input components:
391+
392+
- [`<AutocompleteInput onCreate>`](./AutocompleteInput.md#oncreate)
393+
- [`<SelectInput onCreate>`](./SelectInput.md#oncreate)
394+
395+
If you need to ask the user for more details about the new reference, you display a custom element (e.g. a dialog) when the user selects the "Create" option. use the `create` prop for that instead of `onCreate`.
396+
397+
You can learn more about the `create` prop in the documentation of the selection input components:
398+
399+
- [`<AutocompleteInput create>`](./AutocompleteInput.md#create)
400+
- [`<SelectInput create>`](./SelectInput.md#create)
401+
344402
## Tree Structure
345403

346404
If the reference resource is a tree, use [`<ReferenceNodeInput>`](./ReferenceNodeInput.md) instead of `<ReferenceInput>`.

0 commit comments

Comments
 (0)