@samitouri / QOS-React-2 / commits / fd870e6b6a

[react-ui/events] Tap responder API changes (#16827)

This patch limits the `onTap*` callbacks to the primary pointer button. Auxiliary button and modified primary button interactions call `onAuxiliaryTap`, cancel any active tap, and preserve the native behavior.

Nicolas Gallagher committed Sep 19, 2019 at 09:36 UTC fd870e6b6a9c35cf4bb77d83dbfe61e07327368f
7 files changed +254 -143
packages/react-ui/events/src/dom/Press.js
+16 -3
@@ -34,7 +34,7 @@ type PressEventType =
34
35 type PressEvent = {|
36 altKey: boolean,
37 - buttons: null | 0 | 1 | 4,
37 + buttons: null | 1 | 4,
38 ctrlKey: boolean,
39 defaultPrevented: boolean,
40 key: null | string,
@@ -53,7 +53,7 @@ type PressEvent = {|
53 function createGestureState(e: any, type: PressEventType): PressEvent {
54 return {
55 altKey: e.altKey,
56 - buttons: e.buttons,
56 + buttons: e.type === 'tap:auxiliary' ? 4 : 1,
57 ctrlKey: e.ctrlKey,
58 defaultPrevented: e.defaultPrevented,
59 key: e.key,
@@ -103,6 +103,19 @@ export function usePress(props: PressProps) {
103 const tap = useTap({
104 disabled: disabled || active === 'keyboard',
105 preventDefault,
106 + onAuxiliaryTap(e) {
107 + if (onPressStart != null) {
108 + onPressStart(createGestureState(e, 'pressstart'));
109 + }
110 + if (onPressEnd != null) {
111 + onPressEnd(createGestureState(e, 'pressend'));
112 + }
113 + // Here we rely on Tap only calling 'onAuxiliaryTap' with modifiers when
114 + // the primary button is pressed
115 + if (onPress != null && (e.metaKey || e.shiftKey)) {
116 + onPress(createGestureState(e, 'press'));
117 + }
118 + },
119 onTapStart(e) {
120 if (active == null) {
121 updateActive('tap');
@@ -124,7 +137,7 @@ export function usePress(props: PressProps) {
137 if (onPressEnd != null) {
138 onPressEnd(createGestureState(e, 'pressend'));
139 }
127 - if (onPress != null && e.buttons !== 4) {
140 + if (onPress != null) {
141 onPress(createGestureState(e, 'press'));
142 }
143 updateActive(null);
packages/react-ui/events/src/dom/Tap.js
+93 -51
@@ -17,18 +17,18 @@ import type {ReactEventResponderListener} from 'shared/ReactTypes';
17 import React from 'react';
18 import {
19 buttonsEnum,
20 - hasPointerEvents,
21 - isMac,
20 dispatchDiscreteEvent,
21 dispatchUserBlockingEvent,
22 getTouchById,
23 hasModifierKey,
24 + hasPointerEvents,
25 } from './shared';
26
27 type TapProps = $ReadOnly<{|
28 disabled?: boolean,
29 maximumDistance?: number,
30 preventDefault?: boolean,
31 + onAuxiliaryTap?: (e: TapEvent) => void,
32 onTapCancel?: (e: TapEvent) => void,
33 onTapChange?: boolean => void,
34 onTapEnd?: (e: TapEvent) => void,
@@ -38,7 +38,6 @@ type TapProps = $ReadOnly<{|
38
39 type TapGestureState = {|
40 altKey: boolean,
41 - buttons: 0 | 1 | 4,
41 ctrlKey: boolean,
42 height: number,
43 metaKey: boolean,
@@ -67,13 +66,15 @@ type TapState = {|
66 ignoreEmulatedEvents: boolean,
67 initialPosition: {|x: number, y: number|},
68 isActive: boolean,
69 + isAuxiliaryActive: boolean,
70 pointerType: PointerType,
71 responderTarget: null | Element,
72 rootEvents: null | Array<string>,
73 - shouldPreventClick: boolean,
73 + shouldPreventDefault: boolean,
74 |};
75
76 type TapEventType =
77 + | 'tap:auxiliary'
78 | 'tap:cancel'
79 | 'tap:change'
80 | 'tap:end'
@@ -125,14 +126,14 @@ function createInitialState(): TapState {
126 buttons: 0,
127 ignoreEmulatedEvents: false,
128 isActive: false,
129 + isAuxiliaryActive: false,
130 initialPosition: {x: 0, y: 0},
131 pointerType: '',
132 responderTarget: null,
133 rootEvents: null,
132 - shouldPreventClick: true,
134 + shouldPreventDefault: true,
135 gestureState: {
136 altKey: false,
135 - buttons: 0,
137 ctrlKey: false,
138 height: 1,
139 metaKey: false,
@@ -187,7 +188,6 @@ function createPointerEventGestureState(
188
189 return {
190 altKey,
190 - buttons: state.buttons,
191 ctrlKey,
192 height,
193 metaKey,
@@ -249,7 +249,6 @@ function createFallbackGestureState(
249
250 return {
251 altKey,
252 - buttons: state.buttons != null ? state.buttons : 1,
252 ctrlKey,
253 height: !isCancelType && radiusY != null ? radiusY * 2 : 1,
254 metaKey,
@@ -351,19 +350,23 @@ function isActivePointer(
350 }
351 }
352
353 +function isAuxiliary(buttons: number, nativeEvent: any): boolean {
354 + return (
355 + // middle-click
356 + buttons === buttonsEnum.auxiliary ||
357 + // open-in-new-tab
358 + (buttons === buttonsEnum.primary && nativeEvent.metaKey) ||
359 + // open-in-new-window
360 + (buttons === buttonsEnum.primary && nativeEvent.shiftKey)
361 + );
362 +}
363 +
364 function shouldActivate(event: ReactDOMResponderEvent): boolean {
365 const nativeEvent: any = event.nativeEvent;
366 const pointerType = event.pointerType;
367 const buttons = nativeEvent.buttons;
358 - const isContextMenu = pointerType === 'mouse' && nativeEvent.ctrlKey && isMac;
359 - const isValidButton =
360 - buttons === buttonsEnum.primary || buttons === buttonsEnum.middle;
361 -
362 - if (pointerType === 'touch' || (isValidButton && !isContextMenu)) {
363 - return true;
364 - } else {
365 - return false;
366 - }
368 + const isValidButton = buttons === buttonsEnum.primary;
369 + return pointerType === 'touch' || (isValidButton && !hasModifierKey(event));
370 }
371
372 /**
@@ -418,7 +421,7 @@ function dispatchEnd(
421 const onTapEnd = props.onTapEnd;
422 dispatchChange(context, props, state);
423 if (onTapEnd != null) {
421 - const defaultPrevented = state.shouldPreventClick === true;
424 + const defaultPrevented = state.shouldPreventDefault === true;
425 const payload = context.objectAssign({}, state.gestureState, {
426 defaultPrevented,
427 type,
@@ -441,6 +444,22 @@ function dispatchCancel(
444 }
445 }
446
447 +function dispatchAuxiliaryTap(
448 + context: ReactDOMResponderContext,
449 + props: TapProps,
450 + state: TapState,
451 +): void {
452 + const type = 'tap:auxiliary';
453 + const onAuxiliaryTap = props.onAuxiliaryTap;
454 + if (onAuxiliaryTap != null) {
455 + const payload = context.objectAssign({}, state.gestureState, {
456 + defaultPrevented: false,
457 + type,
458 + });
459 + dispatchDiscreteEvent(context, payload, onAuxiliaryTap);
460 + }
461 +}
462 +
463 /**
464 * Responder implementation
465 */
@@ -493,26 +512,41 @@ const responderImpl = {
512 }
513 }
514
496 - if (!state.isActive && shouldActivate(event)) {
497 - state.isActive = true;
498 - state.buttons = nativeEvent.buttons;
499 - state.pointerType = event.pointerType;
500 - state.responderTarget = context.getResponderNode();
501 - state.shouldPreventClick = props.preventDefault !== false;
502 -
503 - const gestureState = createGestureState(context, props, state, event);
504 - state.gestureState = gestureState;
505 - state.initialPosition.x = gestureState.x;
506 - state.initialPosition.y = gestureState.y;
507 -
508 - dispatchStart(context, props, state);
509 - addRootEventTypes(rootEventTypes, context, state);
510 -
511 - if (!hasPointerEvents) {
512 - if (eventType === 'touchstart') {
513 - state.ignoreEmulatedEvents = true;
515 + if (!state.isActive) {
516 + const activate = shouldActivate(event);
517 + const activateAuxiliary = isAuxiliary(
518 + nativeEvent.buttons,
519 + nativeEvent,
520 + );
521 +
522 + if (activate || activateAuxiliary) {
523 + state.buttons = nativeEvent.buttons;
524 + state.pointerType = event.pointerType;
525 + state.responderTarget = context.getResponderNode();
526 + addRootEventTypes(rootEventTypes, context, state);
527 + if (!hasPointerEvents) {
528 + if (eventType === 'touchstart') {
529 + state.ignoreEmulatedEvents = true;
530 + }
531 }
532 }
533 +
534 + if (activate) {
535 + const gestureState = createGestureState(
536 + context,
537 + props,
538 + state,
539 + event,
540 + );
541 + state.isActive = true;
542 + state.shouldPreventDefault = props.preventDefault !== false;
543 + state.gestureState = gestureState;
544 + state.initialPosition.x = gestureState.x;
545 + state.initialPosition.y = gestureState.y;
546 + dispatchStart(context, props, state);
547 + } else if (activateAuxiliary) {
548 + state.isAuxiliaryActive = true;
549 + }
550 }
551 break;
552 }
@@ -575,24 +609,30 @@ const responderImpl = {
609 case 'mouseup':
610 case 'touchend': {
611 if (state.isActive && isActivePointer(event, state)) {
578 - if (state.buttons === buttonsEnum.middle) {
579 - // Remove the root events here as no 'click' event is dispatched
580 - // when this 'button' is pressed.
581 - removeRootEventTypes(context, state);
582 - }
583 -
612 state.gestureState = createGestureState(context, props, state, event);
585 -
613 state.isActive = false;
587 - if (context.isTargetWithinResponder(hitTarget)) {
588 - // Determine whether to call preventDefault on subsequent native events.
589 - if (hasModifierKey(event)) {
590 - state.shouldPreventClick = false;
591 - }
592 - dispatchEnd(context, props, state);
593 - } else {
614 + if (isAuxiliary(state.buttons, nativeEvent)) {
615 + dispatchCancel(context, props, state);
616 + dispatchAuxiliaryTap(context, props, state);
617 + // Remove the root events here as no 'click' event is dispatched
618 + removeRootEventTypes(context, state);
619 + } else if (
620 + !context.isTargetWithinResponder(hitTarget) ||
621 + hasModifierKey(event)
622 + ) {
623 dispatchCancel(context, props, state);
624 + } else {
625 + dispatchEnd(context, props, state);
626 }
627 + } else if (
628 + state.isAuxiliaryActive &&
629 + isAuxiliary(state.buttons, nativeEvent)
630 + ) {
631 + state.isAuxiliaryActive = false;
632 + state.gestureState = createGestureState(context, props, state, event);
633 + dispatchAuxiliaryTap(context, props, state);
634 + // Remove the root events here as no 'click' event is dispatched
635 + removeRootEventTypes(context, state);
636 }
637
638 if (!hasPointerEvents) {
@@ -612,6 +652,7 @@ const responderImpl = {
652 state.gestureState = createGestureState(context, props, state, event);
653 state.isActive = false;
654 dispatchCancel(context, props, state);
655 + removeRootEventTypes(context, state);
656 }
657 break;
658 }
@@ -630,12 +671,13 @@ const responderImpl = {
671 state.gestureState = createGestureState(context, props, state, event);
672 state.isActive = false;
673 dispatchCancel(context, props, state);
674 + removeRootEventTypes(context, state);
675 }
676 break;
677 }
678
679 case 'click': {
638 - if (state.shouldPreventClick) {
680 + if (state.shouldPreventDefault) {
681 nativeEvent.preventDefault();
682 }
683 removeRootEventTypes(context, state);
packages/react-ui/events/src/dom/__tests__/Press-test.internal.js
+13 -5
@@ -125,11 +125,13 @@ describeWithPointerEvent('Press responder', hasPointerEvents => {
125
126 it('is called after middle-button pointer down', () => {
127 const target = createEventTarget(ref.current);
128 - target.pointerdown({buttons: buttonsType.middle, pointerType: 'mouse'});
128 + const pointerType = 'mouse';
129 + target.pointerdown({buttons: buttonsType.auxiliary, pointerType});
130 + target.pointerup({pointerType});
131 expect(onPressStart).toHaveBeenCalledTimes(1);
132 expect(onPressStart).toHaveBeenCalledWith(
133 expect.objectContaining({
132 - buttons: buttonsType.middle,
134 + buttons: buttonsType.auxiliary,
135 pointerType: 'mouse',
136 type: 'pressstart',
137 }),
@@ -209,12 +211,15 @@ describeWithPointerEvent('Press responder', hasPointerEvents => {
211
212 it('is called after middle-button pointer up', () => {
213 const target = createEventTarget(ref.current);
212 - target.pointerdown({buttons: buttonsType.middle, pointerType: 'mouse'});
214 + target.pointerdown({
215 + buttons: buttonsType.auxiliary,
216 + pointerType: 'mouse',
217 + });
218 target.pointerup({pointerType: 'mouse'});
219 expect(onPressEnd).toHaveBeenCalledTimes(1);
220 expect(onPressEnd).toHaveBeenCalledWith(
221 expect.objectContaining({
217 - buttons: buttonsType.middle,
222 + buttons: buttonsType.auxiliary,
223 pointerType: 'mouse',
224 type: 'pressend',
225 }),
@@ -350,7 +355,10 @@ describeWithPointerEvent('Press responder', hasPointerEvents => {
355
356 it('is not called after middle-button press', () => {
357 const target = createEventTarget(ref.current);
353 - target.pointerdown({buttons: buttonsType.middle, pointerType: 'mouse'});
358 + target.pointerdown({
359 + buttons: buttonsType.auxiliary,
360 + pointerType: 'mouse',
361 + });
362 target.pointerup({pointerType: 'mouse'});
363 expect(onPress).not.toHaveBeenCalled();
364 });
packages/react-ui/events/src/dom/__tests__/PressLegacy-test.internal.js
+18 -6
@@ -120,11 +120,14 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
120
121 it('is called after middle-button pointer down', () => {
122 const target = createEventTarget(ref.current);
123 - target.pointerdown({buttons: buttonsType.middle, pointerType: 'mouse'});
123 + target.pointerdown({
124 + buttons: buttonsType.auxiliary,
125 + pointerType: 'mouse',
126 + });
127 expect(onPressStart).toHaveBeenCalledTimes(1);
128 expect(onPressStart).toHaveBeenCalledWith(
129 expect.objectContaining({
127 - buttons: buttonsType.middle,
130 + buttons: buttonsType.auxiliary,
131 pointerType: 'mouse',
132 type: 'pressstart',
133 }),
@@ -135,7 +138,10 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
138 const node = ref.current;
139 const target = createEventTarget(node);
140 target.setBoundingClientRect({x: 0, y: 0, width: 100, height: 100});
138 - target.pointerdown({buttons: buttonsType.middle, pointerType: 'mouse'});
141 + target.pointerdown({
142 + buttons: buttonsType.auxiliary,
143 + pointerType: 'mouse',
144 + });
145 target.pointerup({pointerType: 'mouse'});
146 target.pointerhover({x: 110, y: 110});
147 target.pointerhover({x: 50, y: 50});
@@ -215,12 +221,15 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
221
222 it('is called after middle-button pointer up', () => {
223 const target = createEventTarget(ref.current);
218 - target.pointerdown({buttons: buttonsType.middle, pointerType: 'mouse'});
224 + target.pointerdown({
225 + buttons: buttonsType.auxiliary,
226 + pointerType: 'mouse',
227 + });
228 target.pointerup({pointerType: 'mouse'});
229 expect(onPressEnd).toHaveBeenCalledTimes(1);
230 expect(onPressEnd).toHaveBeenCalledWith(
231 expect.objectContaining({
223 - buttons: buttonsType.middle,
232 + buttons: buttonsType.auxiliary,
233 pointerType: 'mouse',
234 type: 'pressend',
235 }),
@@ -356,7 +365,10 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
365
366 it('is not called after middle-button press', () => {
367 const target = createEventTarget(ref.current);
359 - target.pointerdown({buttons: buttonsType.middle, pointerType: 'mouse'});
368 + target.pointerdown({
369 + buttons: buttonsType.auxiliary,
370 + pointerType: 'mouse',
371 + });
372 target.pointerup({pointerType: 'mouse'});
373 expect(onPress).not.toHaveBeenCalled();
374 });
packages/react-ui/events/src/dom/__tests__/Tap-test.internal.js
+112 -76
@@ -193,6 +193,39 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
193 });
194 });
195
196 + describe('onAuxiliaryTap', () => {
197 + let onAuxiliaryTap, ref;
198 +
199 + beforeEach(() => {
200 + onAuxiliaryTap = jest.fn();
201 + ref = React.createRef();
202 + const Component = () => {
203 + const listener = useTap({onAuxiliaryTap});
204 + return <div ref={ref} listeners={listener} />;
205 + };
206 + ReactDOM.render(<Component />, container);
207 + document.elementFromPoint = () => ref.current;
208 + });
209 +
210 + test('auxiliary-button pointer up', () => {
211 + const pointerType = 'mouse';
212 + const buttons = buttonsType.auxiliary;
213 + const target = createEventTarget(ref.current);
214 + target.pointerdown({buttons, pointerType});
215 + target.pointerup({pointerType});
216 + expect(onAuxiliaryTap).toHaveBeenCalledTimes(1);
217 + });
218 +
219 + test('modifier-button pointer up', () => {
220 + const pointerType = 'mouse';
221 + const buttons = buttonsType.primary;
222 + const target = createEventTarget(ref.current);
223 + target.pointerdown({buttons, pointerType});
224 + target.pointerup({metaKey: true, pointerType});
225 + expect(onAuxiliaryTap).toHaveBeenCalledTimes(1);
226 + });
227 + });
228 +
229 describe('onTapStart', () => {
230 let onTapStart, ref;
231
@@ -210,6 +243,7 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
243 testWithPointerType('pointer down', pointerType => {
244 const target = createEventTarget(ref.current);
245 const nativeEvent = {
246 + buttons: buttonsType.primary,
247 pageX: 10,
248 pageY: 10,
249 pointerType,
@@ -226,7 +260,6 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
260 expect(onTapStart).toHaveBeenCalledWith(
261 expect.objectContaining({
262 altKey: false,
229 - buttons: 1,
263 ctrlKey: false,
264 height: pointerType === 'mouse' ? 1 : 23,
265 metaKey: false,
@@ -269,36 +302,31 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
302 expect(onTapStart).toHaveBeenCalledTimes(1);
303 });
304
272 - test('primary-button pointer down', () => {
273 - const pointerType = 'mouse';
274 - const buttons = buttonsType.primary;
275 - const target = createEventTarget(ref.current);
276 - target.pointerdown({buttons, pointerType});
277 - expect(onTapStart).toHaveBeenCalledTimes(1);
278 - expect(onTapStart).toHaveBeenCalledWith(
279 - expect.objectContaining({buttons, pointerType}),
280 - );
281 - });
282 -
283 - test('middle-button pointer down', () => {
284 - const pointerType = 'mouse';
285 - const buttons = buttonsType.middle;
286 - const target = createEventTarget(ref.current);
287 - target.pointerdown({buttons, pointerType});
288 - expect(onTapStart).toHaveBeenCalledTimes(1);
289 - expect(onTapStart).toHaveBeenCalledWith(
290 - expect.objectContaining({buttons, pointerType}),
291 - );
292 - });
293 -
294 - test('unsupported buttons', () => {
305 + test('ignored buttons and modifiers', () => {
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();
317 + // alt-click
318 + target.pointerdown({buttons: primary, altKey: true});
319 + target.pointerup();
320 + // ctrl-click
321 + target.pointerdown({buttons: primary, ctrlKey: true});
322 + target.pointerup();
323 + // meta-click
324 + target.pointerdown({buttons: primary, metaKey: true});
325 + target.pointerup();
326 + // shift-click
327 + target.pointerdown({buttons: primary, shiftKey: true});
328 + target.pointerup();
329 +
330 expect(onTapStart).toHaveBeenCalledTimes(0);
331 });
332 });
@@ -319,7 +347,7 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
347
348 testWithPointerType('pointer up', pointerType => {
349 const target = createEventTarget(ref.current);
322 - target.pointerdown({pointerType});
350 + target.pointerdown({buttons: buttonsType.primary, pointerType});
351 target.pointerup({
352 pageX: 10,
353 pageY: 10,
@@ -331,7 +359,6 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
359 expect(onTapEnd).toHaveBeenCalledWith(
360 expect.objectContaining({
361 altKey: false,
334 - buttons: 1,
362 ctrlKey: false,
363 height: pointerType === 'mouse' ? 1 : 23,
364 metaKey: false,
@@ -356,30 +383,6 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
383 );
384 });
385
359 - test('primary-button pointer up', () => {
360 - const pointerType = 'mouse';
361 - const buttons = buttonsType.primary;
362 - const target = createEventTarget(ref.current);
363 - target.pointerdown({buttons, pointerType});
364 - target.pointerup({pointerType});
365 - expect(onTapEnd).toHaveBeenCalledTimes(1);
366 - expect(onTapEnd).toHaveBeenCalledWith(
367 - expect.objectContaining({buttons, pointerType}),
368 - );
369 - });
370 -
371 - test('middle-button pointer up', () => {
372 - const pointerType = 'mouse';
373 - const buttons = buttonsType.middle;
374 - const target = createEventTarget(ref.current);
375 - target.pointerdown({buttons, pointerType});
376 - target.pointerup({pointerType});
377 - expect(onTapEnd).toHaveBeenCalledTimes(1);
378 - expect(onTapEnd).toHaveBeenCalledWith(
379 - expect.objectContaining({buttons, pointerType}),
380 - );
381 - });
382 -
386 testWithPointerType('zero-dimension hit rect', pointerType => {
387 const targetRef = React.createRef();
388 const innerRef = React.createRef();
@@ -414,6 +417,34 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
417 });
418 expect(onTapEnd).not.toBeCalled();
419 });
420 +
421 + test('ignored buttons and modifiers', () => {
422 + const target = createEventTarget(ref.current);
423 + const primary = buttonsType.primary;
424 + // right-click
425 + target.pointerdown({buttons: buttonsType.secondary});
426 + target.pointerup();
427 + // middle-click
428 + target.pointerdown({buttons: buttonsType.auxiliary});
429 + target.pointerup();
430 + // pen eraser
431 + target.pointerdown({buttons: buttonsType.eraser});
432 + target.pointerup();
433 + // alt-click
434 + target.pointerdown({buttons: primary});
435 + target.pointerup({altKey: true});
436 + // ctrl-click
437 + target.pointerdown({buttons: primary});
438 + target.pointerup({ctrlKey: true});
439 + // meta-click
440 + target.pointerdown({buttons: primary});
441 + target.pointerup({metaKey: true});
442 + // shift-click
443 + target.pointerdown({buttons: primary});
444 + target.pointerup({shiftKey: true});
445 +
446 + expect(onTapEnd).toHaveBeenCalledTimes(0);
447 + });
448 });
449
450 describe('onTapUpdate', () => {
@@ -449,7 +480,6 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
480 expect(onTapUpdate).toHaveBeenCalledWith(
481 expect.objectContaining({
482 altKey: false,
452 - buttons: 1,
483 ctrlKey: false,
484 height: pointerType === 'mouse' ? 1 : 23,
485 metaKey: false,
@@ -594,7 +624,6 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
624 expect(onTapCancel).toHaveBeenCalledWith(
625 expect.objectContaining({
626 altKey: false,
597 - buttons: buttonsType.primary,
627 ctrlKey: false,
628 height: 1,
629 metaKey: false,
@@ -621,6 +650,37 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
650 expect(onTapUpdate).not.toBeCalled();
651 });
652
653 + testWithPointerType('pointer move outside target', pointerType => {
654 + const downTarget = createEventTarget(ref.current);
655 + const upTarget = createEventTarget(container);
656 + tapAndMoveOutside({
657 + hasPointerEvents,
658 + downTarget,
659 + upTarget,
660 + pointerType,
661 + });
662 + expect(onTapCancel).toBeCalled();
663 + });
664 +
665 + test('ignored modifiers', () => {
666 + const target = createEventTarget(ref.current);
667 + const primary = buttonsType.primary;
668 + // alt-click
669 + target.pointerdown({buttons: primary});
670 + target.pointerup({altKey: true});
671 + // ctrl-click
672 + target.pointerdown({buttons: primary});
673 + target.pointerup({ctrlKey: true});
674 + // meta-click
675 + target.pointerdown({buttons: primary});
676 + target.pointerup({metaKey: true});
677 + // shift-click
678 + target.pointerdown({buttons: primary});
679 + target.pointerup({shiftKey: true});
680 +
681 + expect(onTapCancel).toHaveBeenCalledTimes(4);
682 + });
683 +
684 test('long press context menu', () => {
685 const target = createEventTarget(ref.current);
686 target.contextmenu({}, {pointerType: 'touch'});
@@ -659,18 +719,6 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
719 containerTarget.scroll();
720 expect(onTapCancel).toHaveBeenCalledTimes(1);
721 });
662 -
663 - testWithPointerType('pointer move outside target', pointerType => {
664 - const downTarget = createEventTarget(ref.current);
665 - const upTarget = createEventTarget(container);
666 - tapAndMoveOutside({
667 - hasPointerEvents,
668 - downTarget,
669 - upTarget,
670 - pointerType,
671 - });
672 - expect(onTapCancel).toBeCalled();
673 - });
722 });
723
724 describe('preventDefault', () => {
@@ -718,18 +766,6 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
766 );
767 });
768
721 - test('allows native behaviour by default (modifier keys)', () => {
722 - ['metaKey', 'ctrlKey', 'shiftKey'].forEach(modifierKey => {
723 - const target = createEventTarget(ref.current);
724 - target.pointerdown({[modifierKey]: true});
725 - target.pointerup({[modifierKey]: true, preventDefault});
726 - expect(preventDefault).not.toBeCalled();
727 - expect(onTapEnd).toHaveBeenCalledWith(
728 - expect.objectContaining({defaultPrevented: false}),
729 - );
730 - });
731 - });
732 -
769 test('allows native behaviour if false', () => {
770 remount(false);
771
packages/react-ui/events/src/dom/shared/index.js
+1 -1
@@ -27,7 +27,7 @@ export const buttonsEnum = {
27 none: 0,
28 primary: 1,
29 secondary: 2,
30 - middle: 4,
30 + auxiliary: 4,
31 };
32
33 export function dispatchDiscreteEvent(
packages/react-ui/events/src/dom/testing-library/domEnvironment.js
+1 -1
@@ -78,7 +78,7 @@ export const buttonsType = {
78 // pen barrel button
79 secondary: 2,
80 // middle mouse
81 - middle: 4,
81 + auxiliary: 4,
82 // back mouse
83 back: 8,
84 // forward mouse