diff --git a/fixtures/dom/src/components/Header.js b/fixtures/dom/src/components/Header.js index bb19e6247a531..9bb07072c40d8 100644 --- a/fixtures/dom/src/components/Header.js +++ b/fixtures/dom/src/components/Header.js @@ -51,6 +51,7 @@ class Header extends React.Component { + diff --git a/fixtures/dom/src/components/fixtures/index.js b/fixtures/dom/src/components/fixtures/index.js index 76712994e1991..0fff483a337a9 100644 --- a/fixtures/dom/src/components/fixtures/index.js +++ b/fixtures/dom/src/components/fixtures/index.js @@ -4,6 +4,7 @@ import TextInputFixtures from './text-inputs'; import SelectFixtures from './selects'; import TextAreaFixtures from './textareas'; import InputChangeEvents from './input-change-events'; +import MouseEventsFixtures from './mouse-events'; import NumberInputFixtures from './number-inputs'; import PasswordInputFixtures from './password-inputs'; import ButtonFixtures from './buttons'; @@ -25,6 +26,8 @@ function FixturesPage() { return ; case '/input-change-events': return ; + case '/mouse-events': + return ; case '/number-inputs': return ; case '/password-inputs': diff --git a/fixtures/dom/src/components/fixtures/mouse-events/index.js b/fixtures/dom/src/components/fixtures/mouse-events/index.js new file mode 100644 index 0000000000000..e72920c9abab2 --- /dev/null +++ b/fixtures/dom/src/components/fixtures/mouse-events/index.js @@ -0,0 +1,104 @@ +const React = window.React; +const ReactDOM = window.ReactDOM; + +const Rectangle = ({style, nodeRef, ...props}) => ( +
+); + +var portalContainer = document.createElement('div'); +document.body.appendChild(portalContainer) + + +class MouseEventsFixtures extends React.Component { + state = {log: [], box: null }; + + log = msg => { + this.setState(({log}) => ({ + log: log.concat(msg), + })); + }; + clearLog = () => { + this.setState({log: []}); + }; + + componentDidMount() { + this.getDimensions() + } + componentDidUpdate() { + this.getDimensions() + } + + getDimensions() { + if (!this.state.box && this.node) { + const { left, bottom } = this.node.getBoundingClientRect() + this.setState({ + box: { left: left + 10, top: bottom - 10 }, + }) + } else if (this.state.box && !this.node) { + this.setState({ box: null }) + } + } + + render() { + const { box } = this.state; + + let getLogger = prefix => e => this.log(`${prefix}: ${e.type}`); + let outerLogger = getLogger('outer'); + let innerLogger = getLogger('inner'); + + return ( +
+
+

+ Mouse the mouse between the two rectangles. The console should + only log for a given box when the mouse crosses into the box the first + time and again when the mouse exits the + {' '} + outer + {' '} + bounds of each box. +

+ + + + +

+ Moving from the outer rectangle to the inner blue Portal should not trigger a + mouseleave on the outer rectangle. +

+ this.node = n} + > + + {ReactDOM.unstable_createPortal( +
+ portal +
, + portalContainer, + )} +
+
+ + +
+

Console:

+
+            {this.state.log.join('\n')}
+          
+
+
+ ); + } +} + +module.exports = MouseEventsFixtures; diff --git a/scripts/rollup/results.json b/scripts/rollup/results.json index a55a0d0f6c4f8..63ac64b071be2 100644 --- a/scripts/rollup/results.json +++ b/scripts/rollup/results.json @@ -25,36 +25,36 @@ "gzip": 6601 }, "react-dom.development.js (UMD_DEV)": { - "size": 624799, - "gzip": 142849 + "size": 621341, + "gzip": 141972 }, "react-dom.production.min.js (UMD_PROD)": { - "size": 125896, - "gzip": 40026 + "size": 124686, + "gzip": 39686 }, "react-dom.development.js (NODE_DEV)": { - "size": 584197, - "gzip": 133211 + "size": 580739, + "gzip": 132303 }, "react-dom.production.min.js (NODE_PROD)": { - "size": 122812, - "gzip": 38953 + "size": 121602, + "gzip": 38641 }, "ReactDOMFiber-dev.js (FB_DEV)": { - "size": 583480, - "gzip": 133238 + "size": 580022, + "gzip": 132331 }, "ReactDOMFiber-prod.js (FB_PROD)": { - "size": 426550, - "gzip": 96621 + "size": 423122, + "gzip": 95727 }, "react-dom-test-utils.development.js (NODE_DEV)": { - "size": 54887, - "gzip": 14087 + "size": 54949, + "gzip": 14101 }, "ReactTestUtils-dev.js (FB_DEV)": { - "size": 54766, - "gzip": 14049 + "size": 54828, + "gzip": 14062 }, "ReactDOMServerStack-dev.js (FB_DEV)": { "size": 460810, @@ -65,20 +65,20 @@ "gzip": 81957 }, "react-dom-server.development.js (UMD_DEV)": { - "size": 120136, - "gzip": 30487 + "size": 120897, + "gzip": 30672 }, "react-dom-server.production.min.js (UMD_PROD)": { - "size": 20342, - "gzip": 7828 + "size": 20365, + "gzip": 7841 }, "react-dom-server.development.js (NODE_DEV)": { - "size": 89286, - "gzip": 23069 + "size": 90047, + "gzip": 23257 }, "react-dom-server.production.min.js (NODE_PROD)": { - "size": 18848, - "gzip": 7292 + "size": 18871, + "gzip": 7306 }, "ReactDOMServerStream-dev.js (FB_DEV)": { "size": 264750, @@ -113,20 +113,20 @@ "gzip": 45283 }, "ReactNativeStack-dev.js (RN_DEV)": { - "size": 186585, - "gzip": 36205 + "size": 185834, + "gzip": 36032 }, "ReactNativeStack-prod.js (RN_PROD)": { - "size": 136231, - "gzip": 26184 + "size": 135480, + "gzip": 25994 }, "ReactNativeFiber-dev.js (RN_DEV)": { - "size": 284554, - "gzip": 51697 + "size": 283803, + "gzip": 51516 }, "ReactNativeFiber-prod.js (RN_PROD)": { - "size": 220157, - "gzip": 38484 + "size": 219406, + "gzip": 38306 }, "react-test-renderer.development.js (NODE_DEV)": { "size": 281507, @@ -181,20 +181,20 @@ "gzip": 50920 }, "ReactDOMServer-dev.js (FB_DEV)": { - "size": 88798, - "gzip": 23020 + "size": 89559, + "gzip": 23201 }, "ReactDOMServer-prod.js (FB_PROD)": { - "size": 49545, - "gzip": 13626 + "size": 49640, + "gzip": 13644 }, "react-dom-node-stream.development.js (NODE_DEV)": { - "size": 90980, - "gzip": 23577 + "size": 91741, + "gzip": 23757 }, "react-dom-node-stream.production.min.js (NODE_PROD)": { - "size": 19785, - "gzip": 7628 + "size": 19808, + "gzip": 7646 }, "ReactDOMNodeStream-dev.js (FB_DEV)": { "size": 264918, @@ -205,28 +205,28 @@ "gzip": 50956 }, "react-dom-unstable-native-dependencies.development.js (UMD_DEV)": { - "size": 84047, - "gzip": 21057 + "size": 82995, + "gzip": 20767 }, "react-dom-unstable-native-dependencies.production.min.js (UMD_PROD)": { - "size": 18315, - "gzip": 5967 + "size": 17990, + "gzip": 5856 }, "react-dom-unstable-native-dependencies.development.js (NODE_DEV)": { - "size": 77475, - "gzip": 19101 + "size": 76423, + "gzip": 18804 }, "react-dom-unstable-native-dependencies.production.min.js (NODE_PROD)": { - "size": 16588, - "gzip": 5332 + "size": 16262, + "gzip": 5227 }, "ReactDOMUnstableNativeDependencies-dev.js (FB_DEV)": { - "size": 77189, - "gzip": 19061 + "size": 76137, + "gzip": 18768 }, "ReactDOMUnstableNativeDependencies-prod.js (FB_PROD)": { - "size": 65908, - "gzip": 15704 + "size": 64892, + "gzip": 15400 } } } \ No newline at end of file diff --git a/src/renderers/__tests__/ReactTreeTraversal-test.js b/src/renderers/__tests__/ReactTreeTraversal-test.js index 89d479c2a4855..54a3256c31e14 100644 --- a/src/renderers/__tests__/ReactTreeTraversal-test.js +++ b/src/renderers/__tests__/ReactTreeTraversal-test.js @@ -101,91 +101,6 @@ describe('ReactTreeTraversal', () => { }); }); - describe('traverseEnterLeave', () => { - it('should not traverse when enter/leaving outside DOM', () => { - ReactTreeTraversal.traverseEnterLeave(null, null, callback, ARG, ARG2); - expect(mockFn).not.toHaveBeenCalled(); - }); - - it('should not traverse if enter/leave the same node', () => { - var parent = renderParentIntoDocument(); - var leave = getInst(parent.refs.P_P1_C1.refs.DIV_1); - var enter = getInst(parent.refs.P_P1_C1.refs.DIV_1); - ReactTreeTraversal.traverseEnterLeave(leave, enter, callback, ARG, ARG2); - expect(mockFn).not.toHaveBeenCalled(); - }); - - it('should traverse enter/leave to sibling - avoids parent', () => { - var parent = renderParentIntoDocument(); - var leave = getInst(parent.refs.P_P1_C1.refs.DIV_1); - var enter = getInst(parent.refs.P_P1_C1.refs.DIV_2); - var expectedCalls = [ - ['P_P1_C1__DIV_1', 'bubbled', ARG], - // enter/leave shouldn't fire anything on the parent - ['P_P1_C1__DIV_2', 'captured', ARG2], - ]; - ReactTreeTraversal.traverseEnterLeave(leave, enter, callback, ARG, ARG2); - expect(mockFn.mock.calls).toEqual(expectedCalls); - }); - - it('should traverse enter/leave to parent - avoids parent', () => { - var parent = renderParentIntoDocument(); - var leave = getInst(parent.refs.P_P1_C1.refs.DIV_1); - var enter = getInst(parent.refs.P_P1_C1.refs.DIV); - var expectedCalls = [['P_P1_C1__DIV_1', 'bubbled', ARG]]; - ReactTreeTraversal.traverseEnterLeave(leave, enter, callback, ARG, ARG2); - expect(mockFn.mock.calls).toEqual(expectedCalls); - }); - - it('should enter from the window', () => { - var parent = renderParentIntoDocument(); - var leave = null; // From the window or outside of the React sandbox. - var enter = getInst(parent.refs.P_P1_C1.refs.DIV); - var expectedCalls = [ - ['P', 'captured', ARG2], - ['P_P1', 'captured', ARG2], - ['P_P1_C1__DIV', 'captured', ARG2], - ]; - ReactTreeTraversal.traverseEnterLeave(leave, enter, callback, ARG, ARG2); - expect(mockFn.mock.calls).toEqual(expectedCalls); - }); - - it('should enter from the window to the shallowest', () => { - var parent = renderParentIntoDocument(); - var leave = null; // From the window or outside of the React sandbox. - var enter = getInst(parent.refs.P); - var expectedCalls = [['P', 'captured', ARG2]]; - ReactTreeTraversal.traverseEnterLeave(leave, enter, callback, ARG, ARG2); - expect(mockFn.mock.calls).toEqual(expectedCalls); - }); - - it('should leave to the window', () => { - var parent = renderParentIntoDocument(); - var enter = null; // From the window or outside of the React sandbox. - var leave = getInst(parent.refs.P_P1_C1.refs.DIV); - var expectedCalls = [ - ['P_P1_C1__DIV', 'bubbled', ARG], - ['P_P1', 'bubbled', ARG], - ['P', 'bubbled', ARG], - ]; - ReactTreeTraversal.traverseEnterLeave(leave, enter, callback, ARG, ARG2); - expect(mockFn.mock.calls).toEqual(expectedCalls); - }); - - it('should leave to the window from the shallowest', () => { - var parent = renderParentIntoDocument(); - var enter = null; // From the window or outside of the React sandbox. - var leave = getInst(parent.refs.P_P1_C1.refs.DIV); - var expectedCalls = [ - ['P_P1_C1__DIV', 'bubbled', ARG], - ['P_P1', 'bubbled', ARG], - ['P', 'bubbled', ARG], - ]; - ReactTreeTraversal.traverseEnterLeave(leave, enter, callback, ARG, ARG2); - expect(mockFn.mock.calls).toEqual(expectedCalls); - }); - }); - describe('getFirstCommonAncestor', () => { it('should determine the first common ancestor correctly', () => { var parent = renderParentIntoDocument(); diff --git a/src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js b/src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js index 4a4fdbe808ffb..4e1a75b51623a 100644 --- a/src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js +++ b/src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js @@ -859,16 +859,10 @@ describe('ReactDOMFiber', () => { function simulateMouseMove(from, to) { if (from) { - ReactTestUtils.simulateNativeEventOnNode('topMouseOut', from, { - target: from, - relatedTarget: to, - }); + ReactTestUtils.simulateNativeEventOnNode('topMouseLeave', from, {}); } if (to) { - ReactTestUtils.simulateNativeEventOnNode('topMouseOver', to, { - target: to, - relatedTarget: from, - }); + ReactTestUtils.simulateNativeEventOnNode('topMouseEnter', to, {}); } } @@ -876,8 +870,9 @@ describe('ReactDOMFiber', () => {
ops.push('enter parent')} - onMouseLeave={() => ops.push('leave parent')}> -
(firstTarget = n)} /> + onMouseLeave={() => ops.push('leave parent')} + ref={n => (firstTarget = n)}> + {ReactDOM.unstable_createPortal(
ops.push('enter portal')} diff --git a/src/renderers/dom/shared/ReactBrowserEventEmitter.js b/src/renderers/dom/shared/ReactBrowserEventEmitter.js index 1c2661b57f9ab..7da7cd9bde868 100644 --- a/src/renderers/dom/shared/ReactBrowserEventEmitter.js +++ b/src/renderers/dom/shared/ReactBrowserEventEmitter.js @@ -171,6 +171,25 @@ var ReactBrowserEventEmitter = Object.assign({}, ReactEventEmitterMixin, { 'scroll', mountAt, ); + } else if ( + dependency === 'topMouseEnter' || + dependency === 'topMouseLeave' + ) { + if (isEventSupported('mouseenter', true)) { + ReactDOMEventListener.trapCapturedEvent( + 'topMouseEnter', + 'mouseenter', + mountAt, + ); + ReactDOMEventListener.trapCapturedEvent( + 'topMouseLeave', + 'mouseleave', + mountAt, + ); + } + + isListening.topMouseEnter = true; + isListening.topMouseLeave = true; } else if (dependency === 'topFocus' || dependency === 'topBlur') { ReactDOMEventListener.trapCapturedEvent('topFocus', 'focus', mountAt); ReactDOMEventListener.trapCapturedEvent('topBlur', 'blur', mountAt); diff --git a/src/renderers/dom/shared/ReactDOMClientInjection.js b/src/renderers/dom/shared/ReactDOMClientInjection.js index dc929517f41cd..89f7ee22c49da 100644 --- a/src/renderers/dom/shared/ReactDOMClientInjection.js +++ b/src/renderers/dom/shared/ReactDOMClientInjection.js @@ -14,7 +14,6 @@ var BeforeInputEventPlugin = require('BeforeInputEventPlugin'); var ChangeEventPlugin = require('ChangeEventPlugin'); var DOMEventPluginOrder = require('DOMEventPluginOrder'); -var EnterLeaveEventPlugin = require('EnterLeaveEventPlugin'); var EventPluginHub = require('EventPluginHub'); var EventPluginUtils = require('EventPluginUtils'); var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter'); @@ -39,7 +38,6 @@ EventPluginUtils.injection.injectComponentTree(ReactDOMComponentTree); */ EventPluginHub.injection.injectEventPluginsByName({ SimpleEventPlugin: SimpleEventPlugin, - EnterLeaveEventPlugin: EnterLeaveEventPlugin, ChangeEventPlugin: ChangeEventPlugin, SelectEventPlugin: SelectEventPlugin, BeforeInputEventPlugin: BeforeInputEventPlugin, diff --git a/src/renderers/dom/shared/eventPlugins/DOMEventPluginOrder.js b/src/renderers/dom/shared/eventPlugins/DOMEventPluginOrder.js index 510ec07ad0b11..2bf3e9e545284 100644 --- a/src/renderers/dom/shared/eventPlugins/DOMEventPluginOrder.js +++ b/src/renderers/dom/shared/eventPlugins/DOMEventPluginOrder.js @@ -24,7 +24,6 @@ var DOMEventPluginOrder = [ 'ResponderEventPlugin', 'SimpleEventPlugin', 'TapEventPlugin', - 'EnterLeaveEventPlugin', 'ChangeEventPlugin', 'SelectEventPlugin', 'BeforeInputEventPlugin', diff --git a/src/renderers/dom/shared/eventPlugins/EnterLeaveEventPlugin.js b/src/renderers/dom/shared/eventPlugins/EnterLeaveEventPlugin.js deleted file mode 100644 index fa9925f1448cd..0000000000000 --- a/src/renderers/dom/shared/eventPlugins/EnterLeaveEventPlugin.js +++ /dev/null @@ -1,122 +0,0 @@ -/** - * Copyright 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @providesModule EnterLeaveEventPlugin - */ - -'use strict'; - -var EventPropagators = require('EventPropagators'); -var ReactDOMComponentTree = require('ReactDOMComponentTree'); -var SyntheticMouseEvent = require('SyntheticMouseEvent'); - -var eventTypes = { - mouseEnter: { - registrationName: 'onMouseEnter', - dependencies: ['topMouseOut', 'topMouseOver'], - }, - mouseLeave: { - registrationName: 'onMouseLeave', - dependencies: ['topMouseOut', 'topMouseOver'], - }, -}; - -var EnterLeaveEventPlugin = { - eventTypes: eventTypes, - - /** - * For almost every interaction we care about, there will be both a top-level - * `mouseover` and `mouseout` event that occurs. Only use `mouseout` so that - * we do not extract duplicate events. However, moving the mouse into the - * browser from outside will not fire a `mouseout` event. In this case, we use - * the `mouseover` top-level event. - */ - extractEvents: function( - topLevelType, - targetInst, - nativeEvent, - nativeEventTarget, - ) { - if ( - topLevelType === 'topMouseOver' && - (nativeEvent.relatedTarget || nativeEvent.fromElement) - ) { - return null; - } - if (topLevelType !== 'topMouseOut' && topLevelType !== 'topMouseOver') { - // Must not be a mouse in or mouse out - ignoring. - return null; - } - - var win; - if (nativeEventTarget.window === nativeEventTarget) { - // `nativeEventTarget` is probably a window object. - win = nativeEventTarget; - } else { - // TODO: Figure out why `ownerDocument` is sometimes undefined in IE8. - var doc = nativeEventTarget.ownerDocument; - if (doc) { - win = doc.defaultView || doc.parentWindow; - } else { - win = window; - } - } - - var from; - var to; - if (topLevelType === 'topMouseOut') { - from = targetInst; - var related = nativeEvent.relatedTarget || nativeEvent.toElement; - to = related - ? ReactDOMComponentTree.getClosestInstanceFromNode(related) - : null; - } else { - // Moving to a node from outside the window. - from = null; - to = targetInst; - } - - if (from === to) { - // Nothing pertains to our managed components. - return null; - } - - var fromNode = from == null - ? win - : ReactDOMComponentTree.getNodeFromInstance(from); - var toNode = to == null - ? win - : ReactDOMComponentTree.getNodeFromInstance(to); - - var leave = SyntheticMouseEvent.getPooled( - eventTypes.mouseLeave, - from, - nativeEvent, - nativeEventTarget, - ); - leave.type = 'mouseleave'; - leave.target = fromNode; - leave.relatedTarget = toNode; - - var enter = SyntheticMouseEvent.getPooled( - eventTypes.mouseEnter, - to, - nativeEvent, - nativeEventTarget, - ); - enter.type = 'mouseenter'; - enter.target = toNode; - enter.relatedTarget = fromNode; - - EventPropagators.accumulateEnterLeaveDispatches(leave, enter, from, to); - - return [leave, enter]; - }, -}; - -module.exports = EnterLeaveEventPlugin; diff --git a/src/renderers/dom/shared/eventPlugins/SimpleEventPlugin.js b/src/renderers/dom/shared/eventPlugins/SimpleEventPlugin.js index 7acdeaca06c3b..4effc443ce67b 100644 --- a/src/renderers/dom/shared/eventPlugins/SimpleEventPlugin.js +++ b/src/renderers/dom/shared/eventPlugins/SimpleEventPlugin.js @@ -36,6 +36,11 @@ import type { import type {ReactInstance} from 'ReactInstanceType'; import type {EventTypes, PluginModule} from 'PluginModuleType'; +const topLevelDirectDispatches = { + topMouseEnter: true, + topMouseLeave: true, +}; + /** * Turns * ['abort', ...] @@ -95,6 +100,8 @@ var topLevelEventsToDispatchConfig: {[key: TopLevelTypes]: DispatchConfig} = {}; 'loadedMetadata', 'loadStart', 'mouseDown', + 'mouseEnter', + 'mouseLeave', 'mouseMove', 'mouseOut', 'mouseOver', @@ -126,14 +133,23 @@ var topLevelEventsToDispatchConfig: {[key: TopLevelTypes]: DispatchConfig} = {}; var capitalizedEvent = event[0].toUpperCase() + event.slice(1); var onEvent = 'on' + capitalizedEvent; var topEvent = 'top' + capitalizedEvent; + var isPhased = !topLevelDirectDispatches[topEvent]; var type = { - phasedRegistrationNames: { - bubbled: onEvent, - captured: onEvent + 'Capture', - }, dependencies: [topEvent], + phasedRegistrationNames: null, + registrationName: null, }; + + if (isPhased) { + type.phasedRegistrationNames = { + bubbled: onEvent, + captured: onEvent + 'Capture', + }; + } else { + type.registrationName = onEvent; + } + eventTypes[event] = type; topLevelEventsToDispatchConfig[topEvent] = type; }); @@ -213,6 +229,8 @@ var SimpleEventPlugin: PluginModule = { /* falls through */ case 'topDoubleClick': case 'topMouseDown': + case 'topMouseEnter': + case 'topMouseLeave': case 'topMouseMove': case 'topMouseUp': // TODO: Disabled elements should not respond to mouse events @@ -269,7 +287,13 @@ var SimpleEventPlugin: PluginModule = { nativeEvent, nativeEventTarget, ); - EventPropagators.accumulateTwoPhaseDispatches(event); + + if (topLevelDirectDispatches[topLevelType]) { + EventPropagators.accumulateDirectDispatches(event); + } else { + EventPropagators.accumulateTwoPhaseDispatches(event); + } + return event; }, }; diff --git a/src/renderers/dom/shared/eventPlugins/__tests__/EnterLeaveEventPlugin-test.js b/src/renderers/dom/shared/eventPlugins/__tests__/EnterLeaveEventPlugin-test.js deleted file mode 100644 index 2d7a6d426fe1d..0000000000000 --- a/src/renderers/dom/shared/eventPlugins/__tests__/EnterLeaveEventPlugin-test.js +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Copyright 2013-present, Facebook, Inc. - * All rights reserved. - * - * This source code is licensed under the BSD-style license found in the - * LICENSE file in the root directory of this source tree. An additional grant - * of patent rights can be found in the PATENTS file in the same directory. - * - * @emails react-core - */ - -'use strict'; - -var EnterLeaveEventPlugin; -var React; -var ReactDOM; -var ReactDOMComponentTree; - -describe('EnterLeaveEventPlugin', () => { - beforeEach(() => { - jest.resetModules(); - - React = require('react'); - ReactDOM = require('react-dom'); - // TODO: can we express this test with only public API? - ReactDOMComponentTree = require('ReactDOMComponentTree'); - EnterLeaveEventPlugin = require('EnterLeaveEventPlugin'); - }); - - it('should set relatedTarget properly in iframe', () => { - var iframe = document.createElement('iframe'); - document.body.appendChild(iframe); - - var iframeDocument = iframe.contentDocument; - - iframeDocument.write( - '
', - ); - iframeDocument.close(); - - var component = ReactDOM.render( -
, - iframeDocument.body.getElementsByTagName('div')[0], - ); - var div = ReactDOM.findDOMNode(component); - - var extracted = EnterLeaveEventPlugin.extractEvents( - 'topMouseOver', - ReactDOMComponentTree.getInstanceFromNode(div), - {target: div}, - div, - ); - expect(extracted.length).toBe(2); - - var leave = extracted[0]; - var enter = extracted[1]; - - expect(leave.target).toBe(iframe.contentWindow); - expect(leave.relatedTarget).toBe(div); - expect(enter.target).toBe(div); - expect(enter.relatedTarget).toBe(iframe.contentWindow); - }); -}); diff --git a/src/renderers/shared/shared/ReactTreeTraversal.js b/src/renderers/shared/shared/ReactTreeTraversal.js index c6c8aa265610b..dd667a11343f7 100644 --- a/src/renderers/shared/shared/ReactTreeTraversal.js +++ b/src/renderers/shared/shared/ReactTreeTraversal.js @@ -109,38 +109,9 @@ function traverseTwoPhase(inst, fn, arg) { } } -/** - * Traverses the ID hierarchy and invokes the supplied `cb` on any IDs that - * should would receive a `mouseEnter` or `mouseLeave` event. - * - * Does not invoke the callback on the nearest common ancestor because nothing - * "entered" or "left" that element. - */ -function traverseEnterLeave(from, to, fn, argFrom, argTo) { - var common = from && to ? getLowestCommonAncestor(from, to) : null; - var pathFrom = []; - while (from && from !== common) { - pathFrom.push(from); - from = getParent(from); - } - var pathTo = []; - while (to && to !== common) { - pathTo.push(to); - to = getParent(to); - } - var i; - for (i = 0; i < pathFrom.length; i++) { - fn(pathFrom[i], 'bubbled', argFrom); - } - for (i = pathTo.length; i-- > 0; ) { - fn(pathTo[i], 'captured', argTo); - } -} - module.exports = { isAncestor: isAncestor, getLowestCommonAncestor: getLowestCommonAncestor, getParentInstance: getParentInstance, traverseTwoPhase: traverseTwoPhase, - traverseEnterLeave: traverseEnterLeave, }; diff --git a/src/renderers/shared/shared/event/BrowserEventConstants.js b/src/renderers/shared/shared/event/BrowserEventConstants.js index 0f460486be72c..aca602fa9177b 100644 --- a/src/renderers/shared/shared/event/BrowserEventConstants.js +++ b/src/renderers/shared/shared/event/BrowserEventConstants.js @@ -64,6 +64,8 @@ var topLevelTypes = { topLoadedMetadata: 'loadedmetadata', topLoadStart: 'loadstart', topMouseDown: 'mousedown', + topMouseEnter: 'mouseenter', + topMouseLeave: 'mouseleave', topMouseMove: 'mousemove', topMouseOut: 'mouseout', topMouseOver: 'mouseover', diff --git a/src/renderers/shared/shared/event/EventPropagators.js b/src/renderers/shared/shared/event/EventPropagators.js index 4a0426548c472..cd828ad0b104a 100644 --- a/src/renderers/shared/shared/event/EventPropagators.js +++ b/src/renderers/shared/shared/event/EventPropagators.js @@ -127,16 +127,6 @@ function accumulateTwoPhaseDispatchesSkipTarget(events) { forEachAccumulated(events, accumulateTwoPhaseDispatchesSingleSkipTarget); } -function accumulateEnterLeaveDispatches(leave, enter, from, to) { - ReactTreeTraversal.traverseEnterLeave( - from, - to, - accumulateDispatches, - leave, - enter, - ); -} - function accumulateDirectDispatches(events) { forEachAccumulated(events, accumulateDirectDispatchesSingle); } @@ -156,7 +146,6 @@ var EventPropagators = { accumulateTwoPhaseDispatches: accumulateTwoPhaseDispatches, accumulateTwoPhaseDispatchesSkipTarget: accumulateTwoPhaseDispatchesSkipTarget, accumulateDirectDispatches: accumulateDirectDispatches, - accumulateEnterLeaveDispatches: accumulateEnterLeaveDispatches, }; module.exports = EventPropagators;