diff --git a/packages/react-events/src/dom/Press.js b/packages/react-events/src/dom/Press.js
index dcffeaa7e1..31fd03e971 100644
--- a/packages/react-events/src/dom/Press.js
+++ b/packages/react-events/src/dom/Press.js
@@ -685,14 +685,35 @@ const PressResponder: ReactDOMEventResponder = {
const isKeyboardEvent = pointerType === 'keyboard';
const isMouseEvent = pointerType === 'mouse';
+ // Ignore emulated mouse events
+ if (type === 'mousedown' && state.ignoreEmulatedMouseEvents) {
+ return;
+ }
+
+ state.shouldPreventClick = false;
if (isPointerEvent || isTouchEvent) {
state.ignoreEmulatedMouseEvents = true;
- } else if (type === 'mousedown' && state.ignoreEmulatedMouseEvents) {
- // Ignore emulated mouse events
- return;
} else if (isKeyboardEvent) {
// Ignore unrelated key events
- if (!isValidKeyboardEvent(nativeEvent)) {
+ if (isValidKeyboardEvent(nativeEvent)) {
+ const {
+ altKey,
+ ctrlKey,
+ metaKey,
+ shiftKey,
+ } = (nativeEvent: MouseEvent);
+ if (nativeEvent.key === ' ') {
+ nativeEvent.preventDefault();
+ } else if (
+ props.preventDefault !== false &&
+ !shiftKey &&
+ !metaKey &&
+ !ctrlKey &&
+ !altKey
+ ) {
+ state.shouldPreventClick = true;
+ }
+ } else {
return;
}
}
@@ -920,7 +941,6 @@ const PressResponder: ReactDOMEventResponder = {
}
// Determine whether to call preventDefault on subsequent native events.
- state.shouldPreventClick = false;
if (
context.isTargetWithinEventComponent(target) &&
context.isTargetWithinHostComponent(target, 'a', true)
diff --git a/packages/react-events/src/dom/__tests__/Press-test.internal.js b/packages/react-events/src/dom/__tests__/Press-test.internal.js
index 91020fa96f..eec3505087 100644
--- a/packages/react-events/src/dom/__tests__/Press-test.internal.js
+++ b/packages/react-events/src/dom/__tests__/Press-test.internal.js
@@ -46,11 +46,7 @@ function createTouchEvent(type, id, data) {
}
const createKeyboardEvent = (type, data) => {
- return new KeyboardEvent(type, {
- bubbles: true,
- cancelable: true,
- ...data,
- });
+ return createEvent(type, data);
};
function init() {
@@ -216,13 +212,20 @@ describe('Event responder: Press', () => {
});
it('is called once after "keydown" events for Spacebar', () => {
- ref.current.dispatchEvent(createKeyboardEvent('keydown', {key: ' '}));
+ const preventDefault = jest.fn();
+ ref.current.dispatchEvent(
+ createKeyboardEvent('keydown', {key: ' ', preventDefault}),
+ );
+ expect(preventDefault).toBeCalled();
ref.current.dispatchEvent(createKeyboardEvent('keypress', {key: ' '}));
ref.current.dispatchEvent(createKeyboardEvent('keydown', {key: ' '}));
ref.current.dispatchEvent(createKeyboardEvent('keypress', {key: ' '}));
expect(onPressStart).toHaveBeenCalledTimes(1);
expect(onPressStart).toHaveBeenCalledWith(
- expect.objectContaining({pointerType: 'keyboard', type: 'pressstart'}),
+ expect.objectContaining({
+ pointerType: 'keyboard',
+ type: 'pressstart',
+ }),
);
});
@@ -411,7 +414,6 @@ describe('Event responder: Press', () => {
target: ref.current,
}),
);
- ref.current.dispatchEvent(createEvent('mousedown'));
ref.current.dispatchEvent(
createEvent('pointerup', {pointerType: 'touch'}),
);
@@ -420,7 +422,9 @@ describe('Event responder: Press', () => {
target: ref.current,
}),
);
+ ref.current.dispatchEvent(createEvent('mousedown'));
ref.current.dispatchEvent(createEvent('mouseup'));
+ ref.current.dispatchEvent(createEvent('click'));
expect(onPressEnd).toHaveBeenCalledTimes(1);
expect(onPressEnd).toHaveBeenCalledWith(
expect.objectContaining({pointerType: 'touch', type: 'pressend'}),
@@ -2419,7 +2423,7 @@ describe('Event responder: Press', () => {
});
describe('link components', () => {
- it('prevents native behaviour by default', () => {
+ it('prevents native behaviour for pointer events by default', () => {
const onPress = jest.fn();
const preventDefault = jest.fn();
const ref = React.createRef();
@@ -2444,6 +2448,26 @@ describe('Event responder: Press', () => {
);
});
+ it('prevents native behaviour for keyboard events by default', () => {
+ const onPress = jest.fn();
+ const preventDefault = jest.fn();
+ const ref = React.createRef();
+ const element = (
+
+
+
+ );
+ ReactDOM.render(element, container);
+
+ ref.current.dispatchEvent(createEvent('keydown', {key: 'Enter'}));
+ ref.current.dispatchEvent(createEvent('click', {preventDefault}));
+ ref.current.dispatchEvent(createEvent('keyup', {key: 'Enter'}));
+ expect(preventDefault).toBeCalled();
+ expect(onPress).toHaveBeenCalledWith(
+ expect.objectContaining({defaultPrevented: true}),
+ );
+ });
+
it('deeply prevents native behaviour by default', () => {
const onPress = jest.fn();
const preventDefault = jest.fn();
@@ -2527,7 +2551,7 @@ describe('Event responder: Press', () => {
});
});
- it('uses native behaviour if preventDefault is false', () => {
+ it('uses native behaviour for pointer events if preventDefault is false', () => {
const onPress = jest.fn();
const preventDefault = jest.fn();
const ref = React.createRef();
@@ -2552,6 +2576,26 @@ describe('Event responder: Press', () => {
);
});
+ it('uses native behaviour for keyboard events if preventDefault is false', () => {
+ const onPress = jest.fn();
+ const preventDefault = jest.fn();
+ const ref = React.createRef();
+ const element = (
+
+
+
+ );
+ ReactDOM.render(element, container);
+
+ ref.current.dispatchEvent(createEvent('keydown', {key: 'Enter'}));
+ ref.current.dispatchEvent(createEvent('click', {preventDefault}));
+ ref.current.dispatchEvent(createEvent('keyup', {key: 'Enter'}));
+ expect(preventDefault).not.toBeCalled();
+ expect(onPress).toHaveBeenCalledWith(
+ expect.objectContaining({defaultPrevented: false}),
+ );
+ });
+
it('warns when preventDefault is used in an event hook', () => {
const onPress = jest.fn();
const preventDefault = jest.fn();