diff --git a/cypress/integration/edit.js b/cypress/integration/edit.js index 496e27277e4..568b2d36cd8 100644 --- a/cypress/integration/edit.js +++ b/cypress/integration/edit.js @@ -130,9 +130,13 @@ describe('Edit Page', () => { ); }); - it('should allow to select an item from the AutocompleteInput without showing the choices again after', () => { + it.only('should allow to select an item from the AutocompleteInput without showing the choices again after', () => { EditCommentPage.navigate(); + cy.get( + '[value="Accusantium qui nihil voluptatum quia voluptas maxime ab similique - 1"]' + ); cy.get(EditCommentPage.elements.input('post_id')) + .type('{selectall}') .clear() .type('Sed quo'); cy.get('[role="tooltip"]').within(() => { diff --git a/examples/simple/src/comments/CommentEdit.tsx b/examples/simple/src/comments/CommentEdit.tsx index 079c0a36477..eb05c310fbd 100644 --- a/examples/simple/src/comments/CommentEdit.tsx +++ b/examples/simple/src/comments/CommentEdit.tsx @@ -152,7 +152,11 @@ const CommentEdit = props => { matchSuggestion={( filterValue, suggestion - ) => true} + ) => { + const title = `${suggestion.title} - ${suggestion.id}`; + + return title.includes(filterValue); + }} optionText={} inputText={inputText} options={{ diff --git a/packages/ra-core/src/form/useSuggestions.ts b/packages/ra-core/src/form/useSuggestions.ts index 8648ebf4faf..620d0837c35 100644 --- a/packages/ra-core/src/form/useSuggestions.ts +++ b/packages/ra-core/src/form/useSuggestions.ts @@ -211,6 +211,7 @@ export const getSuggestionsFactory = ({ } } else { suggestions = choices.filter(choice => matchSuggestion(filter, choice)); + if (!allowDuplicates) { suggestions = removeAlreadySelectedSuggestions( suggestions, diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx index f9343d9dc58..58c598011fd 100644 --- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx +++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx @@ -221,6 +221,35 @@ export const AutocompleteInput = (props: AutocompleteInputProps) => { [input.value, getSuggestionFromValue] ); + const doesQueryMatchSuggestion = useMemo(() => { + if (isValidElement(optionText)) { + return choices.some(choice => matchSuggestion(filterValue, choice)); + } + + const isFunction = typeof optionText === 'function'; + + if (isFunction) { + const hasOption = choices.some(choice => { + const text = optionText(choice) as string; + + return get(choice, text) === filterValue; + }); + + const selectedItemText = optionText(selectedItem); + + return hasOption || selectedItemText === filterValue; + } + + const selectedItemText = get(selectedItem, optionText); + const hasOption = choices.some(choice => { + return get(choice, optionText) === filterValue; + }); + + return selectedItemText === filterValue || hasOption; + }, [choices, optionText, filterValue, matchSuggestion, selectedItem]); + + const shouldAllowCreate = !doesQueryMatchSuggestion; + const { getChoiceText, getChoiceValue, getSuggestions } = useSuggestions({ allowEmpty, choices, @@ -294,6 +323,8 @@ export const AutocompleteInput = (props: AutocompleteInputProps) => { ? inputText(getChoiceText(selectedItem).props.record) : getChoiceText(selectedItem) ); + + inputEl.current.blur(); }, [ input.value, handleFilterChange, @@ -494,7 +525,9 @@ export const AutocompleteInput = (props: AutocompleteInputProps) => { }); const suggestions = [ ...getSuggestions(filterValue), - ...(onCreate || create ? [getCreateItem()] : []), + ...((onCreate || create) && shouldAllowCreate + ? [getCreateItem()] + : []), ]; return (