diff --git a/packages/react-events/docs/Press.md b/packages/react-events/docs/Press.md index cc7881342ca3..71d269b3c34e 100644 --- a/packages/react-events/docs/Press.md +++ b/packages/react-events/docs/Press.md @@ -1,11 +1,11 @@ # Press The `Press` module responds to press events on the element it wraps. Press -events are dispatched for `mouse`, `pen`, `touch`, and `keyboard` pointer types. -Press events are only dispatched for keyboards when pressing the Enter or -Spacebar keys. If neither `onPress` nor `onLongPress` are called, this signifies -that the press ended outside of the element hit bounds (i.e., the user aborted -the press). +events are dispatched for `mouse`, `pen`, `touch`, `trackpad`, and `keyboard` +pointer types. Press events are only dispatched for keyboards when pressing the +Enter or Spacebar keys. If neither `onPress` nor `onLongPress` are called, this +signifies that the press ended outside of the element hit bounds (i.e., the user +aborted the press). Press events do not propagate between `Press` event responders. @@ -37,9 +37,9 @@ const Button = (props) => ( ```js type PressEvent = { - pointerType: 'mouse' | 'touch' | 'pen' | 'keyboard', + pointerType: 'mouse' | 'touch' | 'pen' | 'trackpad' | 'keyboard', target: Element, - type: 'press' | 'pressstart' | 'pressend' | 'presschange' | 'pressmove' | 'longpress' | 'longpresschange' + type: 'press' | 'pressstart' | 'pressend' | 'presschange' | 'pressmove' | 'longpress' | 'longpresschange' | 'contextmenu' } type PressOffset = { @@ -71,6 +71,16 @@ released before the threshold is exceeded. Disables all `Press` events. +### disableContextMenu: boolean = false + +Disables the native context menu so that it is never shown and `onContextMenu` +is never called. + +### onContextMenu: (e: PressEvent) => void + +Called when the context menu is shown. When a press is active, the context menu +will only be shown (and the press cancelled) if `preventDefault` is `false`. + ### onLongPress: (e: PressEvent) => void Called once the element has been pressed for the length of `delayLongPress`. If diff --git a/packages/react-events/src/Press.js b/packages/react-events/src/Press.js index 3696793b76be..fbf16f2ed66c 100644 --- a/packages/react-events/src/Press.js +++ b/packages/react-events/src/Press.js @@ -19,6 +19,7 @@ import {DiscreteEvent, UserBlockingEvent} from 'shared/ReactTypes'; type PressProps = { disabled: boolean, + disableContextMenu: boolean, delayLongPress: number, delayPressEnd: number, delayPressStart: number, @@ -727,6 +728,12 @@ const PressResponder = { } case 'contextmenu': { + if (props.disableContextMenu) { + // Skip dispatching of onContextMenu below + nativeEvent.preventDefault(); + return; + } + if (isPressed) { if (props.preventDefault !== false) { // Skip dispatching of onContextMenu below @@ -735,6 +742,7 @@ const PressResponder = { } dispatchCancel(event, context, props, state); } + if (props.onContextMenu) { dispatchEvent( event, diff --git a/packages/react-events/src/__tests__/Press-test.internal.js b/packages/react-events/src/__tests__/Press-test.internal.js index 900bb3b36e41..b2c49c2b6f8e 100644 --- a/packages/react-events/src/__tests__/Press-test.internal.js +++ b/packages/react-events/src/__tests__/Press-test.internal.js @@ -2838,6 +2838,22 @@ describe('Event responder: Press', () => { ref.current.dispatchEvent(createEvent('contextmenu')); expect(onContextMenu).toHaveBeenCalledTimes(0); }); + + it('is not called if "disableContextMenu" is true', () => { + const onContextMenu = jest.fn(); + const ref = React.createRef(); + const element = ( + +
+ + ); + ReactDOM.render(element, container); + ref.current.dispatchEvent( + createEvent('pointerdown', {pointerType: 'mouse', button: 2}), + ); + ref.current.dispatchEvent(createEvent('contextmenu')); + expect(onContextMenu).toHaveBeenCalledTimes(0); + }); }); it('should work correctly with stopPropagation set to true', () => {