Skip to content

Commit

Permalink
Merge pull request #8238 from marmelab/fix-autocompleteinput-update-v…
Browse files Browse the repository at this point in the history
…alue

Fix AutocompleteInput input's value not updating correctly
  • Loading branch information
slax57 authored Oct 12, 2022
2 parents 9dd4dff + f551aa5 commit 2e82307
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 24 deletions.
46 changes: 41 additions & 5 deletions packages/ra-ui-materialui/src/input/AutocompleteInput.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,43 @@ describe('<AutocompleteInput />', () => {
expect(screen.queryByDisplayValue('foo')).not.toBeNull();
});

it('should allow filter to match the selected choice while removing characters in the input', async () => {
render(
<AdminContext dataProvider={testDataProvider()}>
<SimpleForm>
<AutocompleteInput
{...defaultProps}
choices={[
{ id: 1, name: 'foo' },
{ id: 2, name: 'bar' },
]}
/>
</SimpleForm>
</AdminContext>
);

const input = screen.getByLabelText(
'resources.users.fields.role'
) as HTMLInputElement;

fireEvent.mouseDown(input);
await waitFor(() => {
expect(screen.getByText('foo')).not.toBe(null);
});
fireEvent.click(screen.getByText('foo'));
await waitFor(() => {
expect(input.value).toEqual('foo');
});
fireEvent.focus(input);
userEvent.type(input, '{end}');
userEvent.type(input, '2');
expect(input.value).toEqual('foo2');
userEvent.type(input, '{backspace}');
await waitFor(() => {
expect(input.value).toEqual('foo');
});
});

describe('emptyText', () => {
it('should allow to have an empty menu option text by passing a string', () => {
const emptyText = 'Default';
Expand Down Expand Up @@ -449,16 +486,15 @@ describe('<AutocompleteInput />', () => {
});
});

it('should allow to clear the first character', async () => {
it('should not match selection when selected choice id equals the emptyValue while changing the input', async () => {
render(
<AdminContext dataProvider={testDataProvider()}>
<SimpleForm onSubmit={jest.fn()} defaultValues={{ role: 2 }}>
<SimpleForm>
<AutocompleteInput
{...defaultProps}
optionText="foobar"
choices={[
{ id: 2, foobar: 'foo' },
{ id: 3, foobar: 'bar' },
{ id: 2, name: 'foo' },
{ id: 3, name: 'bar' },
]}
/>
</SimpleForm>
Expand Down
51 changes: 32 additions & 19 deletions packages/ra-ui-materialui/src/input/AutocompleteInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -195,19 +195,31 @@ export const AutocompleteInput = <

const finalEmptyText = emptyText ?? '';

const finalChoices =
isRequiredOverride || multiple
? allChoices
: [
{
[optionValue || 'id']: emptyValue,
[typeof optionText === 'string'
? optionText
: 'name']: translate(finalEmptyText, {
_: finalEmptyText,
}),
},
].concat(allChoices);
const finalChoices = useMemo(
() =>
isRequiredOverride || multiple
? allChoices
: [
{
[optionValue || 'id']: emptyValue,
[typeof optionText === 'string'
? optionText
: 'name']: translate(finalEmptyText, {
_: finalEmptyText,
}),
},
].concat(allChoices),
[
allChoices,
emptyValue,
finalEmptyText,
isRequiredOverride,
multiple,
optionText,
optionValue,
translate,
]
);

const {
id,
Expand Down Expand Up @@ -422,15 +434,18 @@ If you provided a React element for the optionText prop, you must also provide t
newInputValue: string,
reason: string
) => {
if (!doesQueryMatchSelection(newInputValue, event?.type)) {
if (
event?.type === 'change' ||
!doesQueryMatchSelection(newInputValue)
) {
setFilterValue(newInputValue);
debouncedSetFilter(newInputValue);
}
};

const doesQueryMatchSelection = useCallback(
(filter: string, eventType?: string) => {
let selectedItemTexts = [];
(filter: string) => {
let selectedItemTexts;

if (multiple) {
selectedItemTexts = selectedChoice.map(item =>
Expand All @@ -440,9 +455,7 @@ If you provided a React element for the optionText prop, you must also provide t
selectedItemTexts = [getOptionLabel(selectedChoice)];
}

return eventType && eventType === 'change'
? selectedItemTexts.includes(filter) && selectedChoice
: selectedItemTexts.includes(filter);
return selectedItemTexts.includes(filter);
},
[getOptionLabel, multiple, selectedChoice]
);
Expand Down

0 comments on commit 2e82307

Please sign in to comment.