From 8d2d3f812704781110b13a7966e917f4d5ab66d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Lesiecki?= Date: Fri, 31 Oct 2025 10:57:14 +0100 Subject: [PATCH] usePress: check hasPointerCapture before releasePointerCapture --- packages/@react-aria/interactions/src/usePress.ts | 8 +++++++- packages/@react-aria/interactions/test/usePress.test.js | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/@react-aria/interactions/src/usePress.ts b/packages/@react-aria/interactions/src/usePress.ts index 6dc4fd7f757..b9caaec7f6b 100644 --- a/packages/@react-aria/interactions/src/usePress.ts +++ b/packages/@react-aria/interactions/src/usePress.ts @@ -596,7 +596,13 @@ export function usePress(props: PressHookProps): PressResult { // This enables onPointerLeave and onPointerEnter to fire. let target = getEventTarget(e.nativeEvent); if ('releasePointerCapture' in target) { - target.releasePointerCapture(e.pointerId); + if ('hasPointerCapture' in target) { + if (target.hasPointerCapture(e.pointerId)) { + target.releasePointerCapture(e.pointerId); + } + } else { + (target as Element).releasePointerCapture(e.pointerId); + } } } diff --git a/packages/@react-aria/interactions/test/usePress.test.js b/packages/@react-aria/interactions/test/usePress.test.js index 92a07a57c0e..4de882eb89d 100644 --- a/packages/@react-aria/interactions/test/usePress.test.js +++ b/packages/@react-aria/interactions/test/usePress.test.js @@ -420,7 +420,9 @@ describe('usePress', function () { let el = res.getByText('test'); el.releasePointerCapture = jest.fn(); + el.hasPointerCapture = jest.fn().mockReturnValue(true); fireEvent(el, pointerEvent('pointerdown', {pointerId: 1, pointerType: 'mouse', clientX: 0, clientY: 0})); + expect(el.hasPointerCapture).toHaveBeenCalled(); expect(el.releasePointerCapture).toHaveBeenCalled(); // react listens for pointerout and pointerover instead of pointerleave and pointerenter... fireEvent(el, pointerEvent('pointerout', {pointerId: 1, pointerType: 'mouse', clientX: 100, clientY: 100})); @@ -4011,7 +4013,9 @@ describe('usePress', function () { const el = shadowRoot.getElementById('testElement'); el.releasePointerCapture = jest.fn(); + el.hasPointerCapture = jest.fn().mockReturnValue(true); fireEvent(el, pointerEvent('pointerdown', {pointerId: 1, pointerType: 'mouse', clientX: 0, clientY: 0})); + expect(el.hasPointerCapture).toHaveBeenCalled(); expect(el.releasePointerCapture).toHaveBeenCalled(); // react listens for pointerout and pointerover instead of pointerleave and pointerenter... fireEvent(el, pointerEvent('pointerout', {pointerId: 1, pointerType: 'mouse', clientX: 100, clientY: 100}));