From 216407935a195556036dd3d5dbad4940a83d29bb Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Fri, 14 Aug 2020 09:57:19 -0700 Subject: [PATCH 1/3] Restore Previous Behavior Allowing onMouseEnter and onMouseLeave on Touchables Fixes #5715 See issue for some discussion. Pointer handling in RNW is something of a mess right now, but we need to preserve existing compatibility after conflicting Pressability changes were introuced. Fixup MouseEvent typings, add raw MouseEvent handlers to Pressability, and hook those to Touchables to allow previous behavior. We will need to reconcile this with other platforms eventually. I don't hook this up to Pressable. This leaves it impossible to add hover styling to Pressable, but I would rather we wait until we have a more generalized solution for MouseEvents before forking more component APIs we will need to worry about breaking later. Moved a mouse sample that existed in Playground to RNTester to try to catch mouse regressions in the future. Discovered our forked CoreEventTypes wasn't using the ".windows.js" platform suffix and fixed that. --- .../E2ETest/wdio/test/VisitAllPages.spec.ts | 1 + .../windows/playground/MainPage.xaml | 1 - vnext/.flowconfig | 2 + .../Touchable/TouchableHighlight.windows.js | 2 + .../Touchable/TouchableOpacity.windows.js | 2 + .../TouchableWithoutFeedback.windows.js | 7 +++- .../Components/View/ViewPropTypes.windows.js | 4 +- .../Pressability/Pressability.windows.js | 30 ++++++++++++++ ...ventTypes.js => CoreEventTypes.windows.js} | 21 +++++++++- .../Mouse/MouseExample.windows.js | 41 ++++++++++++------- .../RNTester/js/utils/RNTesterList.windows.ts | 4 ++ 11 files changed, 94 insertions(+), 21 deletions(-) rename vnext/src/Libraries/Types/{CoreEventTypes.js => CoreEventTypes.windows.js} (86%) rename packages/playground/Samples/mouse.tsx => vnext/src/RNTester/js/examples-win/Mouse/MouseExample.windows.js (88%) diff --git a/packages/E2ETest/wdio/test/VisitAllPages.spec.ts b/packages/E2ETest/wdio/test/VisitAllPages.spec.ts index a05a184ea8e..b6cf61ed734 100644 --- a/packages/E2ETest/wdio/test/VisitAllPages.spec.ts +++ b/packages/E2ETest/wdio/test/VisitAllPages.spec.ts @@ -52,6 +52,7 @@ let pages = [ 'Layout Events', 'Linking', 'Layout - Flexbox', + 'Mouse Events', 'Native Animated Example', 'PanResponder Sample', 'PlatformColor', diff --git a/packages/playground/windows/playground/MainPage.xaml b/packages/playground/windows/playground/MainPage.xaml index d416bdc9501..68e01a6bd20 100644 --- a/packages/playground/windows/playground/MainPage.xaml +++ b/packages/playground/windows/playground/MainPage.xaml @@ -69,7 +69,6 @@ - diff --git a/vnext/.flowconfig b/vnext/.flowconfig index a3c11493049..77a452679bf 100644 --- a/vnext/.flowconfig +++ b/vnext/.flowconfig @@ -29,6 +29,8 @@ /Libraries/Network/RCTNetworking.js /Libraries/NewAppScreen/components/DebugInstructions.js /Libraries/NewAppScreen/components/ReloadInstructions.js +/Libraries/Pressability/Pressability.js +/Libraries/Types/CoreEventTypes.js /RNTester/js/components/ListExampleShared.js /RNTester/js/components/RNTesterExampleList.js /RNTester/js/examples/ScrollView/ScrollViewExample.js diff --git a/vnext/src/Libraries/Components/Touchable/TouchableHighlight.windows.js b/vnext/src/Libraries/Components/Touchable/TouchableHighlight.windows.js index 2588a12cf72..50abe262b66 100644 --- a/vnext/src/Libraries/Components/Touchable/TouchableHighlight.windows.js +++ b/vnext/src/Libraries/Components/Touchable/TouchableHighlight.windows.js @@ -176,6 +176,8 @@ class TouchableHighlight extends React.Component { delayPressOut: this.props.delayPressOut, pressRectOffset: this.props.pressRetentionOffset, android_disableSound: this.props.touchSoundDisabled, + onMouseEnter: this.props.onMouseEnter, // [Windows] + onMouseLeave: this.props.onMouseLeave, // [Windows] onBlur: event => { if (Platform.isTV) { this._hideUnderlay(); diff --git a/vnext/src/Libraries/Components/Touchable/TouchableOpacity.windows.js b/vnext/src/Libraries/Components/Touchable/TouchableOpacity.windows.js index 33467a804db..3614afedb2e 100644 --- a/vnext/src/Libraries/Components/Touchable/TouchableOpacity.windows.js +++ b/vnext/src/Libraries/Components/Touchable/TouchableOpacity.windows.js @@ -148,6 +148,8 @@ class TouchableOpacity extends React.Component { delayPressIn: this.props.delayPressIn, delayPressOut: this.props.delayPressOut, pressRectOffset: this.props.pressRetentionOffset, + onMouseEnter: this.props.onMouseEnter, // [Windows] + onMouseLeave: this.props.onMouseLeave, // [Windows] onBlur: event => { if (Platform.isTV) { this._opacityInactive(250); diff --git a/vnext/src/Libraries/Components/Touchable/TouchableWithoutFeedback.windows.js b/vnext/src/Libraries/Components/Touchable/TouchableWithoutFeedback.windows.js index bb375e82310..fd8df17b1c2 100644 --- a/vnext/src/Libraries/Components/Touchable/TouchableWithoutFeedback.windows.js +++ b/vnext/src/Libraries/Components/Touchable/TouchableWithoutFeedback.windows.js @@ -27,6 +27,7 @@ import type { BlurEvent, FocusEvent, LayoutEvent, + MouseEvent, // [Windows] PressEvent, } from '../../Types/CoreEventTypes'; import Platform from '../../Utilities/Platform'; @@ -70,8 +71,8 @@ type Props = $ReadOnly<{| accessibilityPosInSet?: ?number, // [Windows] accessibilitySetSize?: ?number, // [Windows] onAccessibilityTap?: ?() => void, // [Windows] - onMouseEnter?: ?(event: SyntheticEvent<{}>) => void, // [Windows] - onMouseLeave?: ?(event: SyntheticEvent<{}>) => void, // [Windows] + onMouseEnter?: ?(event: MouseEvent) => void, // [Windows] + onMouseLeave?: ?(event: MouseEvent) => void, // [Windows] tabIndex?: ?number, // [Windows] tooltip?: ?Stringish, // [Windows] |}>; @@ -206,6 +207,8 @@ function createPressabilityConfig(props: Props): PressabilityConfig { onPress: props.onPress, onPressIn: props.onPressIn, onPressOut: props.onPressOut, + onMouseEnter: props.onMouseEnter, // [Windows] + onMouseLeave: props.onMouseLeave, // [Windows] }; } diff --git a/vnext/src/Libraries/Components/View/ViewPropTypes.windows.js b/vnext/src/Libraries/Components/View/ViewPropTypes.windows.js index a1c23fa94ce..5fa36a42259 100644 --- a/vnext/src/Libraries/Components/View/ViewPropTypes.windows.js +++ b/vnext/src/Libraries/Components/View/ViewPropTypes.windows.js @@ -435,8 +435,8 @@ type WindowsViewProps = $ReadOnly<{| onFocus?: ?(event: FocusEvent) => mixed, onBlur?: ?(event: FocusEvent) => mixed, - onMouseLeave?: ?(event: SyntheticEvent<{}>) => mixed, - onMouseEnter?: ?(event: SyntheticEvent<{}>) => mixed, + onMouseLeave?: ?(event: MouseEvent) => mixed, + onMouseEnter?: ?(event: MouseEvent) => mixed, |}>; // Windows] diff --git a/vnext/src/Libraries/Pressability/Pressability.windows.js b/vnext/src/Libraries/Pressability/Pressability.windows.js index 8c40816b89e..942618a5500 100644 --- a/vnext/src/Libraries/Pressability/Pressability.windows.js +++ b/vnext/src/Libraries/Pressability/Pressability.windows.js @@ -146,6 +146,22 @@ export type PressabilityConfig = $ReadOnly<{| * @deprecated */ onStartShouldSetResponder_DEPRECATED?: ?() => boolean, + + // [Windows + /** + * Raw handler for onMouseEnter that will be preferred if set over hover + * events. This is to preserve compatibility with pre-0.62 behavior which + * allowed attaching mouse event handlers to Touchables + */ + onMouseEnter?: ?(event: MouseEvent) => mixed, + + /** + * Raw handler for onMouseLeave that will be preferred if set over hover + * events. This is to preserve compatibility with pre-0.62 behavior which + * allowed attaching mouse event handlers to Touchables + */ + onMouseLeave?: ?(event: MouseEvent) => mixed, + // Windows] |}>; export type EventHandlers = $ReadOnly<{| @@ -562,6 +578,13 @@ export default class Pressability { ? null : { onMouseEnter: (event: MouseEvent): void => { + // [Windows Prefer attached raw mouse event handler for compat + if (this._config.onMouseEnter) { + this._config.onMouseEnter(event); + return; + } + // Windows] + if (isHoverEnabled()) { this._isHovered = true; this._cancelHoverOutDelayTimeout(); @@ -582,6 +605,13 @@ export default class Pressability { }, onMouseLeave: (event: MouseEvent): void => { + // [Windows Prefer attached raw mouse event handler for compat + if (this._config.onMouseLeave) { + this._config.onMouseLeave(event); + return; + } + // Windows] + if (this._isHovered) { this._isHovered = false; this._cancelHoverInDelayTimeout(); diff --git a/vnext/src/Libraries/Types/CoreEventTypes.js b/vnext/src/Libraries/Types/CoreEventTypes.windows.js similarity index 86% rename from vnext/src/Libraries/Types/CoreEventTypes.js rename to vnext/src/Libraries/Types/CoreEventTypes.windows.js index 3758e8356fc..cdb4d305025 100644 --- a/vnext/src/Libraries/Types/CoreEventTypes.js +++ b/vnext/src/Libraries/Types/CoreEventTypes.windows.js @@ -151,15 +151,32 @@ export type FocusEvent = SyntheticEvent< |}>, >; +// [Windows Mouse events on Windows don't match up with the version in core +// introduced for react-native-web. Replace typings with our values to catch +// anything dependent on react-native-web specific values export type MouseEvent = SyntheticEvent< $ReadOnly<{| - clientX: number, - clientY: number, + target: number, + identifier: number, pageX: number, pageY: number, + locationX: number, + locationY: number, timestamp: number, + pointerType: string, + force: number, + isLeftButton: boolean, + isRightButton: boolean, + isMiddleButton: boolean, + isBarrelButtonPressed: boolean, + isHorizontalScrollWheel: boolean, + isEraser: boolean, + shiftKey: boolean, + ctrlKey: boolean, + altKey: boolean, |}>, >; +// Windows] // [Windows export type KeyEvent = SyntheticEvent< diff --git a/packages/playground/Samples/mouse.tsx b/vnext/src/RNTester/js/examples-win/Mouse/MouseExample.windows.js similarity index 88% rename from packages/playground/Samples/mouse.tsx rename to vnext/src/RNTester/js/examples-win/Mouse/MouseExample.windows.js index 0c5d4bdf403..5ff0b74d660 100644 --- a/packages/playground/Samples/mouse.tsx +++ b/vnext/src/RNTester/js/examples-win/Mouse/MouseExample.windows.js @@ -3,16 +3,31 @@ * Licensed under the MIT License. * @format */ -import * as React from 'react'; -import { - AppRegistry, + +'use strict'; + +const React = require('react'); + +const { StyleSheet, View, Text, GestureResponderEvent, TouchableHighlight, BackHandler, -} from 'react-native'; +} = require('react-native'); + +exports.displayName = 'MouseExample'; +exports.title = 'Mouse Events'; +exports.description = 'Tests that mouse events can be observed'; +exports.examples = [ + { + title: 'onMouseEnter and onMouseLeave affect style\n', + render: function(): React.Node { + return ; + }, + }, +]; const styles = StyleSheet.create({ page: { @@ -68,16 +83,16 @@ const styles = StyleSheet.create({ }, }); -export default class Bootstrap extends React.Component< +export default class ExampleComponent extends React.Component< {}, { - clicked: number; - pageHover: boolean; - contentHover: boolean; - contentChildHover: boolean; - overlayHover: boolean; - overlayChildHover: boolean; - } + clicked: number, + pageHover: boolean, + contentHover: boolean, + contentChildHover: boolean, + overlayHover: boolean, + overlayChildHover: boolean, + }, > { constructor(props: {}) { super(props); @@ -225,5 +240,3 @@ export default class Bootstrap extends React.Component< return true; }; } - -AppRegistry.registerComponent('Bootstrap', () => Bootstrap); diff --git a/vnext/src/RNTester/js/utils/RNTesterList.windows.ts b/vnext/src/RNTester/js/utils/RNTesterList.windows.ts index 508b7ce85fd..95163ac0f5a 100644 --- a/vnext/src/RNTester/js/utils/RNTesterList.windows.ts +++ b/vnext/src/RNTester/js/utils/RNTesterList.windows.ts @@ -221,6 +221,10 @@ const APIExamples: Array = [ key: 'LayoutExample', module: require('react-native/RNTester/js/examples/Layout/LayoutExample'), }, + { + key: 'MouseExample', + module: require('./../examples-win/Mouse/MouseExample'), + }, { key: 'NativeAnimationsExample', module: require('react-native/RNTester/js/examples/NativeAnimation/NativeAnimationsExample'), From 6fb0025d216a62ca91574e3acd4f15996966d74a Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Fri, 14 Aug 2020 09:57:34 -0700 Subject: [PATCH 2/3] Change files --- ...e-windows-2020-08-14-09-57-33-fix-touchable-mouse.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 change/react-native-windows-2020-08-14-09-57-33-fix-touchable-mouse.json diff --git a/change/react-native-windows-2020-08-14-09-57-33-fix-touchable-mouse.json b/change/react-native-windows-2020-08-14-09-57-33-fix-touchable-mouse.json new file mode 100644 index 00000000000..145678c97f7 --- /dev/null +++ b/change/react-native-windows-2020-08-14-09-57-33-fix-touchable-mouse.json @@ -0,0 +1,8 @@ +{ + "type": "prerelease", + "comment": "Restore Previous Behavior Allowing onMouseEnter and onMouseLeave on Touchables", + "packageName": "react-native-windows", + "email": "ngerlem@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-08-14T16:57:33.938Z" +} From 9f49b158f7eeb34deacd593da8d09d77f4ba60bd Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Fri, 14 Aug 2020 10:57:45 -0700 Subject: [PATCH 3/3] Fix overrides and feedback --- vnext/overrides.json | 2 +- vnext/src/Libraries/Pressability/Pressability.windows.js | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/vnext/overrides.json b/vnext/overrides.json index a52c40c9acb..3675e3c5d32 100644 --- a/vnext/overrides.json +++ b/vnext/overrides.json @@ -437,7 +437,7 @@ }, { "type": "patch", - "file": "src/Libraries/Types/CoreEventTypes.js", + "file": "src/Libraries/Types/CoreEventTypes.windows.js", "baseFile": "Libraries/Types/CoreEventTypes.js", "baseVersion": "0.0.0-d8e6c4578", "baseHash": "83b203d547d9bdc57a8f3346ecee6f6e34b25f5d", diff --git a/vnext/src/Libraries/Pressability/Pressability.windows.js b/vnext/src/Libraries/Pressability/Pressability.windows.js index 942618a5500..23582aebf5a 100644 --- a/vnext/src/Libraries/Pressability/Pressability.windows.js +++ b/vnext/src/Libraries/Pressability/Pressability.windows.js @@ -578,10 +578,9 @@ export default class Pressability { ? null : { onMouseEnter: (event: MouseEvent): void => { - // [Windows Prefer attached raw mouse event handler for compat + // [Windows Add attached raw mouse event handler for compat if (this._config.onMouseEnter) { this._config.onMouseEnter(event); - return; } // Windows] @@ -605,10 +604,9 @@ export default class Pressability { }, onMouseLeave: (event: MouseEvent): void => { - // [Windows Prefer attached raw mouse event handler for compat + // [Windows Add attached raw mouse event handler for compat if (this._config.onMouseLeave) { this._config.onMouseLeave(event); - return; } // Windows]