Skip to content

Commit a9ce237

Browse files
[usePress]: check hasPointerCapture before releasePointerCapture (#9123)
* usePress: check hasPointerCapture before releasePointerCapture * improve test coverage * add same fix to useCalendarCell --------- Co-authored-by: Reid Barber <[email protected]>
1 parent 808343c commit a9ce237

File tree

3 files changed

+30
-4
lines changed

3 files changed

+30
-4
lines changed

packages/@react-aria/calendar/src/useCalendarCell.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,13 @@ export function useCalendarCell(props: AriaCalendarCellProps, state: CalendarSta
338338
// outside the original pressed element.
339339
// (JSDOM does not support this)
340340
if ('releasePointerCapture' in e.target) {
341-
e.target.releasePointerCapture(e.pointerId);
341+
if ('hasPointerCapture' in e.target) {
342+
if (e.target.hasPointerCapture(e.pointerId)) {
343+
e.target.releasePointerCapture(e.pointerId);
344+
}
345+
} else {
346+
e.target.releasePointerCapture(e.pointerId);
347+
}
342348
}
343349
},
344350
onContextMenu(e) {

packages/@react-aria/interactions/src/usePress.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,13 @@ export function usePress(props: PressHookProps): PressResult {
596596
// This enables onPointerLeave and onPointerEnter to fire.
597597
let target = getEventTarget(e.nativeEvent);
598598
if ('releasePointerCapture' in target) {
599-
target.releasePointerCapture(e.pointerId);
599+
if ('hasPointerCapture' in target) {
600+
if (target.hasPointerCapture(e.pointerId)) {
601+
target.releasePointerCapture(e.pointerId);
602+
}
603+
} else {
604+
(target as Element).releasePointerCapture(e.pointerId);
605+
}
600606
}
601607
}
602608

packages/@react-aria/interactions/test/usePress.test.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -420,8 +420,10 @@ describe('usePress', function () {
420420

421421
let el = res.getByText('test');
422422
el.releasePointerCapture = jest.fn();
423+
el.hasPointerCapture = jest.fn().mockReturnValue(true);
423424
fireEvent(el, pointerEvent('pointerdown', {pointerId: 1, pointerType: 'mouse', clientX: 0, clientY: 0}));
424-
expect(el.releasePointerCapture).toHaveBeenCalled();
425+
expect(el.hasPointerCapture).toHaveBeenCalledWith(1);
426+
expect(el.releasePointerCapture).toHaveBeenCalledWith(1);
425427
// react listens for pointerout and pointerover instead of pointerleave and pointerenter...
426428
fireEvent(el, pointerEvent('pointerout', {pointerId: 1, pointerType: 'mouse', clientX: 100, clientY: 100}));
427429
fireEvent(document, pointerEvent('pointerup', {pointerId: 1, pointerType: 'mouse', clientX: 100, clientY: 100}));
@@ -560,6 +562,16 @@ describe('usePress', function () {
560562
]);
561563
});
562564

565+
it('should not call releasePointerCapture when hasPointerCapture returns false', function () {
566+
let res = render(<Example />);
567+
let el = res.getByText('test');
568+
el.releasePointerCapture = jest.fn();
569+
el.hasPointerCapture = jest.fn().mockReturnValue(false);
570+
fireEvent(el, pointerEvent('pointerdown', {pointerId: 1, pointerType: 'mouse', clientX: 0, clientY: 0}));
571+
expect(el.hasPointerCapture).toHaveBeenCalledWith(1);
572+
expect(el.releasePointerCapture).not.toHaveBeenCalled();
573+
});
574+
563575
it('should handle pointer cancel events', function () {
564576
let events = [];
565577
let addEvent = (e) => events.push(e);
@@ -4011,8 +4023,10 @@ describe('usePress', function () {
40114023

40124024
const el = shadowRoot.getElementById('testElement');
40134025
el.releasePointerCapture = jest.fn();
4026+
el.hasPointerCapture = jest.fn().mockReturnValue(true);
40144027
fireEvent(el, pointerEvent('pointerdown', {pointerId: 1, pointerType: 'mouse', clientX: 0, clientY: 0}));
4015-
expect(el.releasePointerCapture).toHaveBeenCalled();
4028+
expect(el.hasPointerCapture).toHaveBeenCalledWith(1);
4029+
expect(el.releasePointerCapture).toHaveBeenCalledWith(1);
40164030
// react listens for pointerout and pointerover instead of pointerleave and pointerenter...
40174031
fireEvent(el, pointerEvent('pointerout', {pointerId: 1, pointerType: 'mouse', clientX: 100, clientY: 100}));
40184032
fireEvent(document, pointerEvent('pointerup', {pointerId: 1, pointerType: 'mouse', clientX: 100, clientY: 100}));

0 commit comments

Comments
 (0)