diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx
index 43255b37d6..e1c23c994f 100644
--- a/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx
+++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx
@@ -26,6 +26,7 @@ import {
InsideReferenceInputOnChange,
WithInputProps,
OnCreate,
+ OnCreateSlow,
} from './AutocompleteInput.stories';
import { ReferenceArrayInput } from './ReferenceArrayInput';
import { AutocompleteArrayInput } from './AutocompleteArrayInput';
@@ -515,7 +516,7 @@ describe('', () => {
];
const OptionText = () => {
const record = useRecordContext();
- return option:{record.name};
+ return option:{record?.name};
};
render(
@@ -1054,7 +1055,7 @@ describe('', () => {
});
describe('onCreate', () => {
- it('should include an option with the createLabel when the input is empty', async () => {
+ it("shouldn't include an option with the createLabel when the input is empty", async () => {
const choices = [
{ id: 'ang', name: 'Angular' },
{ id: 'rea', name: 'React' },
@@ -1093,7 +1094,53 @@ describe('', () => {
target: { value: '' },
});
- expect(screen.queryByText('ra.action.create')).not.toBeNull();
+ expect(screen.queryByText('ra.action.create')).toBeNull();
+ expect(screen.queryByText('ra.action.create_item')).toBeNull();
+ });
+ it('should include an option with the custom createLabel when the input is empty', async () => {
+ const choices = [
+ { id: 'ang', name: 'Angular' },
+ { id: 'rea', name: 'React' },
+ ];
+ const handleCreate = filter => {
+ const newChoice = {
+ id: 'js_fatigue',
+ name: filter,
+ };
+ choices.push(newChoice);
+ return newChoice;
+ };
+
+ render(
+
+
+
+
+
+ );
+
+ const input = screen.getByLabelText(
+ 'resources.posts.fields.language'
+ ) as HTMLInputElement;
+ input.focus();
+ fireEvent.change(input, {
+ target: { value: '' },
+ });
+
+ expect(
+ screen.queryByText('Start typing to create a new item')
+ ).not.toBeNull();
+ expect(screen.queryByText('ra.action.create')).toBeNull();
expect(screen.queryByText('ra.action.create_item')).toBeNull();
});
it('should include an option with the createItemLabel when the input not empty', async () => {
@@ -1245,7 +1292,6 @@ describe('', () => {
fireEvent.focus(input);
expect(screen.queryByText('New Kid On The Block')).not.toBeNull();
});
-
it('should allow the creation of a new choice with a promise', async () => {
const choices = [
{ id: 'ang', name: 'Angular' },
@@ -1314,6 +1360,31 @@ describe('', () => {
fireEvent.focus(input);
expect(screen.queryByText('New Kid On The Block')).not.toBeNull();
});
+ it('should not use the createItemLabel as the value of the input', async () => {
+ render();
+ await screen.findByText('Book War and Peace', undefined, {
+ timeout: 2000,
+ });
+ const input = screen.getByLabelText('Author') as HTMLInputElement;
+ await waitFor(
+ () => {
+ expect(input.value).toBe('Leo Tolstoy');
+ },
+ { timeout: 2000 }
+ );
+ fireEvent.focus(input);
+ expect(screen.getAllByRole('option')).toHaveLength(4);
+ fireEvent.change(input, { target: { value: 'x' } });
+ await waitFor(
+ () => {
+ expect(screen.getAllByRole('option')).toHaveLength(1);
+ },
+ { timeout: 2000 }
+ );
+ fireEvent.click(screen.getByText('Create x'));
+ expect(input.value).not.toBe('Create x');
+ expect(input.value).toBe('x');
+ }, 10000);
});
describe('create', () => {
it('should allow the creation of a new choice', async () => {
diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx
index 4d0a33bf43..f0cab76721 100644
--- a/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx
+++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx
@@ -357,33 +357,42 @@ export const CreateLabel = () => (
{
- const newAuthorName = window.prompt(
- 'Enter a new author',
- filter
- );
+ if (!filter) return;
- if (newAuthorName) {
- const newAuthor = {
- id: choicesForCreationSupport.length + 1,
- name: newAuthorName,
- };
- choicesForCreationSupport.push(newAuthor);
- return newAuthor;
- }
+ const newOption = {
+ id: choicesForCreationSupport.length + 1,
+ name: filter,
+ };
+ choicesForCreationSupport.push(newOption);
+ return newOption;
}}
createLabel="Start typing to create a new item"
/>
);
+export const CreateItemLabel = () => (
+
+ {
+ if (!filter) return;
+
+ const newOption = {
+ id: choicesForCreationSupport.length + 1,
+ name: filter,
+ };
+ choicesForCreationSupport.push(newOption);
+ return newOption;
+ }}
+ createItemLabel="Add a new author: %{item}"
+ />
+
+);
+
const authorsWithFirstAndLastName = [
{ id: 1, first_name: 'Leo', last_name: 'Tolstoy', language: 'Russian' },
{ id: 2, first_name: 'Victor', last_name: 'Hugo', language: 'French' },
diff --git a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx
index 008a4d52da..576aaa7d49 100644
--- a/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx
+++ b/packages/ra-ui-materialui/src/input/AutocompleteInput.tsx
@@ -133,7 +133,7 @@ export const AutocompleteInput = <
closeText = 'ra.action.close',
create,
createLabel,
- createItemLabel,
+ createItemLabel = 'ra.action.create_item',
createValue,
debounce: debounceDelay = 250,
defaultValue,
@@ -465,7 +465,13 @@ If you provided a React element for the optionText prop, you must also provide t
event?.type === 'change' ||
!doesQueryMatchSelection(newInputValue)
) {
- setFilterValue(newInputValue);
+ const createOptionLabel = translate(createItemLabel, {
+ item: filterValue,
+ _: createItemLabel,
+ });
+ const isCreate = newInputValue === createOptionLabel;
+ const valueToSet = isCreate ? filterValue : newInputValue;
+ setFilterValue(valueToSet);
debouncedSetFilter(newInputValue);
}
if (reason === 'clear') {
@@ -513,7 +519,7 @@ If you provided a React element for the optionText prop, you must also provide t
// add create option if necessary
const { inputValue } = params;
if (onCreate || create) {
- if (inputValue === '') {
+ if (inputValue === '' && createLabel) {
// create option with createLabel
filteredOptions = filteredOptions.concat(getCreateItem(''));
} else if (!doesQueryMatchSuggestion(filterValue)) {