Skip to content

Commit cc19468

Browse files
committed
Add dispatchEvent to fragment instances
1 parent 1fd3b75 commit cc19468

File tree

4 files changed

+531
-347
lines changed

4 files changed

+531
-347
lines changed
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import TestCase from '../../TestCase';
2+
import Fixture from '../../Fixture';
3+
4+
const React = window.React;
5+
const {Fragment, useRef, useState} = React;
6+
7+
function WrapperComponent(props) {
8+
return props.children;
9+
}
10+
11+
function handler(e) {
12+
const text = e.currentTarget.innerText;
13+
alert('You clicked: ' + text);
14+
}
15+
16+
const initialState = {
17+
child: false,
18+
parent: false,
19+
grandparent: false,
20+
};
21+
22+
export default function EventListenerCase() {
23+
const fragmentRef = useRef(null);
24+
const [clickedState, setClickedState] = useState({...initialState});
25+
26+
return (
27+
<TestCase title="Event Dispatch">
28+
<TestCase.Steps>
29+
<li>
30+
Each div has regular click handlers, you can click each one to observe
31+
the status changing
32+
</li>
33+
<li>Clear the clicked state</li>
34+
<li>
35+
Click the "Dispatch click event" button to dispatch a click event on
36+
the Fragment
37+
</li>
38+
</TestCase.Steps>
39+
40+
<TestCase.ExpectedResult>
41+
Dispatching an event on a Fragment will forward the dispatch to its
42+
parent. You can observe when dispatching that the parent handler is
43+
called in additional to bubbling from there. A delay is added to make
44+
the bubbling more clear.
45+
</TestCase.ExpectedResult>
46+
47+
<Fixture>
48+
<Fixture.Controls>
49+
<button
50+
onClick={() => {
51+
fragmentRef.current.dispatchEvent(
52+
new MouseEvent('click', {bubbles: true})
53+
);
54+
}}>
55+
Dispatch click event
56+
</button>
57+
<button
58+
onClick={() => {
59+
setClickedState({...initialState});
60+
}}>
61+
Reset clicked state
62+
</button>
63+
</Fixture.Controls>
64+
<div
65+
onClick={() => {
66+
setTimeout(() => {
67+
setClickedState(prev => ({...prev, grandparent: true}));
68+
}, 200);
69+
}}
70+
className="card">
71+
Fragment grandparent - clicked:{' '}
72+
{clickedState.grandparent ? 'true' : 'false'}
73+
<div
74+
onClick={() => {
75+
setTimeout(() => {
76+
setClickedState(prev => ({...prev, parent: true}));
77+
}, 100);
78+
}}
79+
className="card">
80+
Fragment parent - clicked: {clickedState.parent ? 'true' : 'false'}
81+
<Fragment ref={fragmentRef}>
82+
<div
83+
className="card"
84+
onClick={() => {
85+
setClickedState(prev => ({...prev, child: true}));
86+
}}>
87+
Fragment child - clicked:{' '}
88+
{clickedState.child ? 'true' : 'false'}
89+
</div>
90+
</Fragment>
91+
</div>
92+
</div>
93+
</Fixture>
94+
</TestCase>
95+
);
96+
}

fixtures/dom/src/components/fixtures/fragment-refs/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import FixtureSet from '../../FixtureSet';
22
import EventListenerCase from './EventListenerCase';
3+
import EventDispatchCase from './EventDispatchCase';
34
import IntersectionObserverCase from './IntersectionObserverCase';
45
import ResizeObserverCase from './ResizeObserverCase';
56
import FocusCase from './FocusCase';
@@ -11,6 +12,7 @@ export default function FragmentRefsPage() {
1112
return (
1213
<FixtureSet title="Fragment Refs">
1314
<EventListenerCase />
15+
<EventDispatchCase />
1416
<IntersectionObserverCase />
1517
<ResizeObserverCase />
1618
<FocusCase />

packages/react-dom-bindings/src/client/ReactFiberConfigDOM.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2236,6 +2236,7 @@ export type FragmentInstanceType = {
22362236
listener: EventListener,
22372237
optionsOrUseCapture?: EventListenerOptionsOrUseCapture,
22382238
): void,
2239+
dispatchEvent(event: Event): boolean,
22392240
focus(focusOptions?: FocusOptions): void,
22402241
focusLast(focusOptions?: FocusOptions): void,
22412242
blur(): void,
@@ -2331,6 +2332,23 @@ function removeEventListenerFromChild(
23312332
return false;
23322333
}
23332334
// $FlowFixMe[prop-missing]
2335+
FragmentInstance.prototype.dispatchEvent = function (
2336+
this: FragmentInstanceType,
2337+
event: Event,
2338+
): boolean {
2339+
const parentHostInstance = getFragmentParentHostInstance(this._fragmentFiber);
2340+
if (parentHostInstance === null) {
2341+
if (__DEV__) {
2342+
console.error(
2343+
'You are attempting to dispatch an event on a disconnected ' +
2344+
'FragmentInstance. No event was dispatched.',
2345+
);
2346+
}
2347+
return true;
2348+
}
2349+
return parentHostInstance.dispatchEvent(event);
2350+
};
2351+
// $FlowFixMe[prop-missing]
23342352
FragmentInstance.prototype.focus = function (
23352353
this: FragmentInstanceType,
23362354
focusOptions?: FocusOptions,

0 commit comments

Comments
 (0)