mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[Flare] Press events include defaultPrevented (#15916)
* Rename `disableContextMenu` to `preventContextMenu` * Change the behaviour of `preventContextMenu` so that `onContextMenu` is still called when the native context menu is prevented.
This commit is contained in:
@@ -171,14 +171,6 @@ const eventResponderContext: ReactDOMResponderContext = {
|
||||
}
|
||||
},
|
||||
});
|
||||
// $FlowFixMe: we don't need value, Flow thinks we do
|
||||
Object.defineProperty(possibleEventObject, 'defaultPrevented', {
|
||||
get() {
|
||||
if (__DEV__) {
|
||||
showWarning('defaultPrevented');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const eventObject = ((possibleEventObject: any): $Shape<
|
||||
PartialEventObject,
|
||||
|
||||
@@ -925,19 +925,6 @@ describe('DOMEventResponderSystem', () => {
|
||||
' Try wrapping in a conditional, i.e. `if (event.type !== "press") { event.nativeEvent }`',
|
||||
{withoutStack: true},
|
||||
);
|
||||
expect(() => {
|
||||
handler = event => {
|
||||
return event.defaultPrevented;
|
||||
};
|
||||
ReactDOM.render(<Test />, container);
|
||||
dispatchClickEvent(document.body);
|
||||
}).toWarnDev(
|
||||
'Warning: defaultPrevented is not available on event objects created from event responder modules ' +
|
||||
'(React Flare).' +
|
||||
' Try wrapping in a conditional, i.e. `if (event.type !== "press") { event.defaultPrevented }`',
|
||||
{withoutStack: true},
|
||||
);
|
||||
|
||||
expect(container.innerHTML).toBe('<button>Click me!</button>');
|
||||
});
|
||||
|
||||
|
||||
@@ -37,9 +37,34 @@ const Button = (props) => (
|
||||
|
||||
```js
|
||||
type PressEvent = {
|
||||
pointerType: 'mouse' | 'touch' | 'pen' | 'trackpad' | 'keyboard',
|
||||
altKey: boolean,
|
||||
ctrlKey: boolean,
|
||||
defaultPrevented: boolean,
|
||||
metaKey: boolean,
|
||||
pageX: number,
|
||||
pageY: number,
|
||||
pointerType:
|
||||
| 'mouse'
|
||||
| 'touch'
|
||||
| 'pen'
|
||||
| 'trackpad'
|
||||
| 'keyboard',
|
||||
screenX: number,
|
||||
screenY: number,
|
||||
shiftKey: boolean,
|
||||
target: Element,
|
||||
type: 'press' | 'pressstart' | 'pressend' | 'presschange' | 'pressmove' | 'longpress' | 'longpresschange' | 'contextmenu'
|
||||
timeStamp: number,
|
||||
type:
|
||||
| 'press'
|
||||
| 'pressstart'
|
||||
| 'pressend'
|
||||
| 'presschange'
|
||||
| 'pressmove'
|
||||
| 'longpress'
|
||||
| 'longpresschange'
|
||||
| 'contextmenu',
|
||||
x: number,
|
||||
y: number
|
||||
}
|
||||
|
||||
type PressOffset = {
|
||||
@@ -71,11 +96,6 @@ 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
|
||||
@@ -135,6 +155,11 @@ down) can be moved back within the bounds of the element to reactivate it.
|
||||
Ensure you pass in a constant to reduce memory allocations. Default is `20` for
|
||||
each offset.
|
||||
|
||||
### preventContextMenu: boolean = false
|
||||
|
||||
Prevents the native context menu from being shown, but `onContextMenu`
|
||||
is still called.
|
||||
|
||||
### preventDefault: boolean = true
|
||||
|
||||
Whether to `preventDefault()` native events. Native behavior is prevented by
|
||||
|
||||
Vendored
+39
-25
@@ -19,7 +19,6 @@ import {DiscreteEvent, UserBlockingEvent} from 'shared/ReactTypes';
|
||||
|
||||
type PressProps = {
|
||||
disabled: boolean,
|
||||
disableContextMenu: boolean,
|
||||
delayLongPress: number,
|
||||
delayPressEnd: number,
|
||||
delayPressStart: number,
|
||||
@@ -38,6 +37,7 @@ type PressProps = {
|
||||
bottom: number,
|
||||
left: number,
|
||||
},
|
||||
preventContextMenu: boolean,
|
||||
preventDefault: boolean,
|
||||
stopPropagation: boolean,
|
||||
};
|
||||
@@ -72,6 +72,7 @@ type PressState = {
|
||||
|}>,
|
||||
ignoreEmulatedMouseEvents: boolean,
|
||||
activePointerId: null | number,
|
||||
shouldPreventClick: boolean,
|
||||
};
|
||||
|
||||
type PressEventType =
|
||||
@@ -85,6 +86,7 @@ type PressEventType =
|
||||
| 'contextmenu';
|
||||
|
||||
type PressEvent = {|
|
||||
defaultPrevented: boolean,
|
||||
target: Element | Document,
|
||||
type: PressEventType,
|
||||
pointerType: PointerType,
|
||||
@@ -155,6 +157,7 @@ function createPressEvent(
|
||||
target: Element | Document,
|
||||
pointerType: PointerType,
|
||||
event: ?ReactDOMResponderEvent,
|
||||
defaultPrevented: boolean,
|
||||
): PressEvent {
|
||||
const timeStamp = context.getTimeStamp();
|
||||
let clientX = null;
|
||||
@@ -184,6 +187,7 @@ function createPressEvent(
|
||||
}
|
||||
}
|
||||
return {
|
||||
defaultPrevented,
|
||||
target,
|
||||
type,
|
||||
pointerType,
|
||||
@@ -213,12 +217,16 @@ function dispatchEvent(
|
||||
): void {
|
||||
const target = ((state.pressTarget: any): Element | Document);
|
||||
const pointerType = state.pointerType;
|
||||
const defaultPrevented =
|
||||
(event != null && event.nativeEvent.defaultPrevented === true) ||
|
||||
(name === 'press' && state.shouldPreventClick);
|
||||
const syntheticEvent = createPressEvent(
|
||||
context,
|
||||
name,
|
||||
target,
|
||||
pointerType,
|
||||
event,
|
||||
defaultPrevented,
|
||||
);
|
||||
context.dispatchEvent(syntheticEvent, listener, eventPriority);
|
||||
}
|
||||
@@ -631,6 +639,7 @@ const PressResponder = {
|
||||
responderRegionOnDeactivation: null,
|
||||
ignoreEmulatedMouseEvents: false,
|
||||
activePointerId: null,
|
||||
shouldPreventClick: false,
|
||||
};
|
||||
},
|
||||
allowMultipleHostChildren: false,
|
||||
@@ -728,14 +737,13 @@ const PressResponder = {
|
||||
}
|
||||
|
||||
case 'contextmenu': {
|
||||
if (props.disableContextMenu) {
|
||||
if (props.preventContextMenu) {
|
||||
// Skip dispatching of onContextMenu below
|
||||
nativeEvent.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (isPressed) {
|
||||
if (props.preventDefault !== false) {
|
||||
if (props.preventDefault !== false && !nativeEvent.defaultPrevented) {
|
||||
// Skip dispatching of onContextMenu below
|
||||
nativeEvent.preventDefault();
|
||||
return;
|
||||
@@ -863,6 +871,31 @@ const PressResponder = {
|
||||
isKeyboardEvent = true;
|
||||
}
|
||||
|
||||
// Determine whether to call preventDefault on subsequent native events.
|
||||
state.shouldPreventClick = false;
|
||||
if (
|
||||
context.isTargetWithinEventComponent(target) &&
|
||||
context.isTargetWithinHostComponent(target, 'a', true)
|
||||
) {
|
||||
const {
|
||||
altKey,
|
||||
ctrlKey,
|
||||
metaKey,
|
||||
shiftKey,
|
||||
} = (nativeEvent: MouseEvent);
|
||||
// Check "open in new window/tab" and "open context menu" key modifiers
|
||||
const preventDefault = props.preventDefault;
|
||||
if (
|
||||
preventDefault !== false &&
|
||||
!shiftKey &&
|
||||
!metaKey &&
|
||||
!ctrlKey &&
|
||||
!altKey
|
||||
) {
|
||||
state.shouldPreventClick = true;
|
||||
}
|
||||
}
|
||||
|
||||
const wasLongPressed = state.isLongPressed;
|
||||
dispatchPressEndEvents(event, context, props, state);
|
||||
|
||||
@@ -906,27 +939,8 @@ const PressResponder = {
|
||||
|
||||
case 'click': {
|
||||
removeRootEventTypes(context, state);
|
||||
if (
|
||||
context.isTargetWithinEventComponent(target) &&
|
||||
context.isTargetWithinHostComponent(target, 'a', true)
|
||||
) {
|
||||
const {
|
||||
altKey,
|
||||
ctrlKey,
|
||||
metaKey,
|
||||
shiftKey,
|
||||
} = (nativeEvent: MouseEvent);
|
||||
// Check "open in new window/tab" and "open context menu" key modifiers
|
||||
const preventDefault = props.preventDefault;
|
||||
if (
|
||||
preventDefault !== false &&
|
||||
!shiftKey &&
|
||||
!metaKey &&
|
||||
!ctrlKey &&
|
||||
!altKey
|
||||
) {
|
||||
nativeEvent.preventDefault();
|
||||
}
|
||||
if (state.shouldPreventClick) {
|
||||
nativeEvent.preventDefault();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2221,6 +2221,9 @@ describe('Event responder: Press', () => {
|
||||
ref.current.dispatchEvent(createEvent('pointerup'));
|
||||
ref.current.dispatchEvent(createEvent('click', {preventDefault}));
|
||||
expect(preventDefault).toBeCalled();
|
||||
expect(onPress).toHaveBeenCalledWith(
|
||||
expect.objectContaining({defaultPrevented: true}),
|
||||
);
|
||||
});
|
||||
|
||||
it('deeply prevents native behaviour by default', () => {
|
||||
@@ -2259,6 +2262,9 @@ describe('Event responder: Press', () => {
|
||||
ref.current.dispatchEvent(createEvent('pointerup'));
|
||||
ref.current.dispatchEvent(createEvent('click', {preventDefault}));
|
||||
expect(preventDefault).toBeCalled();
|
||||
expect(onPress).toHaveBeenCalledWith(
|
||||
expect.objectContaining({defaultPrevented: true}),
|
||||
);
|
||||
});
|
||||
|
||||
it('uses native behaviour for interactions with modifier keys', () => {
|
||||
@@ -2283,6 +2289,9 @@ describe('Event responder: Press', () => {
|
||||
createEvent('click', {[modifierKey]: true, preventDefault}),
|
||||
);
|
||||
expect(preventDefault).not.toBeCalled();
|
||||
expect(onPress).toHaveBeenCalledWith(
|
||||
expect.objectContaining({defaultPrevented: false}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2301,6 +2310,9 @@ describe('Event responder: Press', () => {
|
||||
ref.current.dispatchEvent(createEvent('pointerup'));
|
||||
ref.current.dispatchEvent(createEvent('click', {preventDefault}));
|
||||
expect(preventDefault).not.toBeCalled();
|
||||
expect(onPress).toHaveBeenCalledWith(
|
||||
expect.objectContaining({defaultPrevented: false}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2839,11 +2851,11 @@ describe('Event responder: Press', () => {
|
||||
expect(onContextMenu).toHaveBeenCalledTimes(0);
|
||||
});
|
||||
|
||||
it('is not called if "disableContextMenu" is true', () => {
|
||||
it('is still called if "preventContextMenu" is true', () => {
|
||||
const onContextMenu = jest.fn();
|
||||
const ref = React.createRef();
|
||||
const element = (
|
||||
<Press disableContextMenu={true} onContextMenu={onContextMenu}>
|
||||
<Press onContextMenu={onContextMenu} preventContextMenu={true}>
|
||||
<div ref={ref} />
|
||||
</Press>
|
||||
);
|
||||
@@ -2852,7 +2864,10 @@ describe('Event responder: Press', () => {
|
||||
createEvent('pointerdown', {pointerType: 'mouse', button: 2}),
|
||||
);
|
||||
ref.current.dispatchEvent(createEvent('contextmenu'));
|
||||
expect(onContextMenu).toHaveBeenCalledTimes(0);
|
||||
expect(onContextMenu).toHaveBeenCalledTimes(1);
|
||||
expect(onContextMenu).toHaveBeenCalledWith(
|
||||
expect.objectContaining({defaultPrevented: true}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user