diff --git a/packages/mui-material/src/Autocomplete/Autocomplete.test.js b/packages/mui-material/src/Autocomplete/Autocomplete.test.js
index c64005ad1c64da..86b7795bdb7ab5 100644
--- a/packages/mui-material/src/Autocomplete/Autocomplete.test.js
+++ b/packages/mui-material/src/Autocomplete/Autocomplete.test.js
@@ -3374,6 +3374,73 @@ describe('', () => {
expect(handleChange.args[0][1]).to.equal('The');
});
+ it('should prevent form submission when committing typed text over auto-highlighted match', async () => {
+ const handleChange = spy();
+ const handleSubmit = spy();
+ const options = ['The Shawshank Redemption', 'The Godfather'];
+ const { user } = render(
+
{
+ if (!event.defaultPrevented && event.key === 'Enter') {
+ handleSubmit();
+ }
+ }}
+ >
+
}
+ />
+ ,
+ );
+
+ await user.type(screen.getByRole('combobox'), 'The{Enter}');
+
+ expect(handleChange.callCount).to.equal(1);
+ expect(handleChange.args[0][1]).to.equal('The');
+ expect(handleSubmit.callCount).to.equal(0);
+
+ await user.keyboard('{Enter}');
+
+ expect(handleSubmit.callCount).to.equal(1);
+ });
+
+ it('should prevent form submission when committing edited selected text over value-highlighted match', async () => {
+ const handleChange = spy();
+ const handleSubmit = spy();
+ const options = ['The Shawshank Redemption', 'The Godfather'];
+ const { user } = render(
+ {
+ if (!event.defaultPrevented && event.key === 'Enter') {
+ handleSubmit();
+ }
+ }}
+ >
+
}
+ />
+ ,
+ );
+
+ await user.keyboard('{Backspace}{Backspace}{Backspace}{Backspace}{Backspace}{Enter}');
+
+ expect(handleChange.callCount).to.equal(1);
+ expect(handleChange.args[0][1]).to.equal('The Godf');
+ expect(handleSubmit.callCount).to.equal(0);
+
+ await user.keyboard('{Enter}');
+
+ expect(handleSubmit.callCount).to.equal(1);
+ });
+
it('should prefer typed text after editing a selected value', async () => {
const handleChange = spy();
const options = ['The Shawshank Redemption', 'The Godfather'];
diff --git a/packages/mui-material/src/useAutocomplete/useAutocomplete.js b/packages/mui-material/src/useAutocomplete/useAutocomplete.js
index c2251a52b1fbc4..8b524bbc1192bc 100644
--- a/packages/mui-material/src/useAutocomplete/useAutocomplete.js
+++ b/packages/mui-material/src/useAutocomplete/useAutocomplete.js
@@ -1016,14 +1016,19 @@ function useAutocomplete(props) {
}
break;
case 'Enter': {
- // In freeSolo, only select the highlighted option if the user hasn't
- // typed new text (inputPristine) or explicitly interacted with an option
- // (keyboard, mouse, or touch — any non-null reason). This lets typed
- // text win over a programmatic highlight (reason=null, e.g. from
- // syncHighlightedIndex matching a previous value) while still honoring
- // deliberate user interactions like hovering a suggestion then pressing Enter.
+ // In freeSolo, once the user has typed new text, Enter should commit that text instead
+ // of treating a programmatic highlight as an intentional selection. Programmatic
+ // highlights include autoHighlight and syncHighlightedIndex matching a previous value.
+ const hasProgrammaticHighlight =
+ popupOpen &&
+ highlightedIndexRef.current !== -1 &&
+ highlightReasonRef.current === null;
+ const shouldCommitFreeSoloOverProgrammaticHighlight =
+ freeSolo && !inputPristine && hasProgrammaticHighlight;
const shouldSelectHighlighted =
- !freeSolo || inputPristine || highlightReasonRef.current !== null;
+ !freeSolo || inputPristine || !hasProgrammaticHighlight;
+ const shouldPreventSubmitAfterFreeSoloCommit =
+ shouldCommitFreeSoloOverProgrammaticHighlight && !touchScrolledRef.current;
if (
highlightedIndexRef.current !== -1 &&
@@ -1053,8 +1058,8 @@ function useAutocomplete(props) {
);
}
} else if (freeSolo && inputValue !== '' && inputValueIsSelectedValue === false) {
- if (multiple) {
- // Allow people to add new values before they submit the form.
+ if (multiple || shouldPreventSubmitAfterFreeSoloCommit) {
+ // Allow people to commit the freeSolo value before they submit the form.
event.preventDefault();
}
selectNewValue(event, inputValue, 'createOption', 'freeSolo');