From c30ca2f02077ada08f0e269e2b487b588856f792 Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Fri, 14 Aug 2020 13:38:00 -0700 Subject: [PATCH] Restore Previous Behavior Allowing onMouseEnter and onMouseLeave on Touchables (#5736) * 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. * Change files * Fix overrides and feedback --- ...20-08-14-09-57-33-fix-touchable-mouse.json | 8 ++++ .../windows/playground/MainPage.xaml | 1 - vnext/.flowconfig | 2 + vnext/overrides.json | 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 | 28 +++++++++++++ ...ventTypes.js => CoreEventTypes.windows.js} | 21 +++++++++- .../Mouse/MouseExample.windows.js | 41 ++++++++++++------- .../RNTester/js/utils/RNTesterList.windows.ts | 4 ++ 12 files changed, 100 insertions(+), 22 deletions(-) create mode 100644 change/react-native-windows-2020-08-14-09-57-33-fix-touchable-mouse.json 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/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" +} diff --git a/packages/playground/windows/playground/MainPage.xaml b/packages/playground/windows/playground/MainPage.xaml index 8cb9ede4f50..4d6f0caf6e5 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 dd962e993de..2f8e56d7e16 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/overrides.json b/vnext/overrides.json index 667540f46ae..ca77325907c 100644 --- a/vnext/overrides.json +++ b/vnext/overrides.json @@ -449,7 +449,7 @@ }, { "type": "patch", - "file": "src/Libraries/Types/CoreEventTypes.js", + "file": "src/Libraries/Types/CoreEventTypes.windows.js", "baseFile": "Libraries/Types/CoreEventTypes.js", "baseVersion": "0.63.2", "baseHash": "83b203d547d9bdc57a8f3346ecee6f6e34b25f5d", diff --git a/vnext/src/Libraries/Components/Touchable/TouchableHighlight.windows.js b/vnext/src/Libraries/Components/Touchable/TouchableHighlight.windows.js index 407407d5c9e..966bde727cb 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 e007b19b260..347ea21b671 100644 --- a/vnext/src/Libraries/Pressability/Pressability.windows.js +++ b/vnext/src/Libraries/Pressability/Pressability.windows.js @@ -151,6 +151,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<{| @@ -569,6 +585,12 @@ export default class Pressability { ? null : { onMouseEnter: (event: MouseEvent): void => { + // [Windows Add attached raw mouse event handler for compat + if (this._config.onMouseEnter) { + this._config.onMouseEnter(event); + } + // Windows] + if (isHoverEnabled()) { this._isHovered = true; this._cancelHoverOutDelayTimeout(); @@ -589,6 +611,12 @@ export default class Pressability { }, onMouseLeave: (event: MouseEvent): void => { + // [Windows Add attached raw mouse event handler for compat + if (this._config.onMouseLeave) { + this._config.onMouseLeave(event); + } + // 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 d6f81af632f..9d2b3f028fb 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'),