Skip to content

Commit

Permalink
fix autocomplete sets previous value using onInputChange
Browse files Browse the repository at this point in the history
  • Loading branch information
tdnl committed Dec 22, 2023
1 parent ab2e448 commit bc7c571
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 32 deletions.
2 changes: 1 addition & 1 deletion docs/AutocompleteInput.md
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,7 @@ const CompanyInput = () => {
choices={choicesWithCurrentCompany}
optionText="name"
disabled={isLoading}
onInputChange={e => setFilter({ q: e.target.value })}
onInputChange={(_, newInputValue) => setFilter({ q: newInputValue })}
/>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -995,7 +995,7 @@ describe('<AutocompleteInput />', () => {
</AdminContext>
);
const input = screen.getByLabelText('resources.users.fields.role');
fireEvent.change(input, { target: { value: 'a' } });
userEvent.type(input, 'a');
await waitFor(() => {
expect(screen.queryAllByRole('option').length).toEqual(2);
});
Expand All @@ -1022,7 +1022,7 @@ describe('<AutocompleteInput />', () => {
</AdminContext>
);
const input = screen.getByLabelText('resources.users.fields.role');
fireEvent.change(input, { target: { value: 'ab' } });
userEvent.type(input, 'ab');
await waitFor(() =>
expect(screen.queryAllByRole('option').length).toEqual(2)
);
Expand Down Expand Up @@ -1376,8 +1376,7 @@ describe('<AutocompleteInput />', () => {
'Author'
)) as HTMLInputElement;
expect(input.value).toBe('Leo Tolstoy');
fireEvent.mouseDown(input);
fireEvent.change(input, { target: { value: 'Leo Tolstoy test' } });
userEvent.type(input, 'Leo Tolstoy test');
// Make sure that 'Leo Tolstoy' did not reappear
let testFailed = false;
try {
Expand Down
25 changes: 25 additions & 0 deletions packages/ra-ui-materialui/src/input/AutocompleteInput.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1240,3 +1240,28 @@ export const WithInputProps = () => {
</Admin>
);
};

export const WithInputOnChange = () => {
const [searchText, setSearchText] = React.useState('');

return (
<AdminContext dataProvider={dataProviderWithAuthors}>
<div>Search text: {searchText}</div>
<SimpleForm onSubmit={() => {}} defaultValues={{ role: 2 }}>
<AutocompleteInput
fullWidth
source="role"
resource="users"
choices={[
{ id: 1, name: 'ab' },
{ id: 2, name: 'abc' },
{ id: 3, name: '123' },
]}
onInputChange={(_, value) => {
setSearchText(value);
}}
/>
</SimpleForm>
</AdminContext>
);
};
68 changes: 41 additions & 27 deletions packages/ra-ui-materialui/src/input/AutocompleteInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,13 @@ const defaultFilterOptions = createFilterOptions();
*
* @example
* <AutocompleteInput source="author_id" options={{ color: 'secondary', InputLabelProps: { shrink: true } }} />
*
* Retrieve the value displayed in the textbox using the `onInputChange` prop:
*
* @example
* const [state, setState] = useState('')
*
* <AutocompleteInput source="gender" choices={choices} onInputChange={(_, newInputValue) => setState(newInputValue)} />
*/
export const AutocompleteInput = <
OptionType extends RaRecord = RaRecord,
Expand Down Expand Up @@ -440,33 +447,14 @@ If you provided a React element for the optionText prop, you must also provide t
useEffect(() => {
if (!multiple) {
const optionLabel = getOptionLabel(selectedChoice);
if (typeof optionLabel === 'string') {
setFilterValue(optionLabel);
} else {
if (typeof optionLabel !== 'string') {
throw new Error(
'When optionText returns a React element, you must also provide the inputText prop'
);
}
}
}, [getOptionLabel, multiple, selectedChoice]);

const handleInputChange: AutocompleteProps<
OptionType,
Multiple,
DisableClearable,
SupportCreate
>['onInputChange'] = (event, newInputValue, reason) => {
if (
event?.type === 'change' ||
!doesQueryMatchSelection(newInputValue)
) {
setFilterValue(newInputValue);
debouncedSetFilter(newInputValue);
}

onInputChange?.(event, newInputValue, reason);
};

const doesQueryMatchSelection = useCallback(
(filter: string) => {
let selectedItemTexts;
Expand Down Expand Up @@ -515,13 +503,39 @@ If you provided a React element for the optionText prop, you must also provide t
return filteredOptions;
};

const handleAutocompleteChange = (
event: any,
newValue: any,
_reason: string
) => {
handleChangeWithCreateSupport(newValue != null ? newValue : emptyValue);
};
const handleAutocompleteChange = useCallback<
AutocompleteProps<
OptionType,
Multiple,
DisableClearable,
SupportCreate
>['onChange']
>(
(_event, newValue, _reason) => {
handleChangeWithCreateSupport(
newValue != null ? newValue : emptyValue
);
},
[emptyValue, handleChangeWithCreateSupport]
);

const handleInputChange = useCallback<
AutocompleteProps<
OptionType,
Multiple,
DisableClearable,
SupportCreate
>['onInputChange']
>(
(event, newInputValue, reason) => {
setFilterValue(newInputValue);
if (!doesQueryMatchSelection(newInputValue)) {
debouncedSetFilter(newInputValue);
}
onInputChange?.(event, newInputValue, reason);
},
[debouncedSetFilter, doesQueryMatchSelection, onInputChange]
);

const oneSecondHasPassed = useTimeout(1000, filterValue);

Expand Down

0 comments on commit bc7c571

Please sign in to comment.