diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx index e66e6f0a924..f8f746b43b1 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx @@ -17,6 +17,7 @@ import { DifferentShapeInGetMany, InsideReferenceInput, InsideReferenceInputDefaultValue, + InsideReferenceInputWithCustomizedItemRendering, Nullable, NullishValuesSupport, VeryLargeOptionsNumber, @@ -1393,6 +1394,39 @@ describe('', () => { expect(testFailed).toBe(false); expect(input.value).toBe('Leo Tolstoy test'); }); + + it('should not use getSuggestions to do client-side filtering', async () => { + // filtering should be done server-side only, and hence matchSuggestion should never be called + const matchSuggestion = jest.fn().mockReturnValue(true); + render( + + ); + await waitFor( + () => { + expect( + (screen.getByRole('textbox') as HTMLInputElement).value + ).toBe('Leo Tolstoy - Russian'); + }, + { timeout: 2000 } + ); + screen.getByRole('textbox').focus(); + fireEvent.click(screen.getByLabelText('Clear value')); + await waitFor(() => { + expect(screen.getByRole('listbox').children).toHaveLength(5); + }); + fireEvent.change(screen.getByRole('textbox'), { + target: { value: 'French' }, + }); + await waitFor( + () => { + screen.getByText('No options'); + }, + { timeout: 2000 } + ); + expect(matchSuggestion).not.toHaveBeenCalled(); + }); }); it("should allow to edit the input if it's inside a FormDataConsumer", () => { diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx index e9597c06e5c..14e59c28507 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx @@ -24,7 +24,7 @@ import fakeRestProvider from 'ra-data-fakerest'; import { Edit } from '../detail'; import { SimpleForm } from '../form'; -import { AutocompleteInput } from './AutocompleteInput'; +import { AutocompleteInput, AutocompleteInputProps } from './AutocompleteInput'; import { ReferenceInput } from './ReferenceInput'; import { TextInput } from './TextInput'; import { useCreateSuggestionContext } from './useSupportCreateSuggestion'; @@ -689,6 +689,46 @@ export const InsideReferenceInputWithCreationSupport = () => ( ); +const BookOptionText = () => { + const book = useRecordContext(); + if (!book) return null; + return
{`${book.name} - ${book.language}`}
; +}; + +export const InsideReferenceInputWithCustomizedItemRendering = ( + props: Partial +) => ( + + + ( + { + console.log(data); + }, + }} + > + + + } + inputText={book => + `${book.name} - ${book.language}` + } + {...props} + /> + + + + )} + /> + +); + const OptionItem = props => { const record = useRecordContext(); return ( diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index fd891c49ebb..cc919e1e872 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx @@ -276,8 +276,12 @@ export const AutocompleteInput = < throw new Error(` If you provided a React element for the optionText prop, you must also provide the inputText prop (used for the text input)`); } - // eslint-disable-next-line eqeqeq - if (isValidElement(optionText) && matchSuggestion == undefined) { + if ( + isValidElement(optionText) && + !isFromReference && + // eslint-disable-next-line eqeqeq + matchSuggestion == undefined + ) { throw new Error(` If you provided a React element for the optionText prop, you must also provide the matchSuggestion prop (used to match the user input with a choice)`); } @@ -515,7 +519,7 @@ If you provided a React element for the optionText prop, you must also provide t const oneSecondHasPassed = useTimeout(1000, filterValue); const suggestions = useMemo(() => { - if (matchSuggestion || limitChoicesToValue) { + if (!isFromReference && (matchSuggestion || limitChoicesToValue)) { return getSuggestions(filterValue); } return finalChoices?.slice(0, suggestionLimit) || []; @@ -526,6 +530,7 @@ If you provided a React element for the optionText prop, you must also provide t limitChoicesToValue, matchSuggestion, suggestionLimit, + isFromReference, ]); const isOptionEqualToValue = (option, value) => {