Skip to content

Commit

Permalink
Add Press event prop types and fix a check in Safari (#15288)
Browse files Browse the repository at this point in the history
* Add PressProps type to event module

* Move default Press event delays to constants

* Fix right-click press check for Safari

* Prettier and Linter

* Use event.key in press responder

event.keyCode is a deprecated API

* Remove unused props from Press event module
  • Loading branch information
necolas authored and trueadm committed Apr 2, 2019
1 parent 4482fdd commit 296c439
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 20 deletions.
72 changes: 54 additions & 18 deletions packages/react-events/src/Press.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,11 @@
import type {EventResponderContext} from 'events/EventTypes';
import {REACT_EVENT_COMPONENT_TYPE} from 'shared/ReactSymbols';

// const DEFAULT_PRESS_DELAY_MS = 0;
// const DEFAULT_PRESS_END_DELAY_MS = 0;
// const DEFAULT_PRESS_START_DELAY_MS = 0;
const DEFAULT_LONG_PRESS_DELAY_MS = 1000;

const targetEventTypes = [
{name: 'click', passive: false},
{name: 'keydown', passive: false},
Expand All @@ -26,6 +31,21 @@ if (typeof window !== 'undefined' && window.PointerEvent === undefined) {
rootEventTypes.push({name: 'mouseup', passive: false});
}

type PressProps = {
disabled: boolean,
delayLongPress: number,
delayPressEnd: number,
delayPressStart: number,
onLongPress: (e: Object) => void,
onLongPressChange: boolean => void,
onLongPressShouldCancelPress: () => boolean,
onPress: (e: Object) => void,
onPressChange: boolean => void,
onPressEnd: (e: Object) => void,
onPressStart: (e: Object) => void,
pressRententionOffset: Object,
};

type PressState = {
defaultPrevented: boolean,
isAnchorTouched: boolean,
Expand All @@ -47,7 +67,7 @@ function dispatchPressEvent(

function dispatchPressStartEvents(
context: EventResponderContext,
props: Object,
props: PressProps,
state: PressState,
): void {
function dispatchPressChangeEvent(bool) {
Expand All @@ -64,7 +84,11 @@ function dispatchPressStartEvents(
dispatchPressChangeEvent(true);
}
if ((props.onLongPress || props.onLongPressChange) && !state.isLongPressed) {
const delayLongPress = calculateDelayMS(props.delayLongPress, 0, 1000);
const delayLongPress = calculateDelayMS(
props.delayLongPress,
0,
DEFAULT_LONG_PRESS_DELAY_MS,
);

state.longPressTimeout = setTimeout(() => {
state.isLongPressed = true;
Expand Down Expand Up @@ -105,7 +129,7 @@ function dispatchPressStartEvents(

function dispatchPressEndEvents(
context: EventResponderContext,
props: Object,
props: PressProps,
state: PressState,
): void {
if (state.longPressTimeout !== null) {
Expand Down Expand Up @@ -138,6 +162,11 @@ function isAnchorTagElement(eventTarget: EventTarget): boolean {
return (eventTarget: any).nodeName === 'A';
}

function isValidKeyPress(key: string): boolean {
// Accessibility for keyboards. Space and Enter only.
return key === ' ' || key === 'Enter';
}

function calculateDelayMS(delay: ?number, min = 0, fallback = 0) {
const maybeNumber = delay == null ? null : delay;
return Math.max(min, maybeNumber != null ? maybeNumber : fallback);
Expand All @@ -158,22 +187,18 @@ const PressResponder = {
},
handleEvent(
context: EventResponderContext,
props: Object,
props: PressProps,
state: PressState,
): void {
const {eventTarget, eventType, event} = context;

switch (eventType) {
case 'keydown': {
if (!props.onPress || context.isTargetOwned(eventTarget)) {
return;
}
const isValidKeyPress =
(event: any).which === 13 ||
(event: any).which === 32 ||
(event: any).keyCode === 13;

if (!isValidKeyPress) {
if (
!props.onPress ||
context.isTargetOwned(eventTarget) ||
!isValidKeyPress((event: any).key)
) {
return;
}
let keyPressEventListener = props.onPress;
Expand All @@ -196,8 +221,12 @@ const PressResponder = {
dispatchPressEvent(context, state, 'press', keyPressEventListener);
break;
}

/**
* Touch event implementations are only needed for Safari, which lacks
* support for pointer events.
*/
case 'touchstart':
// Touch events are for Safari, which lack pointer event support.
if (!state.isPressed && !context.isTargetOwned(eventTarget)) {
// We bail out of polyfilling anchor tags, given the same heuristics
// explained above in regards to needing to use click events.
Expand All @@ -213,7 +242,6 @@ const PressResponder = {

break;
case 'touchend': {
// Touch events are for Safari, which lack pointer event support
if (state.isAnchorTouched) {
return;
}
Expand Down Expand Up @@ -253,14 +281,21 @@ const PressResponder = {
}
break;
}

/**
* Respond to pointer events and fall back to mouse.
*/
case 'pointerdown':
case 'mousedown': {
if (
!state.isPressed &&
!context.isTargetOwned(eventTarget) &&
!state.shouldSkipMouseAfterTouch
) {
if ((event: any).pointerType === 'mouse') {
if (
(event: any).pointerType === 'mouse' ||
eventType === 'mousedown'
) {
// Ignore if we are pressing on hit slop area with mouse
if (
context.isPositionWithinTouchHitTarget(
Expand All @@ -282,8 +317,8 @@ const PressResponder = {
}
break;
}
case 'mouseup':
case 'pointerup': {
case 'pointerup':
case 'mouseup': {
if (state.isPressed) {
if (state.shouldSkipMouseAfterTouch) {
state.shouldSkipMouseAfterTouch = false;
Expand Down Expand Up @@ -320,6 +355,7 @@ const PressResponder = {
state.isAnchorTouched = false;
break;
}

case 'scroll':
case 'touchcancel':
case 'contextmenu':
Expand Down
3 changes: 1 addition & 2 deletions packages/react-events/src/__tests__/Press-test.internal.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,7 @@ describe('Press event responder', () => {

events = [];
const keyDownEvent = new KeyboardEvent('keydown', {
which: 13,
keyCode: 13,
key: 'Enter',
bubbles: true,
cancelable: true,
});
Expand Down

0 comments on commit 296c439

Please sign in to comment.