diff --git a/packages/react/src/select/trigger/SelectTrigger.test.tsx b/packages/react/src/select/trigger/SelectTrigger.test.tsx
index 5dd8283a515..cd58c74953b 100644
--- a/packages/react/src/select/trigger/SelectTrigger.test.tsx
+++ b/packages/react/src/select/trigger/SelectTrigger.test.tsx
@@ -102,13 +102,19 @@ describe('', () => {
});
describe('releasing the opening press', () => {
- async function pressTrigger(container?: ShadowRoot) {
+ async function pressTrigger(
+ options: {
+ container?: ShadowRoot;
+ onOpenChange?: Select.Root.Props['onOpenChange'];
+ release?: boolean;
+ } = {},
+ ) {
await render(
-
+
-
+
a
@@ -124,15 +130,33 @@ describe('', () => {
fireEvent.pointerDown(trigger, { pointerType: 'mouse', button: 0 });
fireEvent.mouseDown(trigger, { button: 0 });
+ if (options.release) {
+ fireEvent.mouseUp(trigger, { button: 0, clientX: 150, clientY: 120 });
+ }
await waitFor(() => {
expect(trigger).toHaveAttribute('aria-expanded', 'true');
});
- // The press is held long enough for the trigger to start watching its release.
+ return trigger;
+ }
+
+ it('ignores a later release when the opening press was released immediately', async () => {
+ const onOpenChange = vi.fn();
+ const trigger = await pressTrigger({ onOpenChange, release: true });
await act(async () => {
await wait(20);
});
- return trigger;
- }
+ onOpenChange.mockClear();
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
+
+ // e.g. a press that started inside the popup and was dragged off it
+ fireEvent.mouseUp(document.body, { button: 0, clientX: 10, clientY: 10 });
+ await act(async () => {
+ await wait(20);
+ });
+
+ expect(trigger).toHaveAttribute('aria-expanded', 'true');
+ expect(onOpenChange).not.toHaveBeenCalled();
+ });
it('keeps the popup open when the press slips up to 5px off the trigger', async () => {
const trigger = await pressTrigger();
@@ -161,7 +185,7 @@ describe('', () => {
const shadowRoot = host.attachShadow({ mode: 'open' });
try {
- const trigger = await pressTrigger(shadowRoot);
+ const trigger = await pressTrigger({ container: shadowRoot });
const popup = shadowRoot.querySelector('[role="listbox"]');
expect(popup).not.toBe(null);
diff --git a/packages/react/src/select/trigger/SelectTrigger.tsx b/packages/react/src/select/trigger/SelectTrigger.tsx
index 7a290c95eaf..59e9d7f8280 100644
--- a/packages/react/src/select/trigger/SelectTrigger.tsx
+++ b/packages/react/src/select/trigger/SelectTrigger.tsx
@@ -99,7 +99,6 @@ export const SelectTrigger = React.forwardRef(function SelectTrigger(
const setTriggerElement = store.useStateSetter('triggerElement');
const timeoutFocus = useTimeout();
- const timeoutMouseDown = useTimeout();
const selectedDelayTimeout = useTimeout();
React.useEffect(() => {
@@ -124,10 +123,8 @@ export const SelectTrigger = React.forwardRef(function SelectTrigger(
dragY: 0,
};
- timeoutMouseDown.clear();
-
return undefined;
- }, [open, store, timeoutMouseDown, selectedDelayTimeout]);
+ }, [open, store, selectedDelayTimeout]);
const mergedProps: HTMLProps = mergeProps<'button'>(
triggerProps,
@@ -201,10 +198,7 @@ export const SelectTrigger = React.forwardRef(function SelectTrigger(
store.setOpen(false, createChangeEventDetails(REASONS.cancelOpen, mouseEvent));
}
- // Firefox can fire this upon mousedown
- timeoutMouseDown.start(0, () => {
- doc.addEventListener('mouseup', handleMouseUp, { once: true });
- });
+ doc.addEventListener('mouseup', handleMouseUp, { once: true });
},
},
elementProps,