diff --git a/packages/react-dom/src/__tests__/ReactDOMEventListener-test.js b/packages/react-dom/src/__tests__/ReactDOMEventListener-test.js
index 0e04aac94e..c1d2fd6758 100644
--- a/packages/react-dom/src/__tests__/ReactDOMEventListener-test.js
+++ b/packages/react-dom/src/__tests__/ReactDOMEventListener-test.js
@@ -9,184 +9,183 @@
'use strict';
-var EVENT_TARGET_PARAM = 1;
-
describe('ReactDOMEventListener', () => {
var React;
var ReactDOM;
- var ReactDOMComponentTree;
- var ReactDOMEventListener;
- var ReactTestUtils;
- var handleTopLevel;
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
- // TODO: can we express this test with only public API?
- ReactDOMComponentTree = require('../client/ReactDOMComponentTree').default;
- ReactDOMEventListener = require('../events/ReactDOMEventListener').default;
- ReactTestUtils = require('react-dom/test-utils');
-
- handleTopLevel = jest.fn();
- ReactDOMEventListener._handleTopLevel = handleTopLevel;
});
it('should dispatch events from outside React tree', () => {
+ var mock = jest.fn();
+
+ var container = document.createElement('div');
+ var node = ReactDOM.render(
, container);
var otherNode = document.createElement('h1');
- var component = ReactDOM.render(
, document.createElement('div'));
- expect(handleTopLevel.mock.calls.length).toBe(0);
- ReactDOMEventListener.dispatchEvent('topMouseOut', {
- type: 'mouseout',
- fromElement: otherNode,
- target: otherNode,
- srcElement: otherNode,
- toElement: ReactDOM.findDOMNode(component),
- relatedTarget: ReactDOM.findDOMNode(component),
- view: window,
- path: [otherNode, otherNode],
- });
- expect(handleTopLevel.mock.calls.length).toBe(1);
+ document.body.appendChild(container);
+ document.body.appendChild(otherNode);
+
+ otherNode.dispatchEvent(
+ new MouseEvent('mouseout', {
+ bubbles: true,
+ cancelable: true,
+ relatedTarget: node,
+ }),
+ );
+ expect(mock).toBeCalled();
});
describe('Propagation', () => {
it('should propagate events one level down', () => {
+ var mouseOut = jest.fn();
+ var onMouseOut = event => mouseOut(event.currentTarget);
+
var childContainer = document.createElement('div');
- var childControl = Child
;
var parentContainer = document.createElement('div');
- var parentControl = Parent
;
- childControl = ReactDOM.render(childControl, childContainer);
- parentControl = ReactDOM.render(parentControl, parentContainer);
- ReactDOM.findDOMNode(parentControl).appendChild(childContainer);
-
- var callback = ReactDOMEventListener.dispatchEvent.bind(null, 'test');
- callback({
- target: ReactDOM.findDOMNode(childControl),
- });
-
- var calls = handleTopLevel.mock.calls;
- expect(calls.length).toBe(2);
- expect(calls[0][EVENT_TARGET_PARAM]).toBe(
- ReactDOMComponentTree.getInstanceFromNode(childControl),
+ var childNode = ReactDOM.render(
+ Child
,
+ childContainer,
);
- expect(calls[1][EVENT_TARGET_PARAM]).toBe(
- ReactDOMComponentTree.getInstanceFromNode(parentControl),
+ var parentNode = ReactDOM.render(
+ div
,
+ parentContainer,
);
+ parentNode.appendChild(childContainer);
+ document.body.appendChild(parentContainer);
+
+ var nativeEvent = document.createEvent('Event');
+ nativeEvent.initEvent('mouseout', true, true);
+ childNode.dispatchEvent(nativeEvent);
+
+ expect(mouseOut).toBeCalled();
+ expect(mouseOut.mock.calls.length).toBe(2);
+ expect(mouseOut.mock.calls[0][0]).toEqual(childNode);
+ expect(mouseOut.mock.calls[1][0]).toEqual(parentNode);
+
+ document.body.removeChild(parentContainer);
});
it('should propagate events two levels down', () => {
+ var mouseOut = jest.fn();
+ var onMouseOut = event => mouseOut(event.currentTarget);
+
var childContainer = document.createElement('div');
- var childControl = Child
;
var parentContainer = document.createElement('div');
- var parentControl = Parent
;
var grandParentContainer = document.createElement('div');
- var grandParentControl = Parent
;
- childControl = ReactDOM.render(childControl, childContainer);
- parentControl = ReactDOM.render(parentControl, parentContainer);
- grandParentControl = ReactDOM.render(
- grandParentControl,
+ var childNode = ReactDOM.render(
+ Child
,
+ childContainer,
+ );
+ var parentNode = ReactDOM.render(
+ Parent
,
+ parentContainer,
+ );
+ var grandParentNode = ReactDOM.render(
+ Parent
,
grandParentContainer,
);
- ReactDOM.findDOMNode(parentControl).appendChild(childContainer);
- ReactDOM.findDOMNode(grandParentControl).appendChild(parentContainer);
+ parentNode.appendChild(childContainer);
+ grandParentNode.appendChild(parentContainer);
- var callback = ReactDOMEventListener.dispatchEvent.bind(null, 'test');
- callback({
- target: ReactDOM.findDOMNode(childControl),
- });
+ document.body.appendChild(grandParentContainer);
- var calls = handleTopLevel.mock.calls;
- expect(calls.length).toBe(3);
- expect(calls[0][EVENT_TARGET_PARAM]).toBe(
- ReactDOMComponentTree.getInstanceFromNode(childControl),
- );
- expect(calls[1][EVENT_TARGET_PARAM]).toBe(
- ReactDOMComponentTree.getInstanceFromNode(parentControl),
- );
- expect(calls[2][EVENT_TARGET_PARAM]).toBe(
- ReactDOMComponentTree.getInstanceFromNode(grandParentControl),
- );
+ var nativeEvent = document.createEvent('Event');
+ nativeEvent.initEvent('mouseout', true, true);
+ childNode.dispatchEvent(nativeEvent);
+
+ expect(mouseOut).toBeCalled();
+ expect(mouseOut.mock.calls.length).toBe(3);
+ expect(mouseOut.mock.calls[0][0]).toEqual(childNode);
+ expect(mouseOut.mock.calls[1][0]).toEqual(parentNode);
+ expect(mouseOut.mock.calls[2][0]).toEqual(grandParentNode);
+
+ document.body.removeChild(grandParentContainer);
});
+ // Regression test for https://github.com/facebook/react/issues/1105
it('should not get confused by disappearing elements', () => {
- var childContainer = document.createElement('div');
- var childControl = Child
;
- var parentContainer = document.createElement('div');
- var parentControl = Parent
;
- childControl = ReactDOM.render(childControl, childContainer);
- parentControl = ReactDOM.render(parentControl, parentContainer);
- ReactDOM.findDOMNode(parentControl).appendChild(childContainer);
-
- // ReactBrowserEventEmitter.handleTopLevel might remove the
- // target from the DOM. Here, we have handleTopLevel remove the
- // node when the first event handlers are called; we'll still
- // expect to receive a second call for the parent control.
- var childNode = ReactDOM.findDOMNode(childControl);
- handleTopLevel.mockImplementation(function(
- topLevelType,
- topLevelTarget,
- topLevelTargetID,
- nativeEvent,
- ) {
- if (topLevelTarget === childNode) {
- ReactDOM.unmountComponentAtNode(childContainer);
+ var container = document.createElement('div');
+ document.body.appendChild(container);
+ class MyComponent extends React.Component {
+ state = {clicked: false};
+ handleClick = () => {
+ this.setState({clicked: true});
+ };
+ componentDidMount() {
+ expect(ReactDOM.findDOMNode(this)).toBe(container.firstChild);
}
- });
-
- var callback = ReactDOMEventListener.dispatchEvent.bind(null, 'test');
- callback({
- target: childNode,
- });
-
- var calls = handleTopLevel.mock.calls;
- expect(calls.length).toBe(2);
- expect(calls[0][EVENT_TARGET_PARAM]).toBe(
- ReactDOMComponentTree.getInstanceFromNode(childNode),
- );
- expect(calls[1][EVENT_TARGET_PARAM]).toBe(
- ReactDOMComponentTree.getInstanceFromNode(parentControl),
+ componentDidUpdate() {
+ expect(ReactDOM.findDOMNode(this)).toBe(container.firstChild);
+ }
+ render() {
+ if (this.state.clicked) {
+ return clicked! ;
+ } else {
+ return not yet clicked ;
+ }
+ }
+ }
+ ReactDOM.render( , container);
+ container.firstChild.dispatchEvent(
+ new MouseEvent('click', {
+ bubbles: true,
+ }),
);
+ expect(container.firstChild.textContent).toBe('clicked!');
+ document.body.removeChild(container);
});
it('should batch between handlers from different roots', () => {
+ var mock = jest.fn();
+
var childContainer = document.createElement('div');
+ var handleChildMouseOut = () => {
+ ReactDOM.render(1
, childContainer);
+ mock(childNode.textContent);
+ };
+
var parentContainer = document.createElement('div');
- var childControl = ReactDOM.render(Child
, childContainer);
- var parentControl = ReactDOM.render(Parent
, parentContainer);
- ReactDOM.findDOMNode(parentControl).appendChild(childContainer);
+ var handleParentMouseOut = () => {
+ ReactDOM.render(2
, childContainer);
+ mock(childNode.textContent);
+ };
- // Suppose an event handler in each root enqueues an update to the
- // childControl element -- the two updates should get batched together.
- var childNode = ReactDOM.findDOMNode(childControl);
- handleTopLevel.mockImplementation(function(
- topLevelType,
- topLevelTarget,
- topLevelTargetID,
- nativeEvent,
- ) {
- ReactDOM.render(
- {topLevelTarget === childNode ? '1' : '2'}
,
- childContainer,
- );
- // Since we're batching, neither update should yet have gone through.
- expect(childNode.textContent).toBe('Child');
- });
-
- var callback = ReactDOMEventListener.dispatchEvent.bind(
- ReactDOMEventListener,
- 'test',
+ var childNode = ReactDOM.render(
+ Child
,
+ childContainer,
);
- callback({
- target: childNode,
- });
+ var parentNode = ReactDOM.render(
+ Parent
,
+ parentContainer,
+ );
+ parentNode.appendChild(childContainer);
+ document.body.appendChild(parentContainer);
- var calls = handleTopLevel.mock.calls;
- expect(calls.length).toBe(2);
+ var nativeEvent = document.createEvent('Event');
+ nativeEvent.initEvent('mouseout', true, true);
+ childNode.dispatchEvent(nativeEvent);
+
+ // Child and parent should both call from event handlers.
+ expect(mock.mock.calls.length).toBe(2);
+ // The first call schedules a render of '1' into the 'Child'.
+ // However, we're batching so it isn't flushed yet.
+ expect(mock.mock.calls[0][0]).toBe('Child');
+ // The first call schedules a render of '2' into the 'Child'.
+ // We're still batching so it isn't flushed yet either.
+ expect(mock.mock.calls[1][0]).toBe('Child');
+ // By the time we leave the handler, the second update is flushed.
expect(childNode.textContent).toBe('2');
+ document.body.removeChild(parentContainer);
});
});
it('should not fire duplicate events for a React DOM tree', () => {
+ var mouseOut = jest.fn();
+ var onMouseOut = event => mouseOut(event.target);
+
class Wrapper extends React.Component {
getInner = () => {
return this.refs.inner;
@@ -194,21 +193,22 @@ describe('ReactDOMEventListener', () => {
render() {
var inner = Inner
;
- return ;
+ return ;
}
}
- var instance = ReactTestUtils.renderIntoDocument( );
+ var container = document.createElement('div');
+ var instance = ReactDOM.render( , container);
- var callback = ReactDOMEventListener.dispatchEvent.bind(null, 'test');
- callback({
- target: ReactDOM.findDOMNode(instance.getInner()),
- });
+ document.body.appendChild(container);
- var calls = handleTopLevel.mock.calls;
- expect(calls.length).toBe(1);
- expect(calls[0][EVENT_TARGET_PARAM]).toBe(
- ReactDOMComponentTree.getInstanceFromNode(instance.getInner()),
- );
+ var nativeEvent = document.createEvent('Event');
+ nativeEvent.initEvent('mouseout', true, true);
+ instance.getInner().dispatchEvent(nativeEvent);
+
+ expect(mouseOut).toBeCalled();
+ expect(mouseOut.mock.calls.length).toBe(1);
+ expect(mouseOut.mock.calls[0][0]).toEqual(instance.getInner());
+ document.body.removeChild(container);
});
});