diff --git a/docs/AutocompleteArrayInput.md b/docs/AutocompleteArrayInput.md index e787630443a..66b811b91c9 100644 --- a/docs/AutocompleteArrayInput.md +++ b/docs/AutocompleteArrayInput.md @@ -16,8 +16,10 @@ This input allows editing values that are arrays of scalar values, e.g. `[123, 4 - [``](./SelectArrayInput.md) renders a dropdown list of choices - [``](./CheckboxGroupInput.md) renders a list of checkbox options +- [``](./DualListInput.md) renders a list of choices that can be moved from one list to another **Tip**: `` is a stateless component, so it only allows to *filter* the list of choices, not to *extend* it. If you need to populate the list of choices based on the result from a `fetch` call (and if [``](./ReferenceArrayInput.md) doesn't cover your need), you'll have to [write your own Input component](./Inputs.md#writing-your-own-input-component) based on MUI `` component. + ## Usage In addition to the `source`, `` requires one prop: the `choices` listing the possible values. diff --git a/docs/CheckboxGroupInput.md b/docs/CheckboxGroupInput.md index 1604cc26082..4b983e1b86f 100644 --- a/docs/CheckboxGroupInput.md +++ b/docs/CheckboxGroupInput.md @@ -15,6 +15,7 @@ This input allows editing values that are arrays of scalar values, e.g. `[123, 4 - [``](./SelectArrayInput.md) renders a dropdown list of choices - [``](./AutocompleteArrayInput.md) renders an autocomplete input of choices +- [``](./DualListInput.md) renders a list of choices that can be moved from one list to another And if you are looking for a way to edit a list of embedded objects (e.g. `[{ id: 123, title: 'Hello' }, { id: 456, title: 'World' }]`), check the [``](./ArrayInput.md) component. diff --git a/docs/DualListInput.md b/docs/DualListInput.md index 93df8347e25..8cb1cf3abdb 100644 --- a/docs/DualListInput.md +++ b/docs/DualListInput.md @@ -5,19 +5,298 @@ title: "The DualListInput Component" # `` -This [Enterprise Edition](https://marmelab.com/ra-enterprise) component allows to edit array values, one-to-many or many-to-many relationships by moving items from one list to another. It's a good alternative to [``](./SelectArrayInput.md) for a small number of choices. +This [Enterprise Edition](https://marmelab.com/ra-enterprise) component allows to edit array values, one-to-many or many-to-many relationships by moving items from one list to another. ![DualListInput](https://marmelab.com/ra-enterprise/modules/assets/ra-relationships-duallistinput.gif) +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: + +- [``](./AutocompleteArrayInput.md) renders an Autocomplete +- [``](./SelectArrayInput.md) renders a dropdown list of choices +- [``](./CheckboxGroupInput.md) renders a list of checkbox options + ## Usage +In addition to the `source`, `` requires one prop: the `choices` listing the possible values. + ```jsx -import { ReferenceArrayInput } from "react-admin"; +import { Create, SimpleForm } from 'react-admin'; import { DualListInput } from "@react-admin/ra-relationships"; - - -; +const UserCreate = () => ( + + + + + +); +``` + +By default, the possible choices are built from the `choices` prop, using: + - the `id` field as the option value, + - the `name` field as the option text + +The form value for the source must be an array of the selected values, e.g. + +```js +{ + id: 123, + name: 'John Doe', + roles: ['u001', 'u003'], +} ``` Check [the `ra-relationships` documentation](https://marmelab.com/ra-enterprise/modules/ra-relationships) for more details. + +## Props + +| Prop | Required | Type | Default | Description | +|------------------------|----------|--------------------------------------------------------------|---------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| `choices` | Optional | `Object[]` | - | List of items to show as options. Required unless inside a ReferenceArray Input. | +| `addButton` | Optional | 'outlined' | 'contained' | 'text' | `element` | - | A MUI `variant` value for the add button or a React element to replace it. | +| `addButtonLabel` | Optional | `string` | `ra-relationships. duallistinput. select` | The text or translation key to use as the label for the add button | +| `availableItems Label` | Optional | `string` | `ra-relationships. duallistinput. availableItems` | The text or translation key to use as the label for the list of available choices | +| `dense` | Optional | `boolean` | `false` | Visual density of the list component | +| `disableValue` | Optional | `string` | 'disabled' | The custom field name used in `choices` to disable some choices | +| `optionText` | Optional | `string` | `Function` | `name` | Field name of record to display in the suggestion item or function which accepts the current record as argument (`record => {string}`) | +| `optionValue` | Optional | `string` | `id` | Field name of record containing the value to use as input value | +| `removeButton` | Optional | 'outlined' | 'contained' | 'text' | `element` | - | A MUI `variant` value for the remove button or a React element to replace it. | +| `removeButton Label` | Optional | `string` | `ra-relationships duallistinput. unselect` | The text or translation key to use as the label for the remove button | +| `selectedItems Label` | Optional | `string` | `ra-relationships. duallistinput. selectedItems` | The text or translation key to use as the label for the list of selected choices | +| `translateChoice` | Optional | `boolean` | `true` | Whether the choices should be translated | + +`` also accepts the [common input props](./Inputs.md#common-input-props). + +## `choices` + +The list of choices must be an array of objects - one object for each possible choice. In each object, `id` is the value, and the `name` is the label displayed to the user. + +```jsx + +``` + +You can render some options as disabled by setting the `disabled` field in some choices: + +```jsx + +``` + +You can also use an array of objects with different properties for the label and value, given you specify the [`optionText`](#optiontext) and [`optionValue`](#optionvalue) props: + +```jsx + +``` + +The choices are translated by default, so you can use translation identifiers as choices: + +```jsx +const choices = [ + { id: 'admin', name: 'myroot.roles.admin' }, + { id: 'u001', name: 'myroot.roles.u001' }, + { id: 'u002', name: 'myroot.roles.u002' }, + { id: 'u003', name: 'myroot.roles.u003' }, +]; +``` + +You can opt-out of this translation by setting [the `translateChoice` prop](#translatechoice) to `false`. + +If you need to *fetch* the options from another resource, you're actually editing a one-to-many or a many-to-many relationship. In this case, wrap the `` in a [``](./ReferenceArrayInput.md) or a [``](./ReferenceManyToManyInput.md) component. You don't need to specify the `choices` prop - the parent component injects it based on the possible values of the related resource. + +```jsx + + + +``` + +If you have an *array of values* for the options, turn it into an array of objects with the `id` and `name` properties: + +```jsx +const possibleValues = ['programming', 'lifestyle', 'photography']; +const ucfirst = name => name.charAt(0).toUpperCase() + name.slice(1); +const choices = possibleValues.map(value => ({ id: value, name: ucfirst(value) })); + + +``` + +## `disableValue` + +By default, `` renders the choices with the field `disabled` as disabled. + +```jsx +const choices = [ + { _id: 'admin', label: 'Admin', disabled: true }, + { _id: 'u001', label: 'Editor' }, + { _id: 'u002', label: 'Moderator' }, + { _id: 'u003', label: 'Reviewer' }, +]; + +``` + +If you want to use another field to denote disabled options, set the `disableValue` prop. + +```jsx +const choices = [ + { _id: 'admin', label: 'Admin', not_available: true }, + { _id: 'u001', label: 'Editor' }, + { _id: 'u002', label: 'Moderator' }, + { _id: 'u003', label: 'Reviewer' }, +]; + +``` + +## `optionText` + +You can customize the properties to use for the option name (instead of the default `name`) thanks to the `optionText` prop: + +```jsx +const choices = [ + { id: 'admin', label: 'Admin' }, + { id: 'u001', label: 'Editor' }, + { id: 'u002', label: 'Moderator' }, + { id: 'u003', label: 'Reviewer' }, +]; + +``` + +`optionText` is especially useful when the choices are records coming from a `` or a ``. By default, react-admin uses the [`recordRepresentation`](./Resource.md#recordrepresentation) function to display the record label. But if you set the `optionText` prop, react-admin will use it instead. + +```jsx + + + +``` + +`optionText` also accepts a function, so you can shape the option text based on the entire choice object: + +```jsx +const choices = [ + { id: 123, first_name: 'Leo', last_name: 'Tolstoi' }, + { id: 456, first_name: 'Jane', last_name: 'Austen' }, +]; +const optionRenderer = choice => `${choice.first_name} ${choice.last_name}`; + + +``` + +`optionText` also accepts a React Element, that will be rendered inside a [``](./useRecordContext.md) using the related choice as the `record` prop. You can use Field components there. + +```jsx +const choices = [ + { id: 123, first_name: 'Leo', last_name: 'Tolstoi' }, + { id: 456, first_name: 'Jane', last_name: 'Austen' }, +]; + +const FullNameField = () => { + const record = useRecordContext(); + return {record.first_name} {record.last_name}; +} + +}/> +``` + +## `optionValue` + +You can customize the properties to use for the option value (instead of the default `id`) thanks to the `optionValue` prop: + +```jsx +const choices = [ + { _id: 'admin', name: 'Admin' }, + { _id: 'u001', name: 'Editor' }, + { _id: 'u002', name: 'Moderator' }, + { _id: 'u003', name: 'Reviewer' }, +]; + +``` + +## `sx`: CSS API + +The `` component accepts the usual `className` prop. You can also override many styles of the inner components thanks to the `sx` property (as most MUI components, see their [documentation about it](https://mui.com/customization/how-to-customize/#overriding-nested-component-styles)). This property accepts the following subclasses: + +| Rule name | Description | +|------------------------------------|----------------------------------| +| `& .RaDualListInput-main` | Applied to the main container | +| `& .RaDualListInput-label` | Applied to the label | +| `& .RaDualListInput-actions` | Applied to the buttons container | +| `& .RaDualListInput-button` | Applied to each button | +| `& .RaDualListInput-addButton` | Applied to the add button | +| `& .RaDualListInput-removeButton` | Applied to the remove button | +| `& .RaDualListInput-list` | Applied to each list | +| `& .RaDualListInput-listHeader` | Applied to each list header | +| `& .RaDualListInput-selectedList` | Applied to the selected list | +| `& .RaDualListInput-availableList` | Applied to the available list | + +To override the style of all instances of `` using the [MUI style overrides](https://mui.com/customization/globals/#css), use the `RaDualListInput` key. + + +## `translateChoice` + +The choices are translated by default, so you can use translation identifiers as choices: + +```jsx +const choices = [ + { id: 'admin', name: 'myroot.roles.admin' }, + { id: 'u001', name: 'myroot.roles.u001' }, + { id: 'u002', name: 'myroot.roles.u002' }, + { id: 'u003', name: 'myroot.roles.u003' }, +]; +``` + +However, in some cases, you may not want the choice to be translated. Set the `translateChoice` prop to `false` for that purpose. + +```jsx + +``` + +Note that `translateChoice` is set to `false` when `` is a child of ``. + +## Using in a ReferenceArrayInput + +If you want to populate the `choices` attribute with a list of related records, you should decorate `` with [``](./ReferenceArrayInput.md), and leave the `choices` empty: + +```jsx +import { + Create, + DateInput, + ReferenceArrayInput, + SimpleForm, + TextInput, +} from 'react-admin'; +import { DualListInput } from "@react-admin/ra-relationships"; + +export const PostCreate = () => ( + + + + + + + + + + +); +``` + +**Tip**: As it does not provide autocompletion, `` might not be suited when the reference resource has a lot of items. diff --git a/docs/ReferenceArrayInput.md b/docs/ReferenceArrayInput.md index 1f3ef9cd128..245d1d82d20 100644 --- a/docs/ReferenceArrayInput.md +++ b/docs/ReferenceArrayInput.md @@ -7,103 +7,315 @@ title: "The ReferenceArrayInput Component" Use `` to edit an array of reference values, i.e. to let users choose a list of values (usually foreign keys) from another REST endpoint. -`` fetches the related resources (using `dataProvider.getMany()`) as well as possible resources (using `dataProvider.getList()`) in the reference endpoint. +![ReferenceArrayInput](./img/reference-array-input.gif) -For instance, if the post object has many tags, a post resource may look like: +## Usage + +For instance, a post record has a `tag_ids` field, which is an array of foreign keys to tags record. + +``` +┌──────────────┐ ┌────────────┐ +│ post │ │ tags │ +│--------------│ │------------│ +│ id │ ┌───│ id │ +│ title │ │ │ name │ +│ body │ │ └────────────┘ +│ tag_ids │───┘ +└──────────────┘ +``` -```json +To make the `tag_ids` for a `post` editable, use the following: + +```jsx +import { Edit, SimpleForm, TextInput, ReferenceArrayInput } from 'react-admin'; + +const PostEdit = () => ( + + + + + + +); +``` + +`` requires a `source` and a `reference` prop. + +`` uses the array of foreign keys to fetch the related records. It also grabs the list of possible choices for the field. For instance, if the `PostEdit` component above is used to edit the following post: + +```js { - "id": 1234, - "tag_ids": [1, 23, 4] + id: 1234, + title: "Lorem Ipsum", + body: "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", + tag_ids: [1, 23, 4] } ``` -Then `` would fetch a list of tag resources from these two calls: +Then `` will issue the following queries: +```js +dataProvider.getMany('tags', { ids: [1, 23, 4] }); +dataProvider.getList('tags', { + filter: {}, + sort: { field: 'id', order: 'DESC' }, + pagination: { page: 1, perPage: 25 } +}); ``` -http://myapi.com/tags?id=[1,23,4] -http://myapi.com/tags?page=1&perPage=25 + +`` renders an [``](./AutocompleteArrayInput.md) to let the user select the related record. Users can narrow down the choices by typing a search term in the input. This modifies the query sent to the `dataProvider` as follows: + +```js +dataProvider.getList('tags', { + filter: { q: [search term] }, + sort: { field: 'id', order: 'DESC' }, + pagination: { page: 1, perPage: 25 } +}); ``` -Once it receives the deduplicated reference resources, this component delegates rendering to its child component, by providing the possible choices through the `ChoicesContext`. This context value can be accessed with the [`useChoicesContext`](./useChoicesContext.md) hook. +See [Customizing the filter query](#customizing-the-filter-query) below for more information about how to change `filter` prop based on the `` search term. -This means you can use `` with [``](./SelectArrayInput.md), [``](./AutocompleteArrayInput.md), [``](./DualListInput.md), [``](./CheckboxGroupInput.md), or with the component of your choice, provided they detect a `ChoicesContext` is available and get their choices from it. +You can tweak how `` fetches the possible values using the `page`, `perPage`, `sort`, and `filter` props. -The component expects a `source` and a `reference` attributes. For instance, to make the `tag_ids` for a `post` editable: +You can replace the default `` with another choice input, by setting a child component. For instance, to use a ``: ```jsx import { ReferenceArrayInput, SelectArrayInput } from 'react-admin'; - + ``` -![SelectArrayInput](./img/select-array-input.gif) +See the [`children`](#children) section for more details. -## Properties +## Props | Prop | Required | Type | Default | Description | |--------------------|----------|---------------------------------------------|------------------------------------|---------------------------------------------------------------------------------------------------------------------| +| `source` | Required | `string` | - | Name of the entity property to use for the input value | +| `reference` | Required | `string` | '' | Name of the reference resource, e.g. 'posts'. | +| `children` | Optional | `ReactNode` | `` | The actual selection component | +| `enableGet Choices` | Optional | `({q: string}) => boolean` | `() => true` | Function taking the `filterValues` and returning a boolean to enable the `getList` call. | | `filter` | Optional | `Object` | `{}` | Permanent filters to use for getting the suggestion list | -| `label` | Optional | `string` | - | Useful only when `ReferenceArrayInput` is in a Filter array, the label is used as the Filter label. | +| `label` | Optional | `string` | - | Useful only when `ReferenceArrayInput` is in a Filter array, the label is used as the Filter label. | | `page` | Optional | `number` | 1 | The current page number | | `perPage` | Optional | `number` | 25 | Number of suggestions to show | -| `reference` | Required | `string` | '' | Name of the reference resource, e.g. 'posts'. | | `sort` | Optional | `{ field: String, order: 'ASC' or 'DESC' }` | `{ field: 'id', order: 'DESC' }` | How to order the list of suggestions | -| `enableGetChoices` | Optional | `({q: string}) => boolean` | `() => true` | Function taking the `filterValues` and returning a boolean to enable the `getList` call. | **Note**: `` doesn't accept the [common input props](./Inputs.md#common-input-props) ; it is the responsability of children to apply them. -## Usage +## `children` -You can tweak how this component fetches the possible values using the `page`, `perPage`, `sort`, and `filter` props. +By default, `` renders an [``](./AutocompleteArrayInput.md) to let end users select the reference record. + +You can pass a child component to customize the way the reference selector is displayed. + +For instance, to customize the input label set the `label` prop on the child component: -{% raw %} ```jsx -// by default, fetches only the first 25 values. You can extend this limit -// by setting the `perPage` prop. - - +import { ReferenceArrayInput, AutocompleteArrayInput } from 'react-admin'; + + + +``` -// by default, orders the possible values by id desc. You can change this order -// by setting the `sort` prop (an object with `field` and `order` properties). - - +The child can be: + +- [``](./SelectArrayInput.md) +- [``](./AutocompleteArrayInput.md) +- [``](./DualListInput.md) +- [``](./CheckboxGroupInput.md), + +```jsx +import { ReferenceArrayInput, SelectInput } from 'react-admin'; + + + +``` + +You can even use a component of your own as child, provided it detects a `ChoicesContext` is available and gets their choices from it. -// you can filter the query used to populate the possible values. Use the -// `filter` prop for that. +The choices context value can be accessed with the [`useChoicesContext`](./useChoicesContext.md) hook. + +## `enableGetChoices` + +You can make the `getList()` call lazy by using the `enableGetChoices` prop. This prop should be a function that receives the `filterValues` as parameter and return a boolean. This can be useful when using an `` on a resource with a lot of data. The following example only starts fetching the options when the query has at least 2 characters: + +```jsx q.length >= 2} +/> +``` + +## `filter` + +You can filter the query used to populate the possible values. Use the `filter` prop for that. + +{% raw %} +```jsx + +``` +{% endraw %} + +**Note**: When users type a search term in the ``, 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. + +## `format` + +If you want to format the input value before displaying it, you have to pass a custom `format` prop to the `` *child component*, because **`` doesn't have a `format` prop**. It is the responsibility of the child component to format the input value. + +For instance, if you want to transform an option value before rendering, and the selection control is an `` (the default), set [the `` prop](./Inputs.md#format) as follows: + +```jsx +import { ReferenceArrayInput, AutocompleteArrayInput } from 'react-admin'; + + + value == null ? 'not defined' : value} /> + +``` + +The same goes if the child is a ``: + +```jsx +import { ReferenceArrayInput, SelectArrayInput } from 'react-admin'; + + + value === undefined ? 'not defined' : null} /> + +``` + +## `label` + +In an `` or `` view, the `label` prop has no effect. `` has no label, it simply renders its child (an `` by default). If you need to customize the label, set the `label` prop on the child element: + +```jsx +import { ReferenceArrayInput, AutocompleteArrayInput } from 'react-admin'; + + + + +``` + +In a Filter form, react-admin uses the `label` prop to set the Filter label. So in this case, the `label` prop is not ignored, but you also have to set it on the child input. + +```jsx +const filters = [ + + + , +]; +``` + +## `parse` + +By default, children of `` transform the empty form value (an empty string) into `null` before passing it to the `dataProvider`. + +If you want to change this behavior, you have to pass a custom `parse` prop to the `` *child component*, because **`` doesn't have a `parse` prop**. It is the responsibility of the child component to parse the input value. + +For instance, if you want to transform an option value before submission, and the selection control is an `` (the default), set [the `` prop](./Inputs.md#parse) as follows: + +```jsx +import { ReferenceArrayInput, AutocompleteArrayInput } from 'react-admin'; + + + value === 'not defined' ? null : value} /> + +``` + +The same goes if the child is a ``: + +```jsx +import { ReferenceArrayInput, SelectArrayInput } from 'react-admin'; + + + value === 'not defined' ? undefined : null} /> + +``` + +## `perPage` + +By default, `` fetches only the first 25 values. You can extend this limit by setting the `perPage` prop. + +```jsx + +``` + +This prop is mostly useful when using [``](./SelectArrayInput.md) or [``](./CheckboxGroupInput.md) as child, as the default `` child allows to filter the possible choices with a search input. + +## `reference` + +The name of the reference resource. For instance, in a post form, if you want to edit the post tags, the reference should be "tags". + +```jsx + +``` + +`` will use the reference resource [`recordRepresentation`](./Resource.md#recordrepresentation) to display the selected record and the list of possible records. So for instance, if the `tags` resource is defined as follows: + +```jsx + +``` + +Then `` will display the company name in the input and the list of possible values. + +You can override this default by specifying the `optionText` prop in the child component. For instance, for an ``: + +```jsx + + + +``` + +## `sort` + +By default, `` orders the possible values by `id` desc. + +You can change this order by setting the `sort` prop (an object with `field` and `order` properties). + +{% raw %} +```jsx + - - + sort={{ field: 'name', order: 'ASC' }} +/> ``` {% endraw %} -**Tip**: `` can also be used with an `` to allow filtering the choices. By default, it will fetch the choices on mount, but you can prevent this by using the `enableGetChoices`. This prop should be a function that receives the `filterValues` as parameter and return a boolean. In order to also hide the choices when `enableGetChoices` returns `false`, you should use `shouldRenderSuggestions` on the ``: +## `source` + +The name of the property in the record that contains the array of identifiers of the selected record. + +For instance, if a post contains a reference to tags via a `tag_ids` property: + +```js +{ + id: 456, + title: "Hello, world!", + tag_ids: [123, 456] +} +``` + +Then to display a selector for the post tags, you should call `` as follows: ```jsx - (q ? q.length >= 2 : false)} -> - value.length >= 2} - /> - + ``` + +## Customizing The Filter Query + +By default, `` renders an ``, which lets users type a search term to filter the possible values. `` calls `dataProvider.getList()` using the search term as filter, using the format `filter: { q: [search term] }`. + +If you want to customize the conversion between the search term and the query filter to match the filtering capabilities of your API, use the [``](./AutocompleteArrayInput.md#filtertoquery) prop. + +```jsx +const filterToQuery = searchText => ({ name_ilike: `%${searchText}%` }); + + + + +``` \ No newline at end of file diff --git a/docs/ReferenceInput.md b/docs/ReferenceInput.md index 841b7cbcf2a..9b1ab4e8f77 100644 --- a/docs/ReferenceInput.md +++ b/docs/ReferenceInput.md @@ -40,7 +40,7 @@ const ContactEdit = () => ( ); ``` -`` require a `source` and a `reference` prop. +`` requires a `source` and a `reference` prop. `` uses the foreign key value to fetch the related record. It also grabs the list of possible choices for the field. For instance, if the `ContactEdit` component above is used to edit the following contact: diff --git a/docs/SelectArrayInput.md b/docs/SelectArrayInput.md index 8f6eeb654fe..42a20c237bd 100644 --- a/docs/SelectArrayInput.md +++ b/docs/SelectArrayInput.md @@ -9,15 +9,58 @@ To let users choose several values in a list using a dropdown, use ``](./AutocompleteArrayInput.md) renders an Autocomplete +- [``](./CheckboxGroupInput.md) renders a list of checkbox options +- [``](./DualListInput.md) renders a list of choices that can be moved from one list to another + +## Usage + +In addition to the `source`, `` requires one prop: the `choices` listing the possible values. + +```jsx +import { SelectArrayInput } from 'react-admin'; + +const UserCreate = () => ( + + + + + +); +``` + +By default, the possible choices are built from the `choices` prop, using: + - the `id` field as the option value, + - the `name` field as the option text + +The form value for the source must be an array of the selected values, e.g. + +```js +{ + id: 123, + name: 'John Doe', + roles: ['u001', 'u003'], +} +``` + +## Props | Prop | Required | Type | Default | Description | |-------------------|----------|----------------------------|--------------------|----------------------------------------------------------------------------------------------------------------------------------------| -| `choices` | Required | `Object[]` | - | List of items to show as options | -| `create` | Optional | `Element` | `-` | A React Element to render when users want to create a new choice | -| `createLabel` | Optional | `string` | `ra.action.create` | The label for the menu item allowing users to create a new choice. Used when the filter is empty | -| `emptyText` | Optional | `string` | '' | The text to display for the empty option | -| `onCreate` | Optional | `Function` | `-` | A function called with the current filter value when users choose to create a new choice. | +| `choices` | Optional | `Object[]` | - | List of items to show as options. Required unless inside a ReferenceArray Input. | +| `create` | Optional | `Element` | - | A React Element to render when users want to create a new choice | +| `createLabel` | Optional | `string` | `ra.action. create` | The label for the menu item allowing users to create a new choice. Used when the filter is empty | +| `disableValue` | Optional | `string` | 'disabled' | The custom field name used in `choices` to disable some choices | +| `onCreate` | Optional | `Function` | - | A function called with the current filter value when users choose to create a new choice. | | `options` | Optional | `Object` | - | Props to pass to the underlying `` element | | `optionText` | Optional | `string` | `Function` | `name` | Field name of record to display in the suggestion item or function which accepts the current record as argument (`record => {string}`) | | `optionValue` | Optional | `string` | `id` | Field name of record containing the value to use as input value | @@ -25,88 +68,324 @@ To let users choose several values in a list using a dropdown, use `` also accepts the [common input props](./Inputs.md#common-input-props). -## Usage +## `choices` -Set the `choices` attribute to determine the options (with `id`, `name` tuples): +The list of choices must be an array of objects - one object for each possible choice. In each object, `id` is the value, and the `name` is the label displayed to the user. ```jsx -import { SelectArrayInput } from 'react-admin'; - - ``` -You can also customize the properties to use for the option name and value, -thanks to the `optionText` and `optionValue` attributes. +You can render some options as disabled by setting the `disabled` field in some choices: + +```jsx + +``` + +You can also use an array of objects with different properties for the label and value, given you specify the [`optionText`](#optiontext) and [`optionValue`](#optionvalue) props: + +```jsx + +``` + +The choices are translated by default, so you can use translation identifiers as choices: ```jsx const choices = [ - { _id: '1', name: 'Book', plural_name: 'Books' }, - { _id: '2', name: 'Video', plural_name: 'Videos' }, - { _id: '3', name: 'Audio', plural_name: 'Audios' }, + { id: 'admin', name: 'myroot.roles.admin' }, + { id: 'u001', name: 'myroot.roles.u001' }, + { id: 'u002', name: 'myroot.roles.u002' }, + { id: 'u003', name: 'myroot.roles.u003' }, ]; - ``` -`optionText` also accepts a function, so you can shape the option text at will: +You can opt-out of this translation by setting [the `translateChoice` prop](#translatechoice) to `false`. + +If you need to *fetch* the options from another resource, you're actually editing a one-to-many or a many-to-many relationship. In this case, wrap the `` in a [``](./ReferenceArrayInput.md) or a [``](./ReferenceManyToManyInput.md) component. You don't need to specify the `choices` prop - the parent component injects it based on the possible values of the related resource. ```jsx + + + +``` + +If you have an *array of values* for the options, turn it into an array of objects with the `id` and `name` properties: + +```jsx +const possibleValues = ['programming', 'lifestyle', 'photography']; +const ucfirst = name => name.charAt(0).toUpperCase() + name.slice(1); +const choices = possibleValues.map(value => ({ id: value, name: ucfirst(value) })); + + +``` + +## `create` + +To allow users to add new options, pass a React element as the `create` prop. `` will then render a "Create" option at the bottom of the choices list. When clicked, it will render the create element. + +![create option](./img/select-array-input-create.gif) + +In the create component, use the `useCreateSuggestionContext` hook to add a new choice to the list of options. + +{% raw %} +```jsx +import { CreateRole } from './CreateRole'; + const choices = [ - { id: '1', name: 'Book', quantity: 23 }, - { id: '2', name: 'Video', quantity: 56 }, - { id: '3', name: 'Audio', quantity: 12 }, + { id: 'admin', name: 'Admin' }, + { id: 'u001', name: 'Editor' }, + { id: 'u002', name: 'Moderator' }, + { id: 'u003', name: 'Reviewer' }, ]; -const optionRenderer = choice => `${choice.name} (${choice.quantity})`; - + +const UserCreate = () => ( + + + } + /> + + +); + +// in ./CreateRole.js +import { useCreateSuggestionContext } from 'react-admin'; +import { + Button, + Dialog, + DialogActions, + DialogContent, + TextField, +} from '@mui/material'; + +const CreateRole = () => { + const { onCancel, onCreate } = useCreateSuggestionContext(); + const [value, setValue] = React.useState(''); + + const handleSubmit = event => { + event.preventDefault(); + const newOption = { id: value, name: value }; + choices.push(newOption); + setValue(''); + onCreate(newOption); + }; + + return ( + +
+ + setValue(event.target.value)} + autoFocus + /> + + + + + +
+
+ ); +}; ``` +{% endraw %} -The choices are translated by default, so you can use translation identifiers as choices: +If you just need to ask users for a single string to create the new option, you can use [the `onCreate` prop](#oncreate) instead. + +If you're in a `` or ``, the `handleSubmit` will need to create a new record in the related resource. Check the [Creating New Choices](#creating-new-choices) for an example. + +## `disableValue` +By default, `` renders the choices with the field `disabled: true` as disabled. + +```jsx +const choices = [ + { _id: 'admin', label: 'Admin', disabled: true }, + { _id: 'u001', label: 'Editor' }, + { _id: 'u002', label: 'Moderator' }, + { _id: 'u003', label: 'Reviewer' }, +]; + +``` + +If you want to use another field to denote disabled options, set the `disableValue` prop. + +```jsx +const choices = [ + { _id: 'admin', label: 'Admin', not_available: true }, + { _id: 'u001', label: 'Editor' }, + { _id: 'u002', label: 'Moderator' }, + { _id: 'u003', label: 'Reviewer' }, +]; + +``` + +## `onCreate` + +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. + +{% raw %} ```js +import { SelectArrayInput, Create, SimpleForm, TextInput } from 'react-admin'; + +const UserCreate = () => { + const choices = [ + { id: 'admin', name: 'Admin' }, + { id: 'u001', name: 'Editor' }, + { id: 'u002', name: 'Moderator' }, + { id: 'u003', name: 'Reviewer' }, + ]; + return ( + + + { + const newRoleName = prompt('Role name'); + const newRole = { id: newRoleName.toLowerCase(), name: newRoleName }; + choices.push(newRole); + return newRole; + }} + /> + + + ); +} +``` +{% endraw %} + +If a prompt is not enough, you can use [the `create` prop](#create) to render a custom component instead. + +## `options` + +Use the `options` attribute if you want to override any of MUI's `` attributes: +## `sx`: CSS API + +The `` component accepts the usual `className` prop. You can also override many styles of the inner components thanks to the `sx` property (as most MUI components, see their [documentation about it](https://mui.com/customization/how-to-customize/#overriding-nested-component-styles)). This property accepts the following subclasses: + +| Rule name | Description | +|-------------------------------|------------------------------------------------------------------------------------| +| `& .RaSelectArrayInput-chip` | Applied to each MUI's `Chip` component used as selected item | +| `& .RaSelectArrayInput-chips` | Applied to the container of MUI's `Chip` components used as selected items | + +To override the style of all instances of `` using the [MUI style overrides](https://mui.com/customization/globals/#css), use the `RaSelectArrayInput` key. + +## `translateChoice` + +The choices are translated by default, so you can use translation identifiers as choices: -{% raw %} ```jsx - +const choices = [ + { id: 'admin', name: 'myroot.roles.admin' }, + { id: 'u001', name: 'myroot.roles.u001' }, + { id: 'u002', name: 'myroot.roles.u002' }, + { id: 'u003', name: 'myroot.roles.u003' }, +]; ``` -{% endraw %} -Refer to [the Select documentation](https://mui.com/api/select) for more details. +However, in some cases (e.g. inside a ``), you may not want the choice to be translated. +In that case, set the `translateChoice` prop to `false`. + +```jsx + +``` + +## Using in a ReferenceArrayInput -The `SelectArrayInput` component **cannot** be used inside a `ReferenceInput` but can be used inside a `ReferenceArrayInput`. +If you want to populate the `choices` attribute with a list of related records, you should decorate `` with [``](./ReferenceArrayInput.md), and leave the `choices` empty: ```jsx import * as React from "react"; @@ -125,20 +404,15 @@ export const PostCreate = () => ( - - - - +
); ``` -**Tip**: As it does not provide autocompletion, the `SelectArrayInput` might not be suited when the referenced resource has a lot of items. - -`` also accepts the [common input props](./Inputs.md#common-input-props). +**Tip**: As it does not provide autocompletion, `` might not be suited when the reference resource has a lot of items. ## Creating New Choices @@ -257,14 +531,3 @@ const CreateTag = () => { }; ``` {% endraw %} - -## `sx`: CSS API - -The `` component accepts the usual `className` prop. You can also override many styles of the inner components thanks to the `sx` property (as most MUI components, see their [documentation about it](https://mui.com/customization/how-to-customize/#overriding-nested-component-styles)). This property accepts the following subclasses: - -| Rule name | Description | -|-------------------------------|------------------------------------------------------------------------------------| -| `& .RaSelectArrayInput-chip` | Applied to each MUI's `Chip` component used as selected item | -| `& .RaSelectArrayInput-chips` | Applied to the container of MUI's `Chip` components used as selected items | - -To override the style of all instances of `` using the [MUI style overrides](https://mui.com/customization/globals/#css), use the `RaSelectArrayInput` key. diff --git a/docs/SelectInput.md b/docs/SelectInput.md index e766fcdeee1..4c994dbfb5e 100644 --- a/docs/SelectInput.md +++ b/docs/SelectInput.md @@ -311,7 +311,7 @@ If a prompt is not enough, you can use [the `create` prop](#create) to render a ## `options` -Use the `options` attribute if you want to override any of MUI's `` attributes: +Use the `options` attribute if you want to override any of MUI's `