mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Fix incorrect calculation of onMouseEnter/Leave component path (#11164)
* Add a regression test for #10906 * Turn while conditions into breaks without changing the logic This will be easier to follow when we add more code there. * var => const/let So that I can add a block scoped variable. * Check alternates when comparing to the common ancestor This is the actual bugfix.
This commit is contained in:
@@ -13,6 +13,7 @@ var EnterLeaveEventPlugin;
|
||||
var React;
|
||||
var ReactDOM;
|
||||
var ReactDOMComponentTree;
|
||||
var ReactTestUtils;
|
||||
|
||||
describe('EnterLeaveEventPlugin', () => {
|
||||
beforeEach(() => {
|
||||
@@ -20,6 +21,7 @@ describe('EnterLeaveEventPlugin', () => {
|
||||
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
ReactTestUtils = require('react-dom/test-utils');
|
||||
// TODO: can we express this test with only public API?
|
||||
ReactDOMComponentTree = require('ReactDOMComponentTree');
|
||||
EnterLeaveEventPlugin = require('EnterLeaveEventPlugin');
|
||||
@@ -58,4 +60,38 @@ describe('EnterLeaveEventPlugin', () => {
|
||||
expect(enter.target).toBe(div);
|
||||
expect(enter.relatedTarget).toBe(iframe.contentWindow);
|
||||
});
|
||||
|
||||
// Regression test for https://github.com/facebook/react/issues/10906.
|
||||
it('should find the common parent after updates', () => {
|
||||
let parentEnterCalls = 0;
|
||||
let childEnterCalls = 0;
|
||||
let parent = null;
|
||||
class Parent extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<div
|
||||
onMouseEnter={() => parentEnterCalls++}
|
||||
ref={node => (parent = node)}>
|
||||
{this.props.showChild &&
|
||||
<div onMouseEnter={() => childEnterCalls++} />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<Parent />, div);
|
||||
// The issue only reproduced on insertion during the first update.
|
||||
ReactDOM.render(<Parent showChild={true} />, div);
|
||||
|
||||
// Enter from parent into the child.
|
||||
ReactTestUtils.simulateNativeEventOnNode('topMouseOut', parent, {
|
||||
target: parent,
|
||||
relatedTarget: parent.firstChild,
|
||||
});
|
||||
|
||||
// Entering a child should fire on the child, not on the parent.
|
||||
expect(childEnterCalls).toBe(1);
|
||||
expect(parentEnterCalls).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -110,18 +110,38 @@ function traverseTwoPhase(inst, fn, arg) {
|
||||
* "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) {
|
||||
const common = from && to ? getLowestCommonAncestor(from, to) : null;
|
||||
const pathFrom = [];
|
||||
while (true) {
|
||||
if (!from) {
|
||||
break;
|
||||
}
|
||||
if (from === common) {
|
||||
break;
|
||||
}
|
||||
const alternate = from.alternate;
|
||||
if (alternate !== null && alternate === common) {
|
||||
break;
|
||||
}
|
||||
pathFrom.push(from);
|
||||
from = getParent(from);
|
||||
}
|
||||
var pathTo = [];
|
||||
while (to && to !== common) {
|
||||
const pathTo = [];
|
||||
while (true) {
|
||||
if (!to) {
|
||||
break;
|
||||
}
|
||||
if (to === common) {
|
||||
break;
|
||||
}
|
||||
const alternate = to.alternate;
|
||||
if (alternate !== null && alternate === common) {
|
||||
break;
|
||||
}
|
||||
pathTo.push(to);
|
||||
to = getParent(to);
|
||||
}
|
||||
var i;
|
||||
let i;
|
||||
for (i = 0; i < pathFrom.length; i++) {
|
||||
fn(pathFrom[i], 'bubbled', argFrom);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user