diff --git a/packages/react-events/src/__tests__/Press-test.internal.js b/packages/react-events/src/__tests__/Press-test.internal.js
index b86cb24948599..0cc2cbda205c6 100644
--- a/packages/react-events/src/__tests__/Press-test.internal.js
+++ b/packages/react-events/src/__tests__/Press-test.internal.js
@@ -14,7 +14,33 @@ let ReactFeatureFlags;
let ReactDOM;
let Press;
-describe('Press event responder', () => {
+const DEFAULT_LONG_PRESS_DELAY = 1000;
+
+const createPointerEvent = type => {
+ const event = document.createEvent('Event');
+ event.initEvent(type, true, true);
+ return event;
+};
+
+const createKeyboardEvent = (type, data) => {
+ return new KeyboardEvent(type, {
+ bubbles: true,
+ cancelable: true,
+ ...data,
+ });
+};
+
+function createSimpleResponder({responderProps, domProps}) {
+ return function Component() {
+ return (
+
+
+
+ );
+ };
+}
+
+describe('Event responder: Press', () => {
let container;
beforeEach(() => {
@@ -34,96 +60,350 @@ describe('Press event responder', () => {
container = null;
});
- it('should support onPress', () => {
- let buttonRef = React.createRef();
- let events = [];
-
- function handleOnPress1() {
- events.push('press 1');
- }
-
- function handleOnPress2() {
- events.push('press 2');
- }
-
- function handleOnMouseDown() {
- events.push('mousedown');
- }
-
- function handleKeyDown() {
- events.push('keydown');
- }
-
- function Component() {
- return (
-
-
-
-
+ describe('onPressStart', () => {
+ let onPressStart, ref;
+
+ beforeEach(() => {
+ onPressStart = jest.fn();
+ ref = React.createRef();
+ const element = (
+
+
);
- }
+ ReactDOM.render(element, container);
+ });
+
+ it('is called after "pointerdown" event', () => {
+ ref.current.dispatchEvent(createPointerEvent('pointerdown'));
+ expect(onPressStart).toHaveBeenCalledTimes(1);
+ });
- ReactDOM.render(, container);
+ it('ignores emulated "mousedown" event', () => {
+ ref.current.dispatchEvent(createPointerEvent('pointerdown'));
+ ref.current.dispatchEvent(createPointerEvent('mousedown'));
+ expect(onPressStart).toHaveBeenCalledTimes(1);
+ });
+
+ // No PointerEvent fallbacks
+ it('is called after "mousedown" event', () => {
+ ref.current.dispatchEvent(createPointerEvent('mousedown'));
+ expect(onPressStart).toHaveBeenCalledTimes(1);
+ });
+ it('is called after "touchstart" event', () => {
+ ref.current.dispatchEvent(createPointerEvent('touchstart'));
+ expect(onPressStart).toHaveBeenCalledTimes(1);
+ });
+
+ // TODO: complete delayPressStart tests
+ // describe('delayPressStart', () => {});
+ });
- const mouseDownEvent = document.createEvent('Event');
- mouseDownEvent.initEvent('mousedown', true, true);
- buttonRef.current.dispatchEvent(mouseDownEvent);
+ describe('onPressEnd', () => {
+ let onPressEnd, ref;
- const mouseUpEvent = document.createEvent('Event');
- mouseUpEvent.initEvent('mouseup', true, true);
- buttonRef.current.dispatchEvent(mouseUpEvent);
+ beforeEach(() => {
+ onPressEnd = jest.fn();
+ ref = React.createRef();
+ const element = (
+
+
+
+ );
+ ReactDOM.render(element, container);
+ });
- expect(events).toEqual(['mousedown', 'press 2', 'press 1']);
+ it('is called after "pointerup" event', () => {
+ ref.current.dispatchEvent(createPointerEvent('pointerdown'));
+ ref.current.dispatchEvent(createPointerEvent('pointerup'));
+ expect(onPressEnd).toHaveBeenCalledTimes(1);
+ });
- events = [];
- const keyDownEvent = new KeyboardEvent('keydown', {
- which: 13,
- keyCode: 13,
- bubbles: true,
- cancelable: true,
+ it('ignores emulated "mouseup" event', () => {
+ ref.current.dispatchEvent(createPointerEvent('touchstart'));
+ ref.current.dispatchEvent(createPointerEvent('touchend'));
+ ref.current.dispatchEvent(createPointerEvent('mouseup'));
+ expect(onPressEnd).toHaveBeenCalledTimes(1);
});
- buttonRef.current.dispatchEvent(keyDownEvent);
- // press 1 should not occur as press 2 will preventDefault
- expect(events).toEqual(['keydown', 'press 2']);
+ // No PointerEvent fallbacks
+ it('is called after "mouseup" event', () => {
+ ref.current.dispatchEvent(createPointerEvent('mousedown'));
+ ref.current.dispatchEvent(createPointerEvent('mouseup'));
+ expect(onPressEnd).toHaveBeenCalledTimes(1);
+ });
+
+ it('is called after "touchend" event', () => {
+ ref.current.dispatchEvent(createPointerEvent('touchstart'));
+ ref.current.dispatchEvent(createPointerEvent('touchend'));
+ expect(onPressEnd).toHaveBeenCalledTimes(1);
+ });
+
+ // TODO: complete delayPressStart tests
+ // describe('delayPressStart', () => {});
});
- it('should support onPressStart and onPressEnd', () => {
- let divRef = React.createRef();
- let events = [];
+ describe('onPressChange', () => {
+ let onPressChange, ref;
+
+ beforeEach(() => {
+ onPressChange = jest.fn();
+ ref = React.createRef();
+ const element = (
+
+
+
+ );
+ ReactDOM.render(element, container);
+ });
- function handleOnPressStart() {
- events.push('onPressStart');
- }
+ it('is called after "pointerdown" and "pointerup" events', () => {
+ ref.current.dispatchEvent(createPointerEvent('pointerdown'));
+ expect(onPressChange).toHaveBeenCalledTimes(1);
+ expect(onPressChange).toHaveBeenCalledWith(true);
+ ref.current.dispatchEvent(createPointerEvent('pointerup'));
+ expect(onPressChange).toHaveBeenCalledTimes(2);
+ expect(onPressChange).toHaveBeenCalledWith(false);
+ });
+ });
- function handleOnPressEnd() {
- events.push('onPressEnd');
- }
+ describe('onPress', () => {
+ let onPress, ref;
- function Component() {
- return (
-
- Press me!
+ beforeEach(() => {
+ onPress = jest.fn();
+ ref = React.createRef();
+ const element = (
+
+
);
- }
+ ReactDOM.render(element, container);
+ });
- ReactDOM.render(, container);
+ it('is called after "pointerup" event', () => {
+ ref.current.dispatchEvent(createPointerEvent('pointerdown'));
+ ref.current.dispatchEvent(createPointerEvent('pointerup'));
+ expect(onPress).toHaveBeenCalledTimes(1);
+ });
+ });
- const pointerEnterEvent = document.createEvent('Event');
- pointerEnterEvent.initEvent('pointerdown', true, true);
- divRef.current.dispatchEvent(pointerEnterEvent);
+ describe('onLongPress', () => {
+ let onLongPress, ref;
- const pointerLeaveEvent = document.createEvent('Event');
- pointerLeaveEvent.initEvent('pointerup', true, true);
- divRef.current.dispatchEvent(pointerLeaveEvent);
+ beforeEach(() => {
+ onLongPress = jest.fn();
+ ref = React.createRef();
+ const element = (
+
+
+
+ );
+ ReactDOM.render(element, container);
+ });
+
+ it('is called if press lasts default delay', () => {
+ ref.current.dispatchEvent(createPointerEvent('pointerdown'));
+ jest.advanceTimersByTime(DEFAULT_LONG_PRESS_DELAY - 1);
+ expect(onLongPress).not.toBeCalled();
+ jest.advanceTimersByTime(1);
+ expect(onLongPress).toHaveBeenCalledTimes(1);
+ });
- expect(events).toEqual(['onPressStart', 'onPressEnd']);
+ it('is not called if press is released before delay', () => {
+ ref.current.dispatchEvent(createPointerEvent('pointerdown'));
+ jest.advanceTimersByTime(DEFAULT_LONG_PRESS_DELAY - 1);
+ ref.current.dispatchEvent(createPointerEvent('pointerup'));
+ jest.advanceTimersByTime(1);
+ expect(onLongPress).not.toBeCalled();
+ });
+
+ describe('delayLongPress', () => {
+ it('can be configured', () => {
+ const element = (
+
+
+
+ );
+ ReactDOM.render(element, container);
+
+ ref.current.dispatchEvent(createPointerEvent('pointerdown'));
+ jest.advanceTimersByTime(1999);
+ expect(onLongPress).not.toBeCalled();
+ jest.advanceTimersByTime(1);
+ expect(onLongPress).toHaveBeenCalledTimes(1);
+ });
+
+ it('uses 10ms minimum delay length', () => {
+ const element = (
+
+
+
+ );
+ ReactDOM.render(element, container);
+
+ ref.current.dispatchEvent(createPointerEvent('pointerdown'));
+ jest.advanceTimersByTime(9);
+ expect(onLongPress).not.toBeCalled();
+ jest.advanceTimersByTime(1);
+ expect(onLongPress).toHaveBeenCalledTimes(1);
+ });
+
+ /*
+ it('compounds with "delayPressStart"', () => {
+ const delayPressStart = 100;
+ const element = (
+
+
+
+ );
+ ReactDOM.render(element, container);
+
+ ref.current.dispatchEvent(createPointerEvent('pointerdown'));
+ jest.advanceTimersByTime(delayPressStart + DEFAULT_LONG_PRESS_DELAY - 1);
+ expect(onLongPress).not.toBeCalled();
+ jest.advanceTimersByTime(1);
+ expect(onLongPress).toHaveBeenCalledTimes(1);
+ });
+ */
+ });
+ });
+
+ describe('onLongPressChange', () => {
+ let onLongPressChange, ref;
+
+ beforeEach(() => {
+ onLongPressChange = jest.fn();
+ ref = React.createRef();
+ const element = (
+
+
+
+ );
+ ReactDOM.render(element, container);
+ });
+
+ it('is called when long press state changes', () => {
+ ref.current.dispatchEvent(createPointerEvent('pointerdown'));
+ jest.advanceTimersByTime(DEFAULT_LONG_PRESS_DELAY);
+ expect(onLongPressChange).toHaveBeenCalledTimes(1);
+ expect(onLongPressChange).toHaveBeenCalledWith(true);
+ ref.current.dispatchEvent(createPointerEvent('pointerup'));
+ expect(onLongPressChange).toHaveBeenCalledTimes(2);
+ expect(onLongPressChange).toHaveBeenCalledWith(false);
+ });
+ });
+
+ describe('onLongPressShouldCancelPress', () => {
+ it('if true it cancels "onPress"', () => {
+ const onPress = jest.fn();
+ const onPressChange = jest.fn();
+ const ref = React.createRef();
+ const element = (
+ {}}
+ onLongPressShouldCancelPress={() => true}
+ onPressChange={onPressChange}
+ onPress={onPressChange}>
+
+
+ );
+ ReactDOM.render(element, container);
+
+ // NOTE: onPressChange behavior should not be affected
+ ref.current.dispatchEvent(createPointerEvent('pointerdown'));
+ jest.advanceTimersByTime(DEFAULT_LONG_PRESS_DELAY);
+ expect(onPressChange).toHaveBeenCalledTimes(1);
+ ref.current.dispatchEvent(createPointerEvent('pointerup'));
+ expect(onPress).not.toBeCalled();
+ expect(onPressChange).toHaveBeenCalledTimes(2);
+ });
+ });
+
+ // TODO
+ //describe('`onPress*` with movement', () => {
+ //describe('within bounds of hit rect', () => {
+ /** ┌──────────────────┐
+ * │ ┌────────────┐ │
+ * │ │ VisualRect │ │
+ * │ └────────────┘ │
+ * │ HitRect X │ <= Move to X and release
+ * └──────────────────┘
+ */
+
+ //it('"onPress*" events are called when no delay', () => {});
+ //it('"onPress*" events are called after a delay', () => {});
+ //});
+
+ //describe('beyond bounds of hit rect', () => {
+ /** ┌──────────────────┐
+ * │ ┌────────────┐ │
+ * │ │ VisualRect │ │
+ * │ └────────────┘ │
+ * │ HitRect │
+ * └──────────────────┘
+ * X <= Move to X and release
+ */
+
+ //it('"onPress" only is not called when no delay', () => {});
+ //it('"onPress*" events are not called after a delay', () => {});
+ //it('"onPress*" events are called when press is released before measure completes', () => {});
+ //});
+ //});
+
+ describe('nested responders', () => {
+ it('dispatch events in the correct order', () => {
+ let events = [];
+ const ref = React.createRef();
+ const createEventHandler = msg => () => {
+ events.push(msg);
+ };
+
+ const element = (
+
+
+
+
+
+ );
+
+ ReactDOM.render(element, container);
+
+ ref.current.dispatchEvent(createPointerEvent('pointerdown'));
+ ref.current.dispatchEvent(createPointerEvent('pointerup'));
+ expect(events).toEqual([
+ 'pointerdown',
+ 'inner: onPressStart',
+ 'inner: onPressChange',
+ 'outer: onPressStart',
+ 'outer: onPressChange',
+ 'pointerup',
+ 'inner: onPressEnd',
+ 'inner: onPressChange',
+ 'inner: onPress',
+ 'outer: onPressEnd',
+ 'outer: onPressChange',
+ 'outer: onPress',
+ ]);
+
+ events = [];
+ ref.current.dispatchEvent(createKeyboardEvent('keydown', {keyCode: 13}));
+ // Outer press should not occur as inner press will preventDefault
+ expect(events).toEqual(['keydown', 'inner: onPress']);
+ });
});
});