Skip to content

Commit

Permalink
React events: keyboard press, types, tests (#15314)
Browse files Browse the repository at this point in the history
* Add HoverProps type
* Add more Hover event module tests
* Add more Press event module tests
* Change default longPress delay from 1000 to 500
* Rename dispatchPressEvent -> dispatchEvent
* Consolidate state updates in Press event module
* Add keyboard support for Press events
* Add FocusProps type and unit tests
  • Loading branch information
necolas authored Apr 4, 2019
1 parent 7a2dc48 commit 937d262
Show file tree
Hide file tree
Showing 6 changed files with 584 additions and 241 deletions.
33 changes: 23 additions & 10 deletions packages/react-events/src/Focus.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@
import type {EventResponderContext} from 'events/EventTypes';
import {REACT_EVENT_COMPONENT_TYPE} from 'shared/ReactSymbols';

const targetEventTypes = [
{name: 'focus', passive: true, capture: true},
{name: 'blur', passive: true, capture: true},
];
type FocusProps = {
disabled: boolean,
onBlur: (e: FocusEvent) => void,
onFocus: (e: FocusEvent) => void,
onFocusChange: boolean => void,
};

type FocusState = {
isFocused: boolean,
Expand All @@ -27,6 +29,11 @@ type FocusEvent = {|
type: FocusEventType,
|};

const targetEventTypes = [
{name: 'focus', passive: true, capture: true},
{name: 'blur', passive: true, capture: true},
];

function createFocusEvent(
type: FocusEventType,
target: Element | Document,
Expand All @@ -39,7 +46,10 @@ function createFocusEvent(
};
}

function dispatchFocusInEvents(context: EventResponderContext, props: Object) {
function dispatchFocusInEvents(
context: EventResponderContext,
props: FocusProps,
) {
const {event, eventTarget} = context;
if (context.isTargetWithinEventComponent((event: any).relatedTarget)) {
return;
Expand All @@ -53,19 +63,22 @@ function dispatchFocusInEvents(context: EventResponderContext, props: Object) {
context.dispatchEvent(syntheticEvent, {discrete: true});
}
if (props.onFocusChange) {
const focusChangeEventListener = () => {
const listener = () => {
props.onFocusChange(true);
};
const syntheticEvent = createFocusEvent(
'focuschange',
eventTarget,
focusChangeEventListener,
listener,
);
context.dispatchEvent(syntheticEvent, {discrete: true});
}
}

function dispatchFocusOutEvents(context: EventResponderContext, props: Object) {
function dispatchFocusOutEvents(
context: EventResponderContext,
props: FocusProps,
) {
const {event, eventTarget} = context;
if (context.isTargetWithinEventComponent((event: any).relatedTarget)) {
return;
Expand All @@ -75,13 +88,13 @@ function dispatchFocusOutEvents(context: EventResponderContext, props: Object) {
context.dispatchEvent(syntheticEvent, {discrete: true});
}
if (props.onFocusChange) {
const focusChangeEventListener = () => {
const listener = () => {
props.onFocusChange(false);
};
const syntheticEvent = createFocusEvent(
'focuschange',
eventTarget,
focusChangeEventListener,
listener,
);
context.dispatchEvent(syntheticEvent, {discrete: true});
}
Expand Down
65 changes: 41 additions & 24 deletions packages/react-events/src/Hover.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@
import type {EventResponderContext} from 'events/EventTypes';
import {REACT_EVENT_COMPONENT_TYPE} from 'shared/ReactSymbols';

const targetEventTypes = [
'pointerover',
'pointermove',
'pointerout',
'pointercancel',
];
type HoverProps = {
disabled: boolean,
delayHoverEnd: number,
delayHoverStart: number,
onHoverChange: boolean => void,
onHoverEnd: (e: HoverEvent) => void,
onHoverStart: (e: HoverEvent) => void,
};

type HoverState = {
isHovered: boolean,
Expand All @@ -31,6 +33,21 @@ type HoverEvent = {|
type: HoverEventType,
|};

// const DEFAULT_HOVER_END_DELAY_MS = 0;
// const DEFAULT_HOVER_START_DELAY_MS = 0;

const targetEventTypes = [
'pointerover',
'pointermove',
'pointerout',
'pointercancel',
];

// If PointerEvents is not supported (e.g., Safari), also listen to touch and mouse events.
if (typeof window !== 'undefined' && window.PointerEvent === undefined) {
targetEventTypes.push('touchstart', 'mouseover', 'mouseout');
}

function createHoverEvent(
type: HoverEventType,
target: Element | Document,
Expand All @@ -43,16 +60,9 @@ function createHoverEvent(
};
}

// In the case we don't have PointerEvents (Safari), we listen to touch events
// too
if (typeof window !== 'undefined' && window.PointerEvent === undefined) {
targetEventTypes.push('touchstart', 'mouseover', 'mouseout');
}

function dispatchHoverStartEvents(
context: EventResponderContext,
props: Object,
state: HoverState,
props: HoverProps,
): void {
const {event, eventTarget} = context;
if (context.isTargetWithinEventComponent((event: any).relatedTarget)) {
Expand All @@ -67,19 +77,22 @@ function dispatchHoverStartEvents(
context.dispatchEvent(syntheticEvent, {discrete: true});
}
if (props.onHoverChange) {
const hoverChangeEventListener = () => {
const listener = () => {
props.onHoverChange(true);
};
const syntheticEvent = createHoverEvent(
'hoverchange',
eventTarget,
hoverChangeEventListener,
listener,
);
context.dispatchEvent(syntheticEvent, {discrete: true});
}
}

function dispatchHoverEndEvents(context: EventResponderContext, props: Object) {
function dispatchHoverEndEvents(
context: EventResponderContext,
props: HoverProps,
) {
const {event, eventTarget} = context;
if (context.isTargetWithinEventComponent((event: any).relatedTarget)) {
return;
Expand All @@ -93,13 +106,13 @@ function dispatchHoverEndEvents(context: EventResponderContext, props: Object) {
context.dispatchEvent(syntheticEvent, {discrete: true});
}
if (props.onHoverChange) {
const hoverChangeEventListener = () => {
const listener = () => {
props.onHoverChange(false);
};
const syntheticEvent = createHoverEvent(
'hoverchange',
eventTarget,
hoverChangeEventListener,
listener,
);
context.dispatchEvent(syntheticEvent, {discrete: true});
}
Expand All @@ -116,18 +129,22 @@ const HoverResponder = {
},
handleEvent(
context: EventResponderContext,
props: Object,
props: HoverProps,
state: HoverState,
): void {
const {eventType, eventTarget, event} = context;

switch (eventType) {
case 'touchstart':
// Touch devices don't have hover support
/**
* Prevent hover events when touch is being used.
*/
case 'touchstart': {
if (!state.isTouched) {
state.isTouched = true;
}
break;
}

case 'pointerover':
case 'mouseover': {
if (
Expand All @@ -148,7 +165,7 @@ const HoverResponder = {
state.isInHitSlop = true;
return;
}
dispatchHoverStartEvents(context, props, state);
dispatchHoverStartEvents(context, props);
state.isHovered = true;
}
break;
Expand All @@ -172,7 +189,7 @@ const HoverResponder = {
(event: any).y,
)
) {
dispatchHoverStartEvents(context, props, state);
dispatchHoverStartEvents(context, props);
state.isHovered = true;
state.isInHitSlop = false;
}
Expand Down
Loading

0 comments on commit 937d262

Please sign in to comment.