From 9a9ce199853e4496235b13b86f125bce845f71a9 Mon Sep 17 00:00:00 2001 From: Antoine Fricker Date: Wed, 29 Jun 2022 01:52:57 +0200 Subject: [PATCH 1/4] Fix 7902 --- .../src/input/AutocompleteInput.spec.tsx | 35 +++++++++++++++++++ .../src/input/AutocompleteInput.tsx | 7 +++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx index f3830b93df9..6054c142e81 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( diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index 170a27f2891..7dd80655bd9 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx @@ -319,11 +319,16 @@ If you provided a React element for the optionText prop, you must also provide t if (option == undefined) { return ''; } + // Value selected with enter, right from the input if (typeof option === 'string') { return option; } + if (option?.id === '@@ra-create') { + return option?.name; + } + // eslint-disable-next-line eqeqeq if (inputText != undefined) { return inputText(option); @@ -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)}
  • )} /> {createElement} From 51339de6038763d1716290b64f8ee17632d5239d Mon Sep 17 00:00:00 2001 From: Antoine Fricker Date: Mon, 4 Jul 2022 16:43:05 +0200 Subject: [PATCH 2/4] Handle custom createValues [skip ci] --- .../ra-ui-materialui/src/input/AutocompleteInput.tsx | 12 ++++++------ .../src/input/useSupportCreateSuggestion.tsx | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index 7dd80655bd9..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,7 +315,7 @@ 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 ''; @@ -325,18 +326,17 @@ If you provided a React element for the optionText prop, you must also provide t return option; } - if (option?.id === '@@ra-create') { + if (option?.id === createId) { return option?.name; } - // eslint-disable-next-line eqeqeq - if (inputText != undefined) { + if (!isListItem && inputText !== undefined) { return inputText(option); } return getChoiceText(option); }, - [getChoiceText, inputText] + [getChoiceText, inputText, createId] ); useEffect(() => { @@ -532,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) => ( -
  • {getOptionLabel(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 }; From b4febbe24e0df0a9ad73a32d9e7b4a5b8e773410 Mon Sep 17 00:00:00 2001 From: Antoine Fricker Date: Mon, 4 Jul 2022 16:43:24 +0200 Subject: [PATCH 3/4] Fix tests --- .../src/input/AutocompleteInput.spec.tsx | 29 +++++++++++++++++ .../src/input/AutocompleteInput.stories.tsx | 32 ++++++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx index 6054c142e81..829f7a64701 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx @@ -431,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++) { From 4dbaebee9b6878a310836989ed0dc917e49c2623 Mon Sep 17 00:00:00 2001 From: Antoine Fricker Date: Wed, 6 Jul 2022 15:14:13 +0200 Subject: [PATCH 4/4] Fix CI --- .../src/input/AutocompleteArrayInput.spec.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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(