Skip to content

Commit 2559111

Browse files
authored
[react-events] Rely on 'buttons' rather than 'button' (#16479)
The semantics of 'button' on events differs between PointerEvent and MouseEvent, whereas they are the same for 'buttons'. Furthermore, 'buttons' allows developers to determine when multiple buttons are pressed as the same time. https://w3c.github.io/pointerevents/#the-button-property
1 parent c433fbb commit 2559111

File tree

11 files changed

+205
-123
lines changed

11 files changed

+205
-123
lines changed

packages/react-dom/src/events/DOMEventResponderSystem.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ function createDOMResponderEvent(
445445
passive: boolean,
446446
passiveSupported: boolean,
447447
): ReactDOMResponderEvent {
448-
const {pointerType} = (nativeEvent: any);
448+
const {buttons, pointerType} = (nativeEvent: any);
449449
let eventPointerType = '';
450450
let pointerId = null;
451451

@@ -454,7 +454,7 @@ function createDOMResponderEvent(
454454
pointerId = (nativeEvent: any).pointerId;
455455
} else if (nativeEvent.key !== undefined) {
456456
eventPointerType = 'keyboard';
457-
} else if (nativeEvent.button !== undefined) {
457+
} else if (buttons !== undefined) {
458458
eventPointerType = 'mouse';
459459
} else if ((nativeEvent: any).changedTouches !== undefined) {
460460
eventPointerType = 'touch';

packages/react-events/docs/Press.md

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,6 @@ type PressOffset = {
7878

7979
Disables all `Press` events.
8080

81-
### onContextMenu: (e: PressEvent) => void
82-
83-
Called when the context menu is shown. When a press is active, the context menu
84-
will only be shown (and the press cancelled) if `preventDefault` is `false`.
85-
8681
### onPress: (e: PressEvent) => void
8782

8883
Called immediately after a press is released, unless the press is released
@@ -115,11 +110,6 @@ down) can be moved back within the bounds of the element to reactivate it.
115110
Ensure you pass in a constant to reduce memory allocations. Default is `20` for
116111
each offset.
117112

118-
### preventContextMenu: boolean = false
119-
120-
Prevents the native context menu from being shown, but `onContextMenu`
121-
is still called.
122-
123113
### preventDefault: boolean = true
124114

125115
Whether to `preventDefault()` native events. Native behavior is prevented by

packages/react-events/src/dom/ContextMenu.js

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,20 +28,19 @@ type ContextMenuState = {
2828
};
2929

3030
type ContextMenuEvent = {|
31-
target: Element | Document,
32-
type: 'contextmenu',
33-
pointerType: PointerType,
34-
timeStamp: number,
35-
clientX: null | number,
36-
clientY: null | number,
37-
pageX: null | number,
38-
pageY: null | number,
39-
x: null | number,
40-
y: null | number,
4131
altKey: boolean,
32+
buttons: 0 | 1 | 2,
4233
ctrlKey: boolean,
4334
metaKey: boolean,
35+
pageX: null | number,
36+
pageY: null | number,
37+
pointerType: PointerType,
4438
shiftKey: boolean,
39+
target: Element | Document,
40+
timeStamp: number,
41+
type: 'contextmenu',
42+
x: null | number,
43+
y: null | number,
4544
|};
4645

4746
const hasPointerEvents =
@@ -60,13 +59,13 @@ function dispatchContextMenuEvent(
6059

6160
const gestureState = {
6261
altKey: nativeEvent.altKey,
63-
button: nativeEvent.button === 0 ? 'primary' : 'auxillary',
64-
ctrlKey: nativeEvent.altKey,
65-
metaKey: nativeEvent.altKey,
66-
pageX: nativeEvent.altKey,
67-
pageY: nativeEvent.altKey,
62+
buttons: nativeEvent.buttons != null ? nativeEvent.buttons : 0,
63+
ctrlKey: nativeEvent.ctrlKey,
64+
metaKey: nativeEvent.metaKey,
65+
pageX: nativeEvent.pageX,
66+
pageY: nativeEvent.pageY,
6867
pointerType,
69-
shiftKey: nativeEvent.altKey,
68+
shiftKey: nativeEvent.shiftKey,
7069
target,
7170
timeStamp,
7271
type: 'contextmenu',

packages/react-events/src/dom/Hover.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,16 @@ type HoverState = {
3737
type HoverEventType = 'hoverstart' | 'hoverend' | 'hoverchange' | 'hovermove';
3838

3939
type HoverEvent = {|
40-
pointerType: PointerType,
41-
target: Element | Document,
42-
type: HoverEventType,
43-
timeStamp: number,
4440
clientX: null | number,
4541
clientY: null | number,
4642
pageX: null | number,
4743
pageY: null | number,
44+
pointerType: PointerType,
4845
screenX: null | number,
4946
screenY: null | number,
47+
target: Element | Document,
48+
timeStamp: number,
49+
type: HoverEventType,
5050
x: null | number,
5151
y: null | number,
5252
|};

packages/react-events/src/dom/Press.js

Lines changed: 36 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -75,24 +75,24 @@ type PressEventType =
7575
| 'presschange';
7676

7777
type PressEvent = {|
78-
button: 'primary' | 'auxillary',
79-
defaultPrevented: boolean,
80-
target: Element | Document,
81-
type: PressEventType,
82-
pointerType: PointerType,
83-
timeStamp: number,
78+
altKey: boolean,
79+
buttons: 0 | 1 | 4,
8480
clientX: null | number,
8581
clientY: null | number,
82+
ctrlKey: boolean,
83+
defaultPrevented: boolean,
84+
metaKey: boolean,
8685
pageX: null | number,
8786
pageY: null | number,
87+
pointerType: PointerType,
8888
screenX: null | number,
8989
screenY: null | number,
90+
shiftKey: boolean,
91+
target: Element | Document,
92+
timeStamp: number,
93+
type: PressEventType,
9094
x: null | number,
9195
y: null | number,
92-
altKey: boolean,
93-
ctrlKey: boolean,
94-
metaKey: boolean,
95-
shiftKey: boolean,
9696
|};
9797

9898
const hasPointerEvents =
@@ -151,7 +151,7 @@ function createPressEvent(
151151
defaultPrevented: boolean,
152152
): PressEvent {
153153
const timeStamp = context.getTimeStamp();
154-
let button = 'primary';
154+
let buttons = 1;
155155
let clientX = null;
156156
let clientY = null;
157157
let pageX = null;
@@ -171,31 +171,36 @@ function createPressEvent(
171171
let eventObject;
172172
eventObject = (touchEvent: any) || (nativeEvent: any);
173173
if (eventObject) {
174-
({clientX, clientY, pageX, pageY, screenX, screenY} = eventObject);
175-
}
176-
if (nativeEvent.button === 1) {
177-
button = 'auxillary';
174+
({
175+
buttons,
176+
clientX,
177+
clientY,
178+
pageX,
179+
pageY,
180+
screenX,
181+
screenY,
182+
} = eventObject);
178183
}
179184
}
180185
return {
181-
button,
182-
defaultPrevented,
183-
target,
184-
type,
185-
pointerType,
186-
timeStamp,
186+
altKey,
187+
buttons,
187188
clientX,
188189
clientY,
190+
ctrlKey,
191+
defaultPrevented,
192+
metaKey,
189193
pageX,
190194
pageY,
195+
pointerType,
191196
screenX,
192197
screenY,
198+
shiftKey,
199+
target,
200+
timeStamp,
201+
type,
193202
x: clientX,
194203
y: clientY,
195-
altKey,
196-
ctrlKey,
197-
metaKey,
198-
shiftKey,
199204
};
200205
}
201206

@@ -581,11 +586,12 @@ const pressResponderImpl = {
581586
state.activePointerId = touchEvent.identifier;
582587
}
583588

584-
// Ignore any device buttons except primary/auxillary and touch/pen contact.
589+
// Ignore any device buttons except primary/secondary and touch/pen contact.
585590
// Additionally we ignore primary-button + ctrl-key with Macs as that
586591
// acts like right-click and opens the contextmenu.
587592
if (
588-
nativeEvent.button > 1 ||
593+
nativeEvent.buttons === 2 ||
594+
nativeEvent.buttons > 4 ||
589595
(isMac && isMouseEvent && nativeEvent.ctrlKey)
590596
) {
591597
return;
@@ -703,7 +709,7 @@ const pressResponderImpl = {
703709
case 'mouseup':
704710
case 'touchend': {
705711
if (isPressed) {
706-
const button = nativeEvent.button;
712+
const buttons = nativeEvent.buttons;
707713
let isKeyboardEvent = false;
708714
let touchEvent;
709715
if (type === 'pointerup' && activePointerId !== pointerId) {
@@ -722,7 +728,7 @@ const pressResponderImpl = {
722728
}
723729
isKeyboardEvent = true;
724730
removeRootEventTypes(context, state);
725-
} else if (button === 1) {
731+
} else if (buttons === 4) {
726732
// Remove the root events here as no 'click' event is dispatched when this 'button' is pressed.
727733
removeRootEventTypes(context, state);
728734
}
@@ -780,7 +786,7 @@ const pressResponderImpl = {
780786
}
781787
}
782788

783-
if (state.isPressWithinResponderRegion && button !== 1) {
789+
if (state.isPressWithinResponderRegion && buttons !== 4) {
784790
dispatchEvent(
785791
event,
786792
onPress,

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

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@
99

1010
'use strict';
1111

12-
import {createEventTarget, platform, setPointerEvent} from '../testing-library';
12+
import {
13+
buttonsType,
14+
createEventTarget,
15+
platform,
16+
setPointerEvent,
17+
} from '../testing-library';
1318

1419
let React;
1520
let ReactFeatureFlags;
@@ -61,7 +66,11 @@ describe.each(table)('ContextMenu responder', hasPointerEvents => {
6166
expect(preventDefault).toHaveBeenCalledTimes(1);
6267
expect(onContextMenu).toHaveBeenCalledTimes(1);
6368
expect(onContextMenu).toHaveBeenCalledWith(
64-
expect.objectContaining({pointerType: 'mouse', type: 'contextmenu'}),
69+
expect.objectContaining({
70+
buttons: buttonsType.secondary,
71+
pointerType: 'mouse',
72+
type: 'contextmenu',
73+
}),
6574
);
6675
});
6776

@@ -80,7 +89,11 @@ describe.each(table)('ContextMenu responder', hasPointerEvents => {
8089
expect(preventDefault).toHaveBeenCalledTimes(1);
8190
expect(onContextMenu).toHaveBeenCalledTimes(1);
8291
expect(onContextMenu).toHaveBeenCalledWith(
83-
expect.objectContaining({pointerType: 'touch', type: 'contextmenu'}),
92+
expect.objectContaining({
93+
buttons: buttonsType.none,
94+
pointerType: 'touch',
95+
type: 'contextmenu',
96+
}),
8497
);
8598
});
8699

@@ -144,7 +157,11 @@ describe.each(table)('ContextMenu responder', hasPointerEvents => {
144157
target.contextmenu({}, {modified: true});
145158
expect(onContextMenu).toHaveBeenCalledTimes(1);
146159
expect(onContextMenu).toHaveBeenCalledWith(
147-
expect.objectContaining({pointerType: 'mouse', type: 'contextmenu'}),
160+
expect.objectContaining({
161+
buttons: buttonsType.primary,
162+
pointerType: 'mouse',
163+
type: 'contextmenu',
164+
}),
148165
);
149166
});
150167
});

0 commit comments

Comments
 (0)