@samitouri / QOS-React-2 / commits / 9e937e7e86

[react-interactions] fix Press/Tap behavior for virtual middle clicks (#17554)

Tools like BetterTouchTool for macOS trigger middle-clicks with a 'buttons' value that doesn't correspond to the middle-mouse button. To account for this we also inspect the value of 'button'. Close #17367

Nicolas Gallagher committed Dec 9, 2019 at 13:04 UTC 9e937e7e86be4af829bd7be2045bf3601928c9b5
9 files changed +292 -65
packages/react-interactions/events/src/dom/PressLegacy.js
+3
@@ -626,6 +626,9 @@ const pressResponderImpl = {
626 state.responderRegionOnDeactivation = null;
627 state.isPressWithinResponderRegion = true;
628 state.buttons = nativeEvent.buttons;
629 + if (nativeEvent.button === 1) {
630 + state.buttons = 4;
631 + }
632 dispatchPressStartEvents(event, context, props, state);
633 addRootEventTypes(context, state);
634 } else {
packages/react-interactions/events/src/dom/Tap.js
+6 -2
@@ -520,10 +520,14 @@ const responderImpl = {
520 }
521
522 const activate = shouldActivate(event);
523 - const activateAuxiliary = isAuxiliary(nativeEvent.buttons, event);
523 + const buttons =
524 + nativeEvent.button === 1
525 + ? buttonsEnum.auxiliary
526 + : nativeEvent.buttons;
527 + const activateAuxiliary = isAuxiliary(buttons, event);
528
529 if (activate || activateAuxiliary) {
526 - state.buttons = nativeEvent.buttons;
530 + state.buttons = buttons;
531 state.pointerType = event.pointerType;
532 state.responderTarget = context.getResponderNode();
533 addRootEventTypes(rootEventTypes, context, state);
packages/react-interactions/events/src/dom/__tests__/Press-test.internal.js
+56 -1
@@ -10,6 +10,7 @@
10 'use strict';
11
12 import {
13 + buttonType,
14 buttonsType,
15 createEventTarget,
16 describeWithPointerEvent,
@@ -126,7 +127,30 @@ describeWithPointerEvent('Press responder', hasPointerEvents => {
127 it('is called after middle-button pointer down', () => {
128 const target = createEventTarget(ref.current);
129 const pointerType = 'mouse';
129 - target.pointerdown({buttons: buttonsType.auxiliary, pointerType});
130 + target.pointerdown({
131 + button: buttonType.auxiliary,
132 + buttons: buttonsType.auxiliary,
133 + pointerType,
134 + });
135 + target.pointerup({pointerType});
136 + expect(onPressStart).toHaveBeenCalledTimes(1);
137 + expect(onPressStart).toHaveBeenCalledWith(
138 + expect.objectContaining({
139 + buttons: buttonsType.auxiliary,
140 + pointerType: 'mouse',
141 + type: 'pressstart',
142 + }),
143 + );
144 + });
145 +
146 + it('is called after virtual middle-button pointer down', () => {
147 + const target = createEventTarget(ref.current);
148 + const pointerType = 'mouse';
149 + target.pointerdown({
150 + button: buttonType.auxiliary,
151 + buttons: 0,
152 + pointerType,
153 + });
154 target.pointerup({pointerType});
155 expect(onPressStart).toHaveBeenCalledTimes(1);
156 expect(onPressStart).toHaveBeenCalledWith(
@@ -212,6 +236,7 @@ describeWithPointerEvent('Press responder', hasPointerEvents => {
236 it('is called after middle-button pointer up', () => {
237 const target = createEventTarget(ref.current);
238 target.pointerdown({
239 + button: buttonType.auxiliary,
240 buttons: buttonsType.auxiliary,
241 pointerType: 'mouse',
242 });
@@ -226,6 +251,24 @@ describeWithPointerEvent('Press responder', hasPointerEvents => {
251 );
252 });
253
254 + it('is called after virtual middle-button pointer up', () => {
255 + const target = createEventTarget(ref.current);
256 + target.pointerdown({
257 + button: buttonType.auxiliary,
258 + buttons: 0,
259 + pointerType: 'mouse',
260 + });
261 + target.pointerup({pointerType: 'mouse'});
262 + expect(onPressEnd).toHaveBeenCalledTimes(1);
263 + expect(onPressEnd).toHaveBeenCalledWith(
264 + expect.objectContaining({
265 + buttons: buttonsType.auxiliary,
266 + pointerType: 'mouse',
267 + type: 'pressend',
268 + }),
269 + );
270 + });
271 +
272 it('is called after "keyup" event for Enter', () => {
273 const target = createEventTarget(ref.current);
274 target.keydown({key: 'Enter'});
@@ -356,6 +399,7 @@ describeWithPointerEvent('Press responder', hasPointerEvents => {
399 it('is not called after middle-button press', () => {
400 const target = createEventTarget(ref.current);
401 target.pointerdown({
402 + button: buttonType.auxiliary,
403 buttons: buttonsType.auxiliary,
404 pointerType: 'mouse',
405 });
@@ -363,6 +407,17 @@ describeWithPointerEvent('Press responder', hasPointerEvents => {
407 expect(onPress).not.toHaveBeenCalled();
408 });
409
410 + it('is not called after virtual middle-button press', () => {
411 + const target = createEventTarget(ref.current);
412 + target.pointerdown({
413 + button: buttonType.auxiliary,
414 + buttons: 0,
415 + pointerType: 'mouse',
416 + });
417 + target.pointerup({pointerType: 'mouse'});
418 + expect(onPress).not.toHaveBeenCalled();
419 + });
420 +
421 it('is called after valid "keyup" event', () => {
422 const target = createEventTarget(ref.current);
423 target.keydown({key: 'Enter'});
packages/react-interactions/events/src/dom/__tests__/PressLegacy-test.internal.js
+14
@@ -10,6 +10,7 @@
10 'use strict';
11
12 import {
13 + buttonType,
14 buttonsType,
15 createEventTarget,
16 setPointerEvent,
@@ -122,6 +123,7 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
123 it('is called after middle-button pointer down', () => {
124 const target = createEventTarget(ref.current);
125 target.pointerdown({
126 + button: buttonType.auxiliary,
127 buttons: buttonsType.auxiliary,
128 pointerType: 'mouse',
129 });
@@ -140,6 +142,7 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
142 const target = createEventTarget(node);
143 target.setBoundingClientRect({x: 0, y: 0, width: 100, height: 100});
144 target.pointerdown({
145 + button: buttonType.auxiliary,
146 buttons: buttonsType.auxiliary,
147 pointerType: 'mouse',
148 });
@@ -374,6 +377,17 @@ describe.each(environmentTable)('Press responder', hasPointerEvents => {
377 expect(onPress).not.toHaveBeenCalled();
378 });
379
380 + it('is not called after virtual middle-button press', () => {
381 + const target = createEventTarget(ref.current);
382 + target.pointerdown({
383 + button: buttonType.auxiliary,
384 + buttons: 0,
385 + pointerType: 'mouse',
386 + });
387 + target.pointerup({pointerType: 'mouse'});
388 + expect(onPress).not.toHaveBeenCalled();
389 + });
390 +
391 it('is called after valid "keyup" event', () => {
392 const target = createEventTarget(ref.current);
393 target.keydown({key: 'Enter'});
packages/react-interactions/events/src/dom/__tests__/Tap-test.internal.js
+127 -47
@@ -10,6 +10,7 @@
10 'use strict';
11
12 import {
13 + buttonType,
14 buttonsType,
15 createEventTarget,
16 describeWithPointerEvent,
@@ -209,19 +210,21 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
210
211 test('auxiliary-button pointer up', () => {
212 const pointerType = 'mouse';
213 + const button = buttonType.auxiliary;
214 const buttons = buttonsType.auxiliary;
215 const target = createEventTarget(ref.current);
214 - target.pointerdown({buttons, pointerType});
215 - target.pointerup({buttons, pointerType});
216 + target.pointerdown({button, buttons, pointerType});
217 + target.pointerup({button, buttons, pointerType});
218 expect(onAuxiliaryTap).toHaveBeenCalledTimes(1);
219 });
220
221 test('modifier-button pointer up', () => {
222 const pointerType = 'mouse';
223 + const button = buttonType.primary;
224 const buttons = buttonsType.primary;
225 const target = createEventTarget(ref.current);
223 - target.pointerdown({buttons, pointerType});
224 - target.pointerup({buttons, metaKey: true, pointerType});
226 + target.pointerdown({button, buttons, pointerType});
227 + target.pointerup({button, buttons, metaKey: true, pointerType});
228 expect(onAuxiliaryTap).toHaveBeenCalledTimes(1);
229 });
230 });
@@ -243,6 +246,7 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
246 testWithPointerType('pointer down', pointerType => {
247 const target = createEventTarget(ref.current);
248 const nativeEvent = {
249 + button: buttonType.primary,
250 buttons: buttonsType.primary,
251 pageX: 10,
252 pageY: 10,
@@ -287,11 +291,12 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
291 test('second pointer on target', () => {
292 const pointerType = 'touch';
293 const target = createEventTarget(ref.current);
294 + const button = buttonType.primary;
295 const buttons = buttonsType.primary;
291 - target.pointerdown({buttons, pointerId: 1, pointerType});
296 + target.pointerdown({button, buttons, pointerId: 1, pointerType});
297 expect(onTapStart).toHaveBeenCalledTimes(1);
298 if (hasPointerEvents) {
294 - target.pointerdown({buttons, pointerId: 2, pointerType});
299 + target.pointerdown({button, buttons, pointerId: 2, pointerType});
300 } else {
301 // TouchEvents
302 target.pointerdown([{pointerId: 1}, {pointerId: 2}]);
@@ -301,29 +306,67 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
306
307 testWithPointerType('ignored buttons and modifiers', pointerType => {
308 const target = createEventTarget(ref.current);
304 - const {auxiliary, eraser, primary, secondary} = buttonsType;
309 if (pointerType !== 'touch') {
310 // right-click
307 - target.pointerdown({buttons: secondary, pointerType});
311 + target.pointerdown({
312 + button: buttonType.secondary,
313 + buttons: buttonsType.secondary,
314 + pointerType,
315 + });
316 target.pointerup();
317 // middle-click
310 - target.pointerdown({buttons: auxiliary, pointerType});
318 + target.pointerdown({
319 + button: buttonType.auxiliary,
320 + buttons: buttonsType.auxiliary,
321 + pointerType,
322 + });
323 + target.pointerup();
324 + // virtual middle-click with misleading 'buttons' value
325 + target.pointerdown({
326 + button: buttonType.auxiliary,
327 + buttons: 0,
328 + pointerType,
329 + });
330 target.pointerup();
331 // pen eraser
313 - target.pointerdown({buttons: eraser, pointerType});
332 + target.pointerdown({
333 + button: buttonType.eraser,
334 + buttons: buttonsType.eraser,
335 + pointerType,
336 + });
337 target.pointerup();
338 }
339 // alt-click
317 - target.pointerdown({buttons: primary, altKey: true, pointerType});
340 + target.pointerdown({
341 + button: buttonType.primary,
342 + buttons: buttonsType.primary,
343 + altKey: true,
344 + pointerType,
345 + });
346 target.pointerup();
347 // ctrl-click
320 - target.pointerdown({buttons: primary, ctrlKey: true, pointerType});
348 + target.pointerdown({
349 + button: buttonType.primary,
350 + buttons: buttonsType.primary,
351 + ctrlKey: true,
352 + pointerType,
353 + });
354 target.pointerup();
355 // meta-click
323 - target.pointerdown({buttons: primary, metaKey: true, pointerType});
356 + target.pointerdown({
357 + button: buttonType.primary,
358 + buttons: buttonsType.primary,
359 + metaKey: true,
360 + pointerType,
361 + });
362 target.pointerup();
363 // shift-click
326 - target.pointerdown({buttons: primary, shiftKey: true, pointerType});
364 + target.pointerdown({
365 + button: buttonType.primary,
366 + buttons: buttonsType.primary,
367 + shiftKey: true,
368 + pointerType,
369 + });
370 target.pointerup();
371
372 expect(onTapStart).toHaveBeenCalledTimes(0);
@@ -346,9 +389,11 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
389
390 testWithPointerType('pointer up', pointerType => {
391 const target = createEventTarget(ref.current);
392 + const button = buttonType.primary;
393 const buttons = buttonsType.primary;
350 - target.pointerdown({buttons, pointerType});
394 + target.pointerdown({button, buttons, pointerType});
395 target.pointerup({
396 + button,
397 buttons,
398 pageX: 10,
399 pageY: 10,
@@ -424,11 +469,13 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
469 const pointerType = 'touch';
470 const target = createEventTarget(ref.current);
471 const offTarget = createEventTarget(container);
472 + const button = buttonType.primary;
473 const buttons = buttonsType.primary;
474
429 - target.pointerdown({buttons, pointerId: 1, pointerType});
430 - offTarget.pointerdown({buttons, pointerId: 2, pointerType});
475 + target.pointerdown({button, buttons, pointerId: 1, pointerType});
476 + offTarget.pointerdown({button, buttons, pointerId: 2, pointerType});
477 offTarget.pointerup({
478 + button,
479 buttons,
480 pageX: 10,
481 pageY: 10,
@@ -443,28 +490,57 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
490
491 test('ignored buttons and modifiers', () => {
492 const target = createEventTarget(ref.current);
446 - const primary = buttonsType.primary;
493 // right-click
448 - target.pointerdown({buttons: buttonsType.secondary});
449 - target.pointerup({buttons: buttonsType.secondary});
494 + target.pointerdown({
495 + button: buttonType.secondary,
496 + buttons: buttonsType.secondary,
497 + });
498 + target.pointerup({
499 + button: buttonType.secondary,
500 + buttons: buttonsType.secondary,
501 + });
502 // middle-click
451 - target.pointerdown({buttons: buttonsType.auxiliary});
452 - target.pointerup({buttons: buttonsType.auxiliary});
503 + target.pointerdown({
504 + button: buttonType.auxiliary,
505 + buttons: buttonsType.auxiliary,
506 + });
507 + target.pointerup({
508 + button: buttonType.auxiliary,
509 + buttons: buttonsType.auxiliary,
510 + });
511 // pen eraser
454 - target.pointerdown({buttons: buttonsType.eraser});
455 - target.pointerup({buttons: buttonsType.eraser});
512 + target.pointerdown({
513 + button: buttonType.eraser,
514 + buttons: buttonsType.eraser,
515 + });
516 + target.pointerup({
517 + button: buttonType.eraser,
518 + buttons: buttonsType.eraser,
519 + });
520 // alt-click
457 - target.pointerdown({buttons: primary});
458 - target.pointerup({altKey: true});
521 + target.pointerdown({
522 + button: buttonType.primary,
523 + buttons: buttonsType.primary,
524 + });
525 + target.pointerup({altKey: true, button: buttonType.primary});
526 // ctrl-click
460 - target.pointerdown({buttons: primary});
461 - target.pointerup({ctrlKey: true});
527 + target.pointerdown({
528 + button: buttonType.primary,
529 + buttons: buttonsType.primary,
530 + });
531 + target.pointerup({ctrlKey: true, button: buttonType.primary});
532 // meta-click
463 - target.pointerdown({buttons: primary});
464 - target.pointerup({metaKey: true});
533 + target.pointerdown({
534 + button: buttonType.primary,
535 + buttons: buttonsType.primary,
536 + });
537 + target.pointerup({metaKey: true, button: buttonType.primary});
538 // shift-click
466 - target.pointerdown({buttons: primary});
467 - target.pointerup({shiftKey: true});
539 + target.pointerdown({
540 + button: buttonType.primary,
541 + buttons: buttonsType.primary,
542 + });
543 + target.pointerup({shiftKey: true, button: buttonType.primary});
544
545 expect(onTapEnd).toHaveBeenCalledTimes(0);
546 });
@@ -560,9 +636,10 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
636 const pointerType = 'touch';
637 const target = createEventTarget(ref.current);
638 const offTarget = createEventTarget(container);
639 + const button = buttonType.primary;
640 const buttons = buttonsType.primary;
564 - target.pointerdown({buttons, pointerId: 1, pointerType});
565 - offTarget.pointerdown({buttons, pointerId: 2, pointerType});
641 + target.pointerdown({button, buttons, pointerId: 1, pointerType});
642 + offTarget.pointerdown({button, buttons, pointerId: 2, pointerType});
643 target.pointermove({pointerId: 1, pointerType, x: 10, y: 10});
644 expect(onTapUpdate).toHaveBeenCalledTimes(1);
645 offTarget.pointermove({pointerId: 2, pointerType, x: 10, y: 10});
@@ -691,10 +768,11 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
768 test('second pointer on target', () => {
769 const pointerType = 'touch';
770 const target = createEventTarget(ref.current);
771 + const button = buttonType.primary;
772 const buttons = buttonsType.primary;
695 - target.pointerdown({buttons, pointerId: 1, pointerType});
773 + target.pointerdown({button, buttons, pointerId: 1, pointerType});
774 if (hasPointerEvents) {
697 - target.pointerdown({buttons, pointerId: 2, pointerType});
775 + target.pointerdown({button, buttons, pointerId: 2, pointerType});
776 } else {
777 // TouchEvents
778 target.pointerdown([{pointerId: 1}, {pointerId: 2}]);
@@ -707,9 +785,10 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
785 const pointerType = 'touch';
786 const target = createEventTarget(ref.current);
787 const offTarget = createEventTarget(container);
788 + const button = buttonType.primary;
789 const buttons = buttonsType.primary;
711 - target.pointerdown({buttons, pointerId: 1, pointerType});
712 - offTarget.pointerdown({buttons, pointerId: 2, pointerType});
790 + target.pointerdown({button, buttons, pointerId: 1, pointerType});
791 + offTarget.pointerdown({button, buttons, pointerId: 2, pointerType});
792 expect(onTapCancel).toHaveBeenCalledTimes(0);
793 });
794 }
@@ -728,19 +807,20 @@ describeWithPointerEvent('Tap responder', hasPointerEvents => {
807
808 test('ignored modifiers', () => {
809 const target = createEventTarget(ref.current);
731 - const primary = buttonsType.primary;
810 + const button = buttonType.primary;
811 + const buttons = buttonsType.primary;
812 // alt-click
733 - target.pointerdown({buttons: primary});
734 - target.pointerup({altKey: true});
813 + target.pointerdown({button, buttons});
814 + target.pointerup({altKey: true, button});
815 // ctrl-click
736 - target.pointerdown({buttons: primary});
737 - target.pointerup({ctrlKey: true});
816 + target.pointerdown({button, buttons});
817 + target.pointerup({ctrlKey: true, button});
818 // meta-click
739 - target.pointerdown({buttons: primary});
740 - target.pointerup({metaKey: true});
819 + target.pointerdown({button, buttons});
820 + target.pointerup({metaKey: true, button});
821 // shift-click
742 - target.pointerdown({buttons: primary});
743 - target.pointerup({shiftKey: true});
822 + target.pointerdown({buttons});
823 + target.pointerup({shiftKey: true, button});
824
825 expect(onTapCancel).toHaveBeenCalledTimes(4);
826 });
packages/react-interactions/events/src/dom/testing-library/domEnvironment.js
+28
@@ -64,6 +64,34 @@ export const platform = {
64 },
65 };
66
67 +/**
68 + * Button property
69 + * This property only guarantees to indicate which buttons are pressed during events caused by pressing or
70 + * releasing one or multiple buttons. As such, it is not reliable for events such as 'mouseenter', 'mouseleave',
71 + * 'mouseover', 'mouseout' or 'mousemove'. Furthermore, the semantics differ for PointerEvent, where the value
72 + * for 'pointermove' will always be -1.
73 + */
74 +
75 +export const buttonType = {
76 + // no change since last event
77 + none: -1,
78 + // left-mouse
79 + // touch contact
80 + // pen contact
81 + primary: 0,
82 + // right-mouse
83 + // pen barrel button
84 + secondary: 2,
85 + // middle mouse
86 + auxiliary: 1,
87 + // back mouse
88 + back: 3,
89 + // forward mouse
90 + forward: 4,
91 + // pen eraser
92 + eraser: 5,
93 +};
94 +
95 /**
96 * Buttons bitmask
97 */
packages/react-interactions/events/src/dom/testing-library/domEventSequences.js
+33 -10
@@ -10,7 +10,12 @@
10 'use strict';
11
12 import * as domEvents from './domEvents';
13 -import {buttonsType, hasPointerEvent, platform} from './domEnvironment';
13 +import {
14 + buttonType,
15 + buttonsType,
16 + hasPointerEvent,
17 + platform,
18 +} from './domEnvironment';
19
20 function emptyFunction() {}
21
@@ -33,31 +38,45 @@ export function contextmenu(
38 if (pointerType === 'touch') {
39 if (hasPointerEvent()) {
40 dispatch(
36 - domEvents.pointerdown({buttons: buttonsType.primary, pointerType}),
41 + domEvents.pointerdown({
42 + button: buttonType.primary,
43 + buttons: buttonsType.primary,
44 + pointerType,
45 + }),
46 );
47 }
48 dispatch(domEvents.touchstart());
49 dispatch(
41 - domEvents.contextmenu({buttons: buttonsType.none, preventDefault}),
50 + domEvents.contextmenu({
51 + button: buttonType.primary,
52 + buttons: buttonsType.none,
53 + preventDefault,
54 + }),
55 );
56 } else if (pointerType === 'mouse') {
57 if (modified === true) {
58 + const button = buttonType.primary;
59 const buttons = buttonsType.primary;
60 const ctrlKey = true;
61 if (hasPointerEvent()) {
48 - dispatch(domEvents.pointerdown({buttons, ctrlKey, pointerType}));
62 + dispatch(
63 + domEvents.pointerdown({button, buttons, ctrlKey, pointerType}),
64 + );
65 }
50 - dispatch(domEvents.mousedown({buttons, ctrlKey}));
66 + dispatch(domEvents.mousedown({button, buttons, ctrlKey}));
67 if (platform.get() === 'mac') {
52 - dispatch(domEvents.contextmenu({buttons, ctrlKey, preventDefault}));
68 + dispatch(
69 + domEvents.contextmenu({button, buttons, ctrlKey, preventDefault}),
70 + );
71 }
72 } else {
73 + const button = buttonType.secondary;
74 const buttons = buttonsType.secondary;
75 if (hasPointerEvent()) {
57 - dispatch(domEvents.pointerdown({buttons, pointerType}));
76 + dispatch(domEvents.pointerdown({button, buttons, pointerType}));
77 }
59 - dispatch(domEvents.mousedown({buttons}));
60 - dispatch(domEvents.contextmenu({buttons, preventDefault}));
78 + dispatch(domEvents.mousedown({button, buttons}));
79 + dispatch(domEvents.contextmenu({button, buttons, preventDefault}));
80 }
81 }
82 }
@@ -84,7 +103,11 @@ export function pointerdown(target, defaultPayload) {
103 // Arrays are for multi-touch only
104 dispatch(domEvents.touchstart(defaultPayload));
105 } else {
87 - const payload = {buttons: buttonsType.primary, ...defaultPayload};
106 + const payload = {
107 + button: buttonType.primary,
108 + buttons: buttonsType.primary,
109 + ...defaultPayload,
110 + };
111 if (pointerType === 'mouse') {
112 if (hasPointerEvent()) {
113 dispatch(domEvents.pointerover(payload));
packages/react-interactions/events/src/dom/testing-library/domEvents.js
+23 -5
@@ -9,7 +9,7 @@
9
10 'use strict';
11
12 -import {buttonsType} from './domEnvironment';
12 +import {buttonType, buttonsType} from './domEnvironment';
13
14 /**
15 * Native event object mocks for higher-level events.
@@ -65,6 +65,7 @@ function createPointerEvent(
65 type,
66 {
67 altKey = false,
68 + button = buttonType.none,
69 buttons = buttonsType.none,
70 ctrlKey = false,
71 detail = 1,
@@ -98,6 +99,7 @@ function createPointerEvent(
99
100 return createEvent(type, {
101 altKey,
102 + button,
103 buttons,
104 clientX: x,
105 clientY: y,
@@ -164,6 +166,7 @@ function createMouseEvent(
166 type,
167 {
168 altKey = false,
169 + button = buttonType.none,
170 buttons = buttonsType.none,
171 ctrlKey = false,
172 detail = 1,
@@ -187,6 +190,7 @@ function createMouseEvent(
190
191 return createEvent(type, {
192 altKey,
193 + button,
194 buttons,
195 clientX: x,
196 clientY: y,
@@ -297,7 +301,10 @@ export function blur({relatedTarget} = {}) {
301 }
302
303 export function click(payload) {
300 - return createMouseEvent('click', payload);
304 + return createMouseEvent('click', {
305 + button: buttonType.primary,
306 + ...payload,
307 + });
308 }
309
310 export function contextmenu(payload) {
@@ -383,6 +390,7 @@ export function pointercancel(payload) {
390 export function pointerdown(payload) {
391 const isTouch = payload != null && payload.pointerType === 'touch';
392 return createPointerEvent('pointerdown', {
393 + button: buttonType.primary,
394 buttons: buttonsType.primary,
395 pressure: isTouch ? 1 : 0.5,
396 ...payload,
@@ -398,7 +406,10 @@ export function pointerleave(payload) {
406 }
407
408 export function pointermove(payload) {
401 - return createPointerEvent('pointermove', payload);
409 + return createPointerEvent('pointermove', {
410 + ...payload,
411 + button: buttonType.none,
412 + });
413 }
414
415 export function pointerout(payload) {
@@ -411,6 +422,7 @@ export function pointerover(payload) {
422
423 export function pointerup(payload) {
424 return createPointerEvent('pointerup', {
425 + button: buttonType.primary,
426 ...payload,
427 buttons: buttonsType.none,
428 pressure: 0,
@@ -422,13 +434,18 @@ export function pointerup(payload) {
434 */
435
436 export function mousedown(payload) {
425 - // The value of 'buttons' for 'mousedown' must not be 0
437 + // The value of 'button' and 'buttons' for 'mousedown' must not be none.
438 + const button =
439 + payload == null || payload.button === buttonType.none
440 + ? buttonType.primary
441 + : payload.button;
442 const buttons =
427 - payload == null || payload.buttons === 0
443 + payload == null || payload.buttons === buttonsType.none
444 ? buttonsType.primary
445 : payload.buttons;
446 return createMouseEvent('mousedown', {
447 ...payload,
448 + button,
449 buttons,
450 });
451 }
@@ -455,6 +472,7 @@ export function mouseover(payload) {
472
473 export function mouseup(payload) {
474 return createMouseEvent('mouseup', {
475 + button: buttonType.primary,
476 ...payload,
477 buttons: buttonsType.none,
478 });
packages/react-interactions/events/src/dom/testing-library/index.js
+2
@@ -12,6 +12,7 @@
12 import * as domEvents from './domEvents';
13 import * as domEventSequences from './domEventSequences';
14 import {
15 + buttonType,
16 buttonsType,
17 hasPointerEvent,
18 setPointerEvent,
@@ -160,6 +161,7 @@ function testWithPointerType(message, testFn) {
161 }
162
163 export {
164 + buttonType,
165 buttonsType,
166 createEventTarget,
167 describeWithPointerEvent,