Skip to content

Commit

Permalink
Rename Chunks API to Blocks (facebook#18086)
Browse files Browse the repository at this point in the history
Sounds like this is the name we're going with. This also helps us
distinguish it from other "chunking" implementation details.

Add Modern Event System fork

Fix

FIX

FIX

Refine comment

Support handling of listening to comment nodes

Update error codes

FXI

FXI

Add test cases and revise traversal

Fix

Refactor

twak

Rename

Address traversal of comment nodes
  • Loading branch information
sebmarkbage authored and trueadm committed Feb 21, 2020
1 parent 8b596e0 commit 664213e
Show file tree
Hide file tree
Showing 35 changed files with 1,552 additions and 245 deletions.
4 changes: 2 additions & 2 deletions packages/react-debug-tools/src/ReactDebugHooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
SimpleMemoComponent,
ContextProvider,
ForwardRef,
Chunk,
Block,
} from 'shared/ReactWorkTags';

type CurrentDispatcherRef = typeof ReactSharedInternals.ReactCurrentDispatcher;
Expand Down Expand Up @@ -628,7 +628,7 @@ export function inspectHooksOfFiber(
fiber.tag !== FunctionComponent &&
fiber.tag !== SimpleMemoComponent &&
fiber.tag !== ForwardRef &&
fiber.tag !== Chunk
fiber.tag !== Block
) {
throw new Error(
'Unknown Fiber. Needs to be a function component to inspect hooks.',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ let ReactDOMComponentTree;
let listenToEvent;
let ReactDOMEventListener;
let ReactTestUtils;
let ReactFeatureFlags;

let idCallOrder;
const recordID = function(id) {
Expand Down Expand Up @@ -60,13 +61,20 @@ describe('ReactBrowserEventEmitter', () => {
jest.resetModules();
LISTENER.mockClear();

ReactFeatureFlags = require('shared/ReactFeatureFlags');
EventPluginGetListener = require('legacy-events/getListener').default;
EventPluginRegistry = require('legacy-events/EventPluginRegistry');
React = require('react');
ReactDOM = require('react-dom');
ReactDOMComponentTree = require('../client/ReactDOMComponentTree');
listenToEvent = require('../events/DOMLegacyEventPluginSystem')
.legacyListenToEvent;
if (ReactFeatureFlags.enableModernEventSystem) {
listenToEvent = require('../events/DOMModernPluginEventSystem')
.listenToEvent;
} else {
listenToEvent = require('../events/DOMLegacyEventPluginSystem')
.legacyListenToEvent;
}

ReactDOMEventListener = require('../events/ReactDOMEventListener');
ReactTestUtils = require('react-dom/test-utils');

Expand Down
95 changes: 64 additions & 31 deletions packages/react-dom/src/__tests__/ReactDOMEventListener-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,41 @@
describe('ReactDOMEventListener', () => {
let React;
let ReactDOM;
let ReactFeatureFlags = require('shared/ReactFeatureFlags');

beforeEach(() => {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
});

it('should dispatch events from outside React tree', () => {
const mock = jest.fn();
// We attached events to roots with the modern system,
// so this test is no longer valid.
if (!ReactFeatureFlags.enableModernEventSystem) {
it('should dispatch events from outside React tree', () => {
const mock = jest.fn();

const container = document.createElement('div');
const node = ReactDOM.render(<div onMouseEnter={mock} />, container);
const otherNode = document.createElement('h1');
document.body.appendChild(container);
document.body.appendChild(otherNode);
const container = document.createElement('div');
const node = ReactDOM.render(<div onMouseEnter={mock} />, container);
const otherNode = document.createElement('h1');
document.body.appendChild(container);
document.body.appendChild(otherNode);

try {
otherNode.dispatchEvent(
new MouseEvent('mouseout', {
bubbles: true,
cancelable: true,
relatedTarget: node,
}),
);
expect(mock).toBeCalled();
} finally {
document.body.removeChild(container);
document.body.removeChild(otherNode);
}
});
try {
otherNode.dispatchEvent(
new MouseEvent('mouseout', {
bubbles: true,
cancelable: true,
relatedTarget: node,
}),
);
expect(mock).toBeCalled();
} finally {
document.body.removeChild(container);
document.body.removeChild(otherNode);
}
});
}

describe('Propagation', () => {
it('should propagate events one level down', () => {
Expand Down Expand Up @@ -189,9 +194,25 @@ describe('ReactDOMEventListener', () => {
// 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');
if (ReactFeatureFlags.enableModernEventSystem) {
// As we have two roots, it means we have two event listeners.
// This also means we enter the event batching phase twice,
// flushing the child to be 1.

// We don't have any good way of knowing if another event will
// occur because another event handler might invoke
// stopPropagation() along the way. After discussions internally
// with Sebastian, it seems that for now over-flushing should
// be fine, especially as the new event system is a breaking
// change anyway. We can maybe revisit this later as part of
// the work to refine this in the scheduler (maybe by leveraging
// isInputPending?).
expect(mock.mock.calls[1][0]).toBe('1');
} else {
// 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');
} finally {
Expand Down Expand Up @@ -362,13 +383,25 @@ describe('ReactDOMEventListener', () => {
bubbles: false,
}),
);
// Historically, we happened to not support onLoadStart
// on <img>, and this test documents that lack of support.
// If we decide to support it in the future, we should change
// this line to expect 1 call. Note that fixing this would
// be simple but would require attaching a handler to each
// <img>. So far nobody asked us for it.
expect(handleImgLoadStart).toHaveBeenCalledTimes(0);
if (ReactFeatureFlags.enableModernEventSystem) {
// As of the modern event system refactor, we now support
// this on <img>. The reason for this, is because we now
// attach all media events to the "root" or "portal" in the
// capture phase, rather than the bubble phase. This allows
// us to assign less event listeners to individual elements,
// which also nicely allows us to support more without needing
// to add more individual code paths to support various
// events that do not bubble.
expect(handleImgLoadStart).toHaveBeenCalledTimes(1);
} else {
// Historically, we happened to not support onLoadStart
// on <img>, and this test documents that lack of support.
// If we decide to support it in the future, we should change
// this line to expect 1 call. Note that fixing this would
// be simple but would require attaching a handler to each
// <img>. So far nobody asked us for it.
expect(handleImgLoadStart).toHaveBeenCalledTimes(0);
}

videoRef.current.dispatchEvent(
new ProgressEvent('loadstart', {
Expand Down
119 changes: 84 additions & 35 deletions packages/react-dom/src/__tests__/ReactTreeTraversal-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

let React;
let ReactDOM;
let ReactFeatureFlags = require('shared/ReactFeatureFlags');

const ChildComponent = ({id, eventHandler}) => (
<div
Expand Down Expand Up @@ -203,41 +204,89 @@ describe('ReactTreeTraversal', () => {
expect(mockFn.mock.calls).toEqual(expectedCalls);
});

it('should enter from the window', () => {
const enterNode = document.getElementById('P_P1_C1__DIV');

const expectedCalls = [
['P', 'mouseenter'],
['P_P1', 'mouseenter'],
['P_P1_C1__DIV', 'mouseenter'],
];

outerNode1.dispatchEvent(
new MouseEvent('mouseout', {
bubbles: true,
cancelable: true,
relatedTarget: enterNode,
}),
);

expect(mockFn.mock.calls).toEqual(expectedCalls);
});

it('should enter from the window to the shallowest', () => {
const enterNode = document.getElementById('P');

const expectedCalls = [['P', 'mouseenter']];

outerNode1.dispatchEvent(
new MouseEvent('mouseout', {
bubbles: true,
cancelable: true,
relatedTarget: enterNode,
}),
);

expect(mockFn.mock.calls).toEqual(expectedCalls);
});
// This will not work with the modern event system that
// attaches event listeners to roots as the event below
// is being triggered on a node that React does not listen
// to any more. Instead we should fire mouseover.
if (ReactFeatureFlags.enableModernEventSystem) {
it('should enter from the window', () => {
const enterNode = document.getElementById('P_P1_C1__DIV');

const expectedCalls = [
['P', 'mouseenter'],
['P_P1', 'mouseenter'],
['P_P1_C1__DIV', 'mouseenter'],
];

enterNode.dispatchEvent(
new MouseEvent('mouseover', {
bubbles: true,
cancelable: true,
relatedTarget: outerNode1,
}),
);

expect(mockFn.mock.calls).toEqual(expectedCalls);
});
} else {
it('should enter from the window', () => {
const enterNode = document.getElementById('P_P1_C1__DIV');

const expectedCalls = [
['P', 'mouseenter'],
['P_P1', 'mouseenter'],
['P_P1_C1__DIV', 'mouseenter'],
];

outerNode1.dispatchEvent(
new MouseEvent('mouseout', {
bubbles: true,
cancelable: true,
relatedTarget: enterNode,
}),
);

expect(mockFn.mock.calls).toEqual(expectedCalls);
});
}

// This will not work with the modern event system that
// attaches event listeners to roots as the event below
// is being triggered on a node that React does not listen
// to any more. Instead we should fire mouseover.
if (ReactFeatureFlags.enableModernEventSystem) {
it('should enter from the window to the shallowest', () => {
const enterNode = document.getElementById('P');

const expectedCalls = [['P', 'mouseenter']];

enterNode.dispatchEvent(
new MouseEvent('mouseover', {
bubbles: true,
cancelable: true,
relatedTarget: outerNode1,
}),
);

expect(mockFn.mock.calls).toEqual(expectedCalls);
});
} else {
it('should enter from the window to the shallowest', () => {
const enterNode = document.getElementById('P');

const expectedCalls = [['P', 'mouseenter']];

outerNode1.dispatchEvent(
new MouseEvent('mouseout', {
bubbles: true,
cancelable: true,
relatedTarget: enterNode,
}),
);

expect(mockFn.mock.calls).toEqual(expectedCalls);
});
}

it('should leave to the window', () => {
const leaveNode = document.getElementById('P_P1_C1__DIV');
Expand Down
Loading

0 comments on commit 664213e

Please sign in to comment.