From 55fca7fd38b4e2f92b768713e0ec619dae7ee7ae Mon Sep 17 00:00:00 2001 From: Vincent Riemer Date: Mon, 17 Oct 2022 15:34:16 -0700 Subject: [PATCH] Add PointerOver/PointerOut handling test Summary: Changelog: [RNTester][Internal] - Add PointerOver/PointerOut handling test This diff adds another platform test based on WPT testcase for mouseover/mouseout events but instead applied to pointerover/pointerout events recently implemented for RN. Reviewed By: lunaleaps Differential Revision: D40359286 fbshipit-source-id: 672f413f56faca55b9d838150fb66de66d78d6f2 --- .../PointerEventPointerOverOut.js | 228 ++++++++++++++++++ .../Experimental/W3CPointerEventsExample.js | 9 + 2 files changed, 237 insertions(+) create mode 100644 packages/rn-tester/js/examples/Experimental/W3CPointerEventPlatformTests/PointerEventPointerOverOut.js diff --git a/packages/rn-tester/js/examples/Experimental/W3CPointerEventPlatformTests/PointerEventPointerOverOut.js b/packages/rn-tester/js/examples/Experimental/W3CPointerEventPlatformTests/PointerEventPointerOverOut.js new file mode 100644 index 00000000000..8b1046a760b --- /dev/null +++ b/packages/rn-tester/js/examples/Experimental/W3CPointerEventPlatformTests/PointerEventPointerOverOut.js @@ -0,0 +1,228 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow + */ + +import type {PlatformTestComponentBaseProps} from '../PlatformTest/RNTesterPlatformTestTypes'; +import type {PointerEvent} from 'react-native/Libraries/Types/CoreEventTypes'; +import type {HostComponent} from 'react-native/Libraries/Renderer/shims/ReactNativeTypes'; + +import RNTesterPlatformTest from '../PlatformTest/RNTesterPlatformTest'; +import * as React from 'react'; +import {useRef, useCallback} from 'react'; +import {StyleSheet, View} from 'react-native'; + +function getNativeTagFromHostElement( + elem: ?React.ElementRef> | number, +): ?number { + if (typeof elem === 'number') { + return elem; + } + if (elem != null) { + // $FlowExpectedError - accessing non-public property + return elem._nativeTag; + } + return undefined; +} + +const styles = StyleSheet.create({ + outer: { + padding: 40, + height: 60, + backgroundColor: 'blue', + }, + inner: { + padding: 40, + height: 60, + backgroundColor: 'green', + }, + released: { + padding: 40, + height: 60, + backgroundColor: 'yellow', + }, +}); + +// adapted from https://github.com/web-platform-tests/wpt/blob/master/uievents/order-of-events/mouse-events/mouseover-out.html +function PointerEventPointerOverOutTestCase( + props: PlatformTestComponentBaseProps, +) { + const {harness} = props; + + const t = harness.useAsyncTest('PointerOver/Out events'); + + const innerNativeTagRef = useRef(-1); + const outerNativeTagRef = useRef(-1); + + const handleInnerRefCallback = useCallback(elem => { + const nativeTag = getNativeTagFromHostElement(elem); + innerNativeTagRef.current = nativeTag != null ? nativeTag : -1; + }, []); + const handleOuterRefCallback = useCallback(elem => { + const nativeTag = getNativeTagFromHostElement(elem); + outerNativeTagRef.current = nativeTag != null ? nativeTag : -1; + }, []); + + const innerOverRef = useRef(0); + const innerOutRef = useRef(0); + + const outerOwnOverRef = useRef(0); + const outerOwnOutRef = useRef(0); + const outerOverRef = useRef(0); + const outerOutRef = useRef(0); + + const innerPointerOverHandler = useCallback( + (e: PointerEvent) => { + t.step(({assert_equals, assert_true}) => { + assert_equals( + innerOverRef.current, + innerOutRef.current, + 'pointerover is recieved before pointerout', + ); + switch (innerOverRef.current) { + case 0: { + assert_equals( + outerOwnOverRef.current, + 1, + 'should have triggered a pointerover in the outer before', + ); + break; + } + case 1: { + assert_equals( + outerOwnOverRef.current, + 1, + 'should have not triggered a pointerover in the outer before', + ); + break; + } + default: { + assert_true(false, 'should not get more than two mouseovers'); + } + } + }); + innerOverRef.current++; + }, + [t], + ); + const innerPointerOutHandler = useCallback( + (e: PointerEvent) => { + t.step(({assert_equals, assert_true}) => { + assert_equals( + innerOverRef.current, + innerOutRef.current + 1, + 'pointerout is received after pointerover', + ); + switch (innerOutRef.current) { + case 0: { + assert_equals( + outerOwnOutRef.current, + 1, + 'pointerout should have been received in the parent when hovering over this element', + ); + break; + } + case 1: { + break; + } + default: { + assert_true(false, 'should not get more than two pointerouts'); + } + } + }); + innerOutRef.current++; + }, + [t], + ); + + const outerPointerOverHandler = useCallback( + (e: PointerEvent) => { + const eventElemTag = getNativeTagFromHostElement(e.target); + t.step(({assert_equals}) => { + if (eventElemTag === outerNativeTagRef.current) { + assert_equals( + outerOwnOverRef.current, + outerOwnOutRef.current, + 'outer: pointerover is recieved before pointerout', + ); + outerOwnOverRef.current++; + } else { + assert_equals( + outerOverRef.current - outerOwnOverRef.current, + innerOverRef.current - 1, + 'pointerover: should only receive this via bubbling', + ); + } + }); + outerOverRef.current++; + }, + [t], + ); + const outerPointerOutHandler = useCallback( + (e: PointerEvent) => { + const eventElemTag = getNativeTagFromHostElement(e.target); + t.step(({assert_equals}) => { + if (eventElemTag === outerNativeTagRef.current) { + assert_equals( + outerOwnOverRef.current, + outerOwnOutRef.current + 1, + 'outer: pointerout is recieved after pointerover', + ); + if (outerOwnOutRef.current === 1) { + assert_equals(innerOutRef.current, 2, 'inner should be done now'); + t.done(); + } + outerOwnOutRef.current++; + } else { + assert_equals( + outerOutRef.current - outerOwnOutRef.current, + innerOutRef.current - 1, + 'pointerout: should only recieve this via bubbling', + ); + } + }); + outerOutRef.current++; + }, + [t], + ); + + return ( + <> + + + + + + ); +} + +type Props = $ReadOnly<{}>; +export default function PointerEventPointerOverOut( + props: Props, +): React.MixedElement { + return ( + + ); +} diff --git a/packages/rn-tester/js/examples/Experimental/W3CPointerEventsExample.js b/packages/rn-tester/js/examples/Experimental/W3CPointerEventsExample.js index 4e7b2757cd8..ac1684eb398 100644 --- a/packages/rn-tester/js/examples/Experimental/W3CPointerEventsExample.js +++ b/packages/rn-tester/js/examples/Experimental/W3CPointerEventsExample.js @@ -21,6 +21,7 @@ import PointerEventPointerMoveOnChordedMouseButton from './W3CPointerEventPlatfo import PointerEventPointerMoveAcross from './W3CPointerEventPlatformTests/PointerEventPointerMoveAcross'; import PointerEventPointerMoveEventOrder from './W3CPointerEventPlatformTests/PointerEventPointerMoveEventOrder'; import PointerEventPointerMoveBetween from './W3CPointerEventPlatformTests/PointerEventPointerMoveBetween'; +import PointerEventPointerOverOut from './W3CPointerEventPlatformTests/PointerEventPointerOverOut'; import EventfulView from './W3CPointerEventsEventfulView'; import ManyPointersPropertiesExample from './Compatibility/ManyPointersPropertiesExample'; @@ -214,6 +215,14 @@ export default { return ; }, }, + { + name: 'pointerevent_pointerover_out', + description: '', + title: 'WPT: PointerOver/PointerOut handling', + render(): React.Node { + return ; + }, + }, { name: 'relative', description: 'Children laid out using relative positioning',