diff --git a/packages/ra-ui-materialui/src/input/AutocompleteArrayInput.spec.tsx b/packages/ra-ui-materialui/src/input/AutocompleteArrayInput.spec.tsx index 8b8ca8f7d35..b72df04b803 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteArrayInput.spec.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteArrayInput.spec.tsx @@ -828,7 +828,7 @@ describe('', () => { resource="posts" choices={choices} onCreate={handleCreate} - optionText={choice => `Choice is ${choice.name}`} + optionText={choice => `Choice is not displayed`} /> @@ -839,7 +839,7 @@ describe('', () => { }) as HTMLInputElement; input.focus(); fireEvent.change(input, { target: { value: 'New Kid On The Block' } }); - fireEvent.click(screen.getByText('Choice is ra.action.create_item')); + fireEvent.click(screen.getByText('ra.action.create_item')); await new Promise(resolve => setTimeout(resolve)); rerender( diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx index f3830b93df9..829f7a64701 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx @@ -169,6 +169,41 @@ describe('', () => { }); }); + it('should not use optionText defined with a function value on the "create new item" option', async () => { + const choices = [ + { id: 'ang', fullname: 'Angular' }, + { id: 'rea', fullname: 'React' }, + ]; + const optionText = jest.fn(choice => choice.fullname); + + const handleCreate = filter => ({ + id: 'newid', + fullname: filter, + }); + + render( + + + + + + ); + + const input = screen.getByLabelText( + 'resources.posts.fields.language' + ) as HTMLInputElement; + input.focus(); + fireEvent.change(input, { target: { value: 'Vue' } }); + await new Promise(resolve => setTimeout(resolve)); + expect(screen.getByText('ra.action.create_item')).not.toBeNull(); + }); + it('should translate the value by default', async () => { render( @@ -396,6 +431,35 @@ describe('', () => { ).not.toBeNull(); }); + it('should allow customized rendering of option items', () => { + const OptionItem = props => { + const record = useRecordContext(); + return
; + }; + + render( + + + } + matchSuggestion={() => true} + inputText={record => record?.name} + choices={[ + { id: 1, name: 'bar' }, + { id: 2, name: 'foo' }, + ]} + /> + + + ); + + const input = screen.getByLabelText('resources.users.fields.role'); + fireEvent.focus(input); + + expect(screen.queryByLabelText('bar')).not.toBeNull(); + }); + it('should reset filter when input value changed', async () => { const setFilter = jest.fn(); const { rerender } = render( diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx index 69e750b3f56..4437474fecf 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx @@ -1,5 +1,5 @@ import * as React from 'react'; -import { Admin } from 'react-admin'; +import { Admin, AdminContext } from 'react-admin'; import { Resource, required, useCreate, useRecordContext } from 'ra-core'; import { createMemoryHistory } from 'history'; import { @@ -420,6 +420,36 @@ export const InsideReferenceInputWithCreationSupport = () => ( ); +const OptionItem = props => { + const record = useRecordContext(); + return ( +
+ {`from optionText: ${record && record.name}`} +
+ ); +}; + +export const CustomizedItemRendering = () => { + return ( + + {}} defaultValues={{ role: 2 }}> + } + inputText={record => `from inputText ${record?.name}`} + matchSuggestion={() => true} + choices={[ + { id: 1, name: 'bar' }, + { id: 2, name: 'foo' }, + ]} + /> + + + ); +}; + const DalmatianEdit = () => { const dalmatians: any[] = []; for (let index = 0; index < 1100; index++) { diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index 170a27f2891..dadfc3710e2 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx @@ -302,6 +302,7 @@ If you provided a React element for the optionText prop, you must also provide t getCreateItem, handleChange: handleChangeWithCreateSupport, createElement, + createId, } = useSupportCreateSuggestion({ create, createLabel, @@ -314,24 +315,28 @@ If you provided a React element for the optionText prop, you must also provide t }); const getOptionLabel = useCallback( - (option: any) => { + (option: any, isListItem: boolean = false) => { // eslint-disable-next-line eqeqeq if (option == undefined) { return ''; } + // Value selected with enter, right from the input if (typeof option === 'string') { return option; } - // eslint-disable-next-line eqeqeq - if (inputText != undefined) { + if (option?.id === createId) { + return option?.name; + } + + if (!isListItem && inputText !== undefined) { return inputText(option); } return getChoiceText(option); }, - [getChoiceText, inputText] + [getChoiceText, inputText, createId] ); useEffect(() => { @@ -527,7 +532,7 @@ If you provided a React element for the optionText prop, you must also provide t onBlur={field.onBlur} onInputChange={handleInputChange} renderOption={(props, record) => ( -
  • {getChoiceText(record)}
  • +
  • {getOptionLabel(record, true)}
  • )} /> {createElement} diff --git a/packages/ra-ui-materialui/src/input/useSupportCreateSuggestion.tsx b/packages/ra-ui-materialui/src/input/useSupportCreateSuggestion.tsx index b066873c4dd..00c39aa13fe 100644 --- a/packages/ra-ui-materialui/src/input/useSupportCreateSuggestion.tsx +++ b/packages/ra-ui-materialui/src/input/useSupportCreateSuggestion.tsx @@ -64,6 +64,7 @@ export const useSupportCreateSuggestion = ( ); return { + createId: createValue, getCreateItem: () => { if (typeof optionText !== 'string') { return { @@ -129,6 +130,7 @@ export interface SupportCreateSuggestionOptions { } export interface UseSupportCreateValue { + createId: string; getCreateItem: ( filterValue?: string ) => { id: Identifier; [key: string]: any };