[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)
Nicolas Gallagher committed
Sep 23, 2019 at 10:48 UTC
312b462d5444c231d5acf656efea6780ad9ba89d
2 files changed
+33
-31
packages/react-interactions/events/src/dom/Tap.js
+16
-16
@@ -350,23 +350,26 @@ function isActivePointer(
350
}
351
}
352
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';
357
return (
358
// middle-click
359
buttons === buttonsEnum.auxiliary ||
360
// open-in-new-tab
358
- (buttons === buttonsEnum.primary && nativeEvent.metaKey) ||
361
+ (isPrimaryPointer && nativeEvent.metaKey) ||
362
// open-in-new-window
360
- (buttons === buttonsEnum.primary && nativeEvent.shiftKey)
363
+ (isPrimaryPointer && nativeEvent.shiftKey)
364
);
365
}
366
367
function shouldActivate(event: ReactDOMResponderEvent): boolean {
368
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);
373
}
374
375
/**
@@ -514,10 +517,7 @@ const responderImpl = {
517
518
if (!state.isActive) {
519
const activate = shouldActivate(event);
517
- const activateAuxiliary = isAuxiliary(
518
- nativeEvent.buttons,
519
- nativeEvent,
520
- );
520
+ const activateAuxiliary = isAuxiliary(nativeEvent.buttons, event);
521
522
if (activate || activateAuxiliary) {
523
state.buttons = nativeEvent.buttons;
@@ -531,7 +531,9 @@ const responderImpl = {
531
}
532
}
533
534
- if (activate) {
534
+ if (activateAuxiliary) {
535
+ state.isAuxiliaryActive = true;
536
+ } else if (activate) {
537
const gestureState = createGestureState(
538
context,
539
props,
@@ -544,8 +546,6 @@ const responderImpl = {
546
state.initialPosition.x = gestureState.x;
547
state.initialPosition.y = gestureState.y;
548
dispatchStart(context, props, state);
547
- } else if (activateAuxiliary) {
548
- state.isAuxiliaryActive = true;
549
}
550
}
551
break;
@@ -611,7 +611,7 @@ const responderImpl = {
611
if (state.isActive && isActivePointer(event, state)) {
612
state.gestureState = createGestureState(context, props, state, event);
613
state.isActive = false;
614
- if (isAuxiliary(state.buttons, nativeEvent)) {
614
+ if (isAuxiliary(state.buttons, event)) {
615
dispatchCancel(context, props, state);
616
dispatchAuxiliaryTap(context, props, state);
617
// Remove the root events here as no 'click' event is dispatched
@@ -626,7 +626,7 @@ const responderImpl = {
626
}
627
} else if (
628
state.isAuxiliaryActive &&
629
- isAuxiliary(state.buttons, nativeEvent)
629
+ isAuxiliary(state.buttons, event)
630
) {
631
state.isAuxiliaryActive = false;
632
state.gestureState = createGestureState(context, props, state, event);
packages/react-interactions/events/src/dom/__tests__/Tap-test.internal.js
+17
-15
@@ -302,29 +302,31 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
302
expect(onTapStart).toHaveBeenCalledTimes(1);
303
});
304
305
- test('ignored buttons and modifiers', () => {
305
+ testWithPointerType('ignored buttons and modifiers', pointerType => {
306
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
+ }
319
// alt-click
318
- target.pointerdown({buttons: primary, altKey: true});
320
+ target.pointerdown({buttons: primary, altKey: true, pointerType});
321
target.pointerup();
322
// ctrl-click
321
- target.pointerdown({buttons: primary, ctrlKey: true});
323
+ target.pointerdown({buttons: primary, ctrlKey: true, pointerType});
324
target.pointerup();
325
// meta-click
324
- target.pointerdown({buttons: primary, metaKey: true});
326
+ target.pointerdown({buttons: primary, metaKey: true, pointerType});
327
target.pointerup();
328
// shift-click
327
- target.pointerdown({buttons: primary, shiftKey: true});
329
+ target.pointerdown({buttons: primary, shiftKey: true, pointerType});
330
target.pointerup();
331
332
expect(onTapStart).toHaveBeenCalledTimes(0);