From ba2ff4341f342d8dabcdb4b219f778925820fd3d Mon Sep 17 00:00:00 2001
From: Huangshuo Kuang <141250392+kkkhs@users.noreply.github.com>
Date: Mon, 17 Aug 2026 15:36:59 +0000
Subject: [PATCH] [material-ui][Select] Prevent opening mouseup auto-selection
---
.../mui-material/src/Select/Select.test.js | 30 +++++++++++++++++++
.../mui-material/src/Select/SelectInput.js | 14 +++++++++
2 files changed, 44 insertions(+)
diff --git a/packages/mui-material/src/Select/Select.test.js b/packages/mui-material/src/Select/Select.test.js
index f41a83a764943d..c8cc5a36e59f8a 100644
--- a/packages/mui-material/src/Select/Select.test.js
+++ b/packages/mui-material/src/Select/Select.test.js
@@ -260,6 +260,7 @@ describe('', () => {
await sleep(450);
});
+ await user.pointer({ target: screen.getByRole('option', { name: 'Ten' }) });
await user.pointer({
keys: '[/MouseLeft]',
target: screen.getByRole('option', { name: 'Ten' }),
@@ -270,6 +271,33 @@ describe('', () => {
expect(screen.queryByRole('listbox', { hidden: false })).to.equal(null);
});
+ it('does not select an option when the opening mouseup lands on it after the drag delay without moving', async () => {
+ const onChange = spy();
+ const { user } = render(
+ ,
+ );
+
+ const trigger = screen.getByRole('combobox');
+ await user.pointer({ keys: '[MouseLeft>]', target: trigger });
+
+ await act(async () => {
+ await sleep(450);
+ });
+
+ await user.pointer({
+ keys: '[/MouseLeft]',
+ target: screen.getByRole('option', { name: 'Twenty' }),
+ });
+
+ expect(trigger).to.have.text('Ten');
+ expect(onChange.callCount).to.equal(0);
+ expect(screen.queryByRole('listbox', { hidden: false })).not.to.equal(null);
+ });
+
it('selects an option when dragging from the trigger and releasing after the drag delay', async () => {
const onChange = spy();
const { user } = render(
@@ -286,6 +314,7 @@ describe('', () => {
await act(async () => {
await sleep(250);
});
+ await user.pointer({ target: screen.getByRole('option', { name: 'Twenty' }) });
await user.pointer({
keys: '[/MouseLeft]',
target: screen.getByRole('option', { name: 'Twenty' }),
@@ -335,6 +364,7 @@ describe('', () => {
await act(async () => {
await sleep(250);
});
+ await user.pointer({ target: screen.getByRole('option', { name: 'Twenty' }) });
await user.pointer({
keys: '[/MouseLeft]',
target: screen.getByRole('option', { name: 'Twenty' }),
diff --git a/packages/mui-material/src/Select/SelectInput.js b/packages/mui-material/src/Select/SelectInput.js
index 7c6c82f938dd93..2f2c2c7b22ea59 100644
--- a/packages/mui-material/src/Select/SelectInput.js
+++ b/packages/mui-material/src/Select/SelectInput.js
@@ -193,6 +193,7 @@ const SelectInput = React.forwardRef(function SelectInput(props, ref) {
const hasSelectedItemInListRef = React.useRef(false);
const openingMouseUpListenerCleanupRef = React.useRef(null);
const didPointerDownOnItemRef = React.useRef(false);
+ const didPointerMoveToItemRef = React.useRef(false);
const selectionRef = React.useRef({
allowSelectedMouseUp: false,
allowUnselectedMouseUp: false,
@@ -257,6 +258,7 @@ const SelectInput = React.forwardRef(function SelectInput(props, ref) {
const resetMouseUpSelection = React.useCallback(() => {
clearSelectionTimers();
didPointerDownOnItemRef.current = false;
+ didPointerMoveToItemRef.current = false;
selectionRef.current = {
allowSelectedMouseUp: false,
allowUnselectedMouseUp: false,
@@ -527,6 +529,10 @@ const SelectInput = React.forwardRef(function SelectInput(props, ref) {
return;
}
+ if (!didPointerMoveToItemRef.current) {
+ return;
+ }
+
const disallowSelectedMouseUp = !selectionRef.current.allowSelectedMouseUp && selected;
const disallowUnselectedMouseUp = !selectionRef.current.allowUnselectedMouseUp && !selected;
@@ -720,6 +726,14 @@ const SelectInput = React.forwardRef(function SelectInput(props, ref) {
didPointerDownOnItemRef.current = true;
child.props.onPointerDown?.(event);
},
+ onMouseMove: (event) => {
+ didPointerMoveToItemRef.current = true;
+ child.props.onMouseMove?.(event);
+ },
+ onPointerMove: (event) => {
+ didPointerMoveToItemRef.current = true;
+ child.props.onPointerMove?.(event);
+ },
onClick: handleItemClick(child),
onMouseUp: handleItemMouseUp(child, selected),
onKeyUp: (event) => {