[Flare] Press: fix keyboard interactions (#16179)

Prevents Spacebar from scrolling the window.
Prevents Enter from triggering a navigation if preventDefault is true.
Fixes the emulated mouse events test.
This commit is contained in:
Nicolas Gallagher
2019-07-22 18:16:40 -07:00
committed by GitHub
parent 03944bfb0b
commit bbd21066e6
2 changed files with 79 additions and 15 deletions
+25 -5
View File
@@ -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)
@@ -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 = (
<Press onPress={onPress}>
<a href="#" ref={ref} />
</Press>
);
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 = (
<Press onPress={onPress} preventDefault={false}>
<a href="#" ref={ref} />
</Press>
);
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();