Skip to content

Commit 312b462

Browse files
authored
[react-interactions] Improve consistency of Tap responder (#16837)
Makes sure that touch events with modifier keys behave the same way as other pointer types (i.e., does not call `onTapStart` if the gesture begins with a modifier key held down)
1 parent 70754f1 commit 312b462

File tree

2 files changed

+33
-31
lines changed

2 files changed

+33
-31
lines changed

packages/react-interactions/events/src/dom/Tap.js

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -350,23 +350,26 @@ function isActivePointer(
350350
}
351351
}
352352

353-
function isAuxiliary(buttons: number, nativeEvent: any): boolean {
353+
function isAuxiliary(buttons: number, event: ReactDOMResponderEvent): boolean {
354+
const nativeEvent: any = event.nativeEvent;
355+
const isPrimaryPointer =
356+
buttons === buttonsEnum.primary || event.pointerType === 'touch';
354357
return (
355358
// middle-click
356359
buttons === buttonsEnum.auxiliary ||
357360
// open-in-new-tab
358-
(buttons === buttonsEnum.primary && nativeEvent.metaKey) ||
361+
(isPrimaryPointer && nativeEvent.metaKey) ||
359362
// open-in-new-window
360-
(buttons === buttonsEnum.primary && nativeEvent.shiftKey)
363+
(isPrimaryPointer && nativeEvent.shiftKey)
361364
);
362365
}
363366

364367
function shouldActivate(event: ReactDOMResponderEvent): boolean {
365368
const nativeEvent: any = event.nativeEvent;
366-
const pointerType = event.pointerType;
367-
const buttons = nativeEvent.buttons;
368-
const isValidButton = buttons === buttonsEnum.primary;
369-
return pointerType === 'touch' || (isValidButton && !hasModifierKey(event));
369+
const isPrimaryPointer =
370+
nativeEvent.buttons === buttonsEnum.primary ||
371+
event.pointerType === 'touch';
372+
return isPrimaryPointer && !hasModifierKey(event);
370373
}
371374

372375
/**
@@ -514,10 +517,7 @@ const responderImpl = {
514517

515518
if (!state.isActive) {
516519
const activate = shouldActivate(event);
517-
const activateAuxiliary = isAuxiliary(
518-
nativeEvent.buttons,
519-
nativeEvent,
520-
);
520+
const activateAuxiliary = isAuxiliary(nativeEvent.buttons, event);
521521

522522
if (activate || activateAuxiliary) {
523523
state.buttons = nativeEvent.buttons;
@@ -531,7 +531,9 @@ const responderImpl = {
531531
}
532532
}
533533

534-
if (activate) {
534+
if (activateAuxiliary) {
535+
state.isAuxiliaryActive = true;
536+
} else if (activate) {
535537
const gestureState = createGestureState(
536538
context,
537539
props,
@@ -544,8 +546,6 @@ const responderImpl = {
544546
state.initialPosition.x = gestureState.x;
545547
state.initialPosition.y = gestureState.y;
546548
dispatchStart(context, props, state);
547-
} else if (activateAuxiliary) {
548-
state.isAuxiliaryActive = true;
549549
}
550550
}
551551
break;
@@ -611,7 +611,7 @@ const responderImpl = {
611611
if (state.isActive && isActivePointer(event, state)) {
612612
state.gestureState = createGestureState(context, props, state, event);
613613
state.isActive = false;
614-
if (isAuxiliary(state.buttons, nativeEvent)) {
614+
if (isAuxiliary(state.buttons, event)) {
615615
dispatchCancel(context, props, state);
616616
dispatchAuxiliaryTap(context, props, state);
617617
// Remove the root events here as no 'click' event is dispatched
@@ -626,7 +626,7 @@ const responderImpl = {
626626
}
627627
} else if (
628628
state.isAuxiliaryActive &&
629-
isAuxiliary(state.buttons, nativeEvent)
629+
isAuxiliary(state.buttons, event)
630630
) {
631631
state.isAuxiliaryActive = false;
632632
state.gestureState = createGestureState(context, props, state, event);

packages/react-interactions/events/src/dom/__tests__/Tap-test.internal.js

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -302,29 +302,31 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
302302
expect(onTapStart).toHaveBeenCalledTimes(1);
303303
});
304304

305-
test('ignored buttons and modifiers', () => {
305+
testWithPointerType('ignored buttons and modifiers', pointerType => {
306306
const target = createEventTarget(ref.current);
307-
const primary = buttonsType.primary;
308-
// right-click
309-
target.pointerdown({buttons: buttonsType.secondary});
310-
target.pointerup();
311-
// middle-click
312-
target.pointerdown({buttons: buttonsType.auxiliary});
313-
target.pointerup();
314-
// pen eraser
315-
target.pointerdown({buttons: buttonsType.eraser});
316-
target.pointerup();
307+
const {auxiliary, eraser, primary, secondary} = buttonsType;
308+
if (pointerType !== 'touch') {
309+
// right-click
310+
target.pointerdown({buttons: secondary, pointerType});
311+
target.pointerup();
312+
// middle-click
313+
target.pointerdown({buttons: auxiliary, pointerType});
314+
target.pointerup();
315+
// pen eraser
316+
target.pointerdown({buttons: eraser, pointerType});
317+
target.pointerup();
318+
}
317319
// alt-click
318-
target.pointerdown({buttons: primary, altKey: true});
320+
target.pointerdown({buttons: primary, altKey: true, pointerType});
319321
target.pointerup();
320322
// ctrl-click
321-
target.pointerdown({buttons: primary, ctrlKey: true});
323+
target.pointerdown({buttons: primary, ctrlKey: true, pointerType});
322324
target.pointerup();
323325
// meta-click
324-
target.pointerdown({buttons: primary, metaKey: true});
326+
target.pointerdown({buttons: primary, metaKey: true, pointerType});
325327
target.pointerup();
326328
// shift-click
327-
target.pointerdown({buttons: primary, shiftKey: true});
329+
target.pointerdown({buttons: primary, shiftKey: true, pointerType});
328330
target.pointerup();
329331

330332
expect(onTapStart).toHaveBeenCalledTimes(0);

0 commit comments

Comments
 (0)