@samitouri / QOS-React-1 / commits / 90b2dd442c

Add additional fixtures for FragmentInstance text node support (#35631)

Stacked on https://github.com/facebook/react/pull/35630 - Adds test case for compareDocumentPosition, missing before and also extending to text nodes - Adds event handling fixture case for text - Adds getRootNode fixture case for text

Jack Pope committed Jan 28, 2026 at 14:55 UTC 90b2dd442cc05048b2a6ade5020c463ab0499eca
7 files changed +669 -48
fixtures/dom/src/components/fixtures/fragment-refs/CompareDocumentPositionCase.js new
+53
@@ -0,0 +1,53 @@
1 +import TestCase from '../../TestCase';
2 +import Fixture from '../../Fixture';
3 +import CompareDocumentPositionFragmentContainer from './CompareDocumentPositionFragmentContainer';
4 +
5 +const React = window.React;
6 +
7 +export default function CompareDocumentPositionCase() {
8 + return (
9 + <TestCase title="compareDocumentPosition">
10 + <TestCase.Steps>
11 + <li>Click the "Compare All Positions" button</li>
12 + </TestCase.Steps>
13 + <TestCase.ExpectedResult>
14 + The compareDocumentPosition method compares the position of the fragment
15 + relative to other elements in the DOM. The "Before Element" should be
16 + PRECEDING the fragment, and the "After Element" should be FOLLOWING.
17 + Elements inside the fragment should be CONTAINED_BY.
18 + </TestCase.ExpectedResult>
19 + <Fixture>
20 + <Fixture.Controls>
21 + <CompareDocumentPositionFragmentContainer>
22 + <div
23 + style={{
24 + padding: '10px',
25 + backgroundColor: 'lightblue',
26 + borderRadius: '4px',
27 + marginBottom: '8px',
28 + }}>
29 + First child element
30 + </div>
31 + <div
32 + style={{
33 + padding: '10px',
34 + backgroundColor: 'lightgreen',
35 + borderRadius: '4px',
36 + marginBottom: '8px',
37 + }}>
38 + Second child element
39 + </div>
40 + <div
41 + style={{
42 + padding: '10px',
43 + backgroundColor: 'lightpink',
44 + borderRadius: '4px',
45 + }}>
46 + Third child element
47 + </div>
48 + </CompareDocumentPositionFragmentContainer>
49 + </Fixture.Controls>
50 + </Fixture>
51 + </TestCase>
52 + );
53 +}
fixtures/dom/src/components/fixtures/fragment-refs/CompareDocumentPositionFragmentContainer.js new
+246
@@ -0,0 +1,246 @@
1 +const React = window.React;
2 +const {Fragment, useRef, useState} = React;
3 +
4 +const POSITION_FLAGS = {
5 + DISCONNECTED: 0x01,
6 + PRECEDING: 0x02,
7 + FOLLOWING: 0x04,
8 + CONTAINS: 0x08,
9 + CONTAINED_BY: 0x10,
10 + IMPLEMENTATION_SPECIFIC: 0x20,
11 +};
12 +
13 +function getPositionDescription(bitmask) {
14 + const flags = [];
15 + if (bitmask & POSITION_FLAGS.DISCONNECTED) flags.push('DISCONNECTED');
16 + if (bitmask & POSITION_FLAGS.PRECEDING) flags.push('PRECEDING');
17 + if (bitmask & POSITION_FLAGS.FOLLOWING) flags.push('FOLLOWING');
18 + if (bitmask & POSITION_FLAGS.CONTAINS) flags.push('CONTAINS');
19 + if (bitmask & POSITION_FLAGS.CONTAINED_BY) flags.push('CONTAINED_BY');
20 + if (bitmask & POSITION_FLAGS.IMPLEMENTATION_SPECIFIC)
21 + flags.push('IMPLEMENTATION_SPECIFIC');
22 + return flags.length > 0 ? flags.join(' | ') : 'SAME';
23 +}
24 +
25 +function ResultRow({label, result, color}) {
26 + if (!result) return null;
27 +
28 + return (
29 + <div
30 + style={{
31 + padding: '10px 14px',
32 + marginBottom: '8px',
33 + backgroundColor: '#f8f9fa',
34 + borderLeft: `4px solid ${color}`,
35 + borderRadius: '4px',
36 + }}>
37 + <div
38 + style={{
39 + fontWeight: 'bold',
40 + marginBottom: '6px',
41 + color: '#333',
42 + }}>
43 + {label}
44 + </div>
45 + <div
46 + style={{
47 + display: 'grid',
48 + gridTemplateColumns: 'auto 1fr',
49 + gap: '4px 12px',
50 + fontSize: '13px',
51 + fontFamily: 'monospace',
52 + }}>
53 + <span style={{color: '#666'}}>Raw value:</span>
54 + <span style={{color: '#333'}}>{result.raw}</span>
55 + <span style={{color: '#666'}}>Flags:</span>
56 + <span style={{color: color, fontWeight: 500}}>
57 + {getPositionDescription(result.raw)}
58 + </span>
59 + </div>
60 + </div>
61 + );
62 +}
63 +
64 +export default function CompareDocumentPositionFragmentContainer({children}) {
65 + const fragmentRef = useRef(null);
66 + const beforeRef = useRef(null);
67 + const afterRef = useRef(null);
68 + const insideRef = useRef(null);
69 + const [results, setResults] = useState(null);
70 +
71 + const compareAll = () => {
72 + const fragment = fragmentRef.current;
73 + const beforePos = fragment.compareDocumentPosition(beforeRef.current);
74 + const afterPos = fragment.compareDocumentPosition(afterRef.current);
75 + const insidePos = insideRef.current
76 + ? fragment.compareDocumentPosition(insideRef.current)
77 + : null;
78 +
79 + setResults({
80 + before: {raw: beforePos},
81 + after: {raw: afterPos},
82 + inside: insidePos !== null ? {raw: insidePos} : null,
83 + });
84 + };
85 +
86 + return (
87 + <Fragment>
88 + <div style={{marginBottom: '16px'}}>
89 + <button
90 + onClick={compareAll}
91 + style={{
92 + padding: '8px 16px',
93 + fontSize: '14px',
94 + fontWeight: 'bold',
95 + cursor: 'pointer',
96 + }}>
97 + Compare All Positions
98 + </button>
99 + {results && (
100 + <span style={{marginLeft: '12px', color: '#666'}}>
101 + Comparison complete
102 + </span>
103 + )}
104 + </div>
105 +
106 + <div style={{display: 'flex', gap: '24px'}}>
107 + <div style={{flex: '0 0 300px'}}>
108 + <div
109 + style={{
110 + padding: '16px',
111 + backgroundColor: '#f0f0f0',
112 + borderRadius: '8px',
113 + }}>
114 + <div
115 + ref={beforeRef}
116 + style={{
117 + padding: '12px',
118 + backgroundColor: '#d4edda',
119 + border: '2px solid #28a745',
120 + borderRadius: '4px',
121 + marginBottom: '12px',
122 + textAlign: 'center',
123 + fontWeight: 'bold',
124 + color: '#155724',
125 + }}>
126 + Before Element
127 + </div>
128 +
129 + <div
130 + style={{
131 + padding: '12px',
132 + backgroundColor: '#fff3cd',
133 + border: '2px dashed #ffc107',
134 + borderRadius: '4px',
135 + marginBottom: '12px',
136 + }}>
137 + <div
138 + style={{
139 + fontSize: '11px',
140 + color: '#856404',
141 + marginBottom: '8px',
142 + fontWeight: 'bold',
143 + }}>
144 + FRAGMENT
145 + </div>
146 + <div ref={insideRef}>
147 + <Fragment ref={fragmentRef}>{children}</Fragment>
148 + </div>
149 + </div>
150 +
151 + <div
152 + ref={afterRef}
153 + style={{
154 + padding: '12px',
155 + backgroundColor: '#f8d7da',
156 + border: '2px solid #dc3545',
157 + borderRadius: '4px',
158 + textAlign: 'center',
159 + fontWeight: 'bold',
160 + color: '#721c24',
161 + }}>
162 + After Element
163 + </div>
164 + </div>
165 + </div>
166 +
167 + <div style={{flex: 1}}>
168 + <div
169 + style={{
170 + fontSize: '14px',
171 + fontWeight: 'bold',
172 + marginBottom: '12px',
173 + color: '#333',
174 + }}>
175 + Comparison Results
176 + </div>
177 +
178 + {!results && (
179 + <div
180 + style={{
181 + padding: '20px',
182 + backgroundColor: '#f8f9fa',
183 + borderRadius: '4px',
184 + color: '#666',
185 + textAlign: 'center',
186 + }}>
187 + Click "Compare All Positions" to see results
188 + </div>
189 + )}
190 +
191 + {results && (
192 + <Fragment>
193 + <ResultRow
194 + label='vs "Before Element"'
195 + result={results.before}
196 + color="#28a745"
197 + />
198 + <ResultRow
199 + label='vs "After Element"'
200 + result={results.after}
201 + color="#dc3545"
202 + />
203 + {results.inside && (
204 + <ResultRow
205 + label='vs "Inside Element"'
206 + result={results.inside}
207 + color="#ffc107"
208 + />
209 + )}
210 +
211 + <div
212 + style={{
213 + marginTop: '16px',
214 + padding: '12px',
215 + backgroundColor: '#e7f3ff',
216 + borderRadius: '4px',
217 + fontSize: '12px',
218 + color: '#0c5460',
219 + }}>
220 + <strong>Flag Reference:</strong>
221 + <div
222 + style={{
223 + marginTop: '8px',
224 + display: 'grid',
225 + gridTemplateColumns: 'auto 1fr',
226 + gap: '2px 12px',
227 + }}>
228 + <code>0x01</code>
229 + <span>DISCONNECTED</span>
230 + <code>0x02</code>
231 + <span>PRECEDING (other is before fragment)</span>
232 + <code>0x04</code>
233 + <span>FOLLOWING (other is after fragment)</span>
234 + <code>0x08</code>
235 + <span>CONTAINS (other contains fragment)</span>
236 + <code>0x10</code>
237 + <span>CONTAINED_BY (other is inside fragment)</span>
238 + </div>
239 + </div>
240 + </Fragment>
241 + )}
242 + </div>
243 + </div>
244 + </Fragment>
245 + );
246 +}
fixtures/dom/src/components/fixtures/fragment-refs/EventFragmentContainer.js new
+112
@@ -0,0 +1,112 @@
1 +const React = window.React;
2 +const {Fragment, useRef, useState} = React;
3 +
4 +export default function EventFragmentContainer({children}) {
5 + const fragmentRef = useRef(null);
6 + const [eventLog, setEventLog] = useState([]);
7 + const [listenerAdded, setListenerAdded] = useState(false);
8 + const [bubblesState, setBubblesState] = useState(true);
9 +
10 + const logEvent = message => {
11 + setEventLog(prev => [...prev, message]);
12 + };
13 +
14 + const fragmentClickHandler = () => {
15 + logEvent('Fragment event listener fired');
16 + };
17 +
18 + const addListener = () => {
19 + fragmentRef.current.addEventListener('click', fragmentClickHandler);
20 + setListenerAdded(true);
21 + logEvent('Added click listener to fragment');
22 + };
23 +
24 + const removeListener = () => {
25 + fragmentRef.current.removeEventListener('click', fragmentClickHandler);
26 + setListenerAdded(false);
27 + logEvent('Removed click listener from fragment');
28 + };
29 +
30 + const dispatchClick = () => {
31 + fragmentRef.current.dispatchEvent(
32 + new MouseEvent('click', {bubbles: bubblesState})
33 + );
34 + logEvent(`Dispatched click event (bubbles: ${bubblesState})`);
35 + };
36 +
37 + const clearLog = () => {
38 + setEventLog([]);
39 + };
40 +
41 + return (
42 + <Fragment>
43 + <div
44 + style={{
45 + marginBottom: '16px',
46 + display: 'flex',
47 + gap: '8px',
48 + flexWrap: 'wrap',
49 + alignItems: 'center',
50 + }}>
51 + <select
52 + value={bubblesState ? 'true' : 'false'}
53 + onChange={e => setBubblesState(e.target.value === 'true')}
54 + style={{padding: '6px 10px'}}>
55 + <option value="true">Bubbles: true</option>
56 + <option value="false">Bubbles: false</option>
57 + </select>
58 + <button onClick={dispatchClick} style={{padding: '6px 12px'}}>
59 + Dispatch click event
60 + </button>
61 + <button
62 + onClick={addListener}
63 + disabled={listenerAdded}
64 + style={{padding: '6px 12px'}}>
65 + Add event listener
66 + </button>
67 + <button
68 + onClick={removeListener}
69 + disabled={!listenerAdded}
70 + style={{padding: '6px 12px'}}>
71 + Remove event listener
72 + </button>
73 + <button onClick={clearLog} style={{padding: '6px 12px'}}>
74 + Clear log
75 + </button>
76 + </div>
77 +
78 + <div
79 + onClick={() => logEvent('Parent div clicked')}
80 + style={{
81 + padding: '12px',
82 + border: '1px dashed #ccc',
83 + borderRadius: '4px',
84 + backgroundColor: '#fff',
85 + }}>
86 + <Fragment ref={fragmentRef}>{children}</Fragment>
87 + </div>
88 +
89 + {eventLog.length > 0 && (
90 + <div
91 + style={{
92 + marginTop: '12px',
93 + padding: '10px',
94 + backgroundColor: '#f5f5f5',
95 + border: '1px solid #ddd',
96 + borderRadius: '4px',
97 + maxHeight: '150px',
98 + overflow: 'auto',
99 + fontFamily: 'monospace',
100 + fontSize: '13px',
101 + }}>
102 + <strong>Event Log:</strong>
103 + <ul style={{margin: '5px 0', paddingLeft: '20px'}}>
104 + {eventLog.map((msg, i) => (
105 + <li key={i}>{msg}</li>
106 + ))}
107 + </ul>
108 + </div>
109 + )}
110 + </Fragment>
111 + );
112 +}
fixtures/dom/src/components/fixtures/fragment-refs/EventListenerCase.js
+25 -47
@@ -1,46 +1,35 @@
1 import TestCase from '../../TestCase';
2 import Fixture from '../../Fixture';
3 +import EventFragmentContainer from './EventFragmentContainer';
4
5 const React = window.React;
5 -const {Fragment, useEffect, useRef, useState} = React;
6 +const {useState} = React;
7
8 function WrapperComponent(props) {
9 return props.children;
10 }
11
11 -function handler(e) {
12 - const text = e.currentTarget.innerText;
13 - alert('You clicked: ' + text);
14 -}
15 -
12 export default function EventListenerCase() {
17 - const fragmentRef = useRef(null);
13 const [extraChildCount, setExtraChildCount] = useState(0);
14
20 - useEffect(() => {
21 - fragmentRef.current.addEventListener('click', handler);
22 -
23 - const lastFragmentRefValue = fragmentRef.current;
24 - return () => {
25 - lastFragmentRefValue.removeEventListener('click', handler);
26 - };
27 - });
28 -
15 return (
16 <TestCase title="Event Registration">
17 <TestCase.Steps>
32 - <li>Click one of the children, observe the alert</li>
33 - <li>Add a new child, click it, observe the alert</li>
34 - <li>Remove the event listeners, click a child, observe no alert</li>
35 - <li>Add the event listeners back, click a child, observe the alert</li>
18 + <li>
19 + Click "Add event listener" to attach a click handler to the fragment
20 + </li>
21 + <li>Click "Dispatch click event" to dispatch a click event</li>
22 + <li>Observe the event log showing the event fired</li>
23 + <li>Add a new child, dispatch again to see it still works</li>
24 + <li>
25 + Click "Remove event listener" and dispatch again to see no event fires
26 + </li>
27 </TestCase.Steps>
28
29 <TestCase.ExpectedResult>
30 <p>
31 Fragment refs can manage event listeners on the first level of host
41 - children. This page loads with an effect that sets up click event
42 - hanndlers on each child card. Clicking on a card will show an alert
43 - with the card's text.
32 + children. The event log shows when events are dispatched and handled.
33 </p>
34 <p>
35 New child nodes will also have event listeners applied. Removed nodes
@@ -50,28 +39,17 @@ export default function EventListenerCase() {
39
40 <Fixture>
41 <Fixture.Controls>
53 - <div>Target count: {extraChildCount + 3}</div>
54 - <button
55 - onClick={() => {
56 - setExtraChildCount(prev => prev + 1);
57 - }}>
58 - Add Child
59 - </button>
60 - <button
61 - onClick={() => {
62 - fragmentRef.current.addEventListener('click', handler);
63 - }}>
64 - Add click event listeners
65 - </button>
66 - <button
67 - onClick={() => {
68 - fragmentRef.current.removeEventListener('click', handler);
69 - }}>
70 - Remove click event listeners
71 - </button>
72 - </Fixture.Controls>
73 - <div className="card-container">
74 - <Fragment ref={fragmentRef}>
42 + <div style={{marginBottom: '10px'}}>
43 + Target count: {extraChildCount + 3}
44 + <button
45 + onClick={() => {
46 + setExtraChildCount(prev => prev + 1);
47 + }}
48 + style={{marginLeft: '10px'}}>
49 + Add Child
50 + </button>
51 + </div>
52 + <EventFragmentContainer>
53 <div className="card" id="child-a">
54 Child A
55 </div>
@@ -88,8 +66,8 @@ export default function EventListenerCase() {
66 </div>
67 ))}
68 </WrapperComponent>
91 - </Fragment>
92 - </div>
69 + </EventFragmentContainer>
70 + </Fixture.Controls>
71 </Fixture>
72 </TestCase>
73 );
fixtures/dom/src/components/fixtures/fragment-refs/GetRootNodeFragmentContainer.js new
+79
@@ -0,0 +1,79 @@
1 +const React = window.React;
2 +const {Fragment, useRef, useState} = React;
3 +
4 +export default function GetRootNodeFragmentContainer({children}) {
5 + const fragmentRef = useRef(null);
6 + const [rootNodeInfo, setRootNodeInfo] = useState(null);
7 +
8 + const getRootNodeInfo = () => {
9 + const rootNode = fragmentRef.current.getRootNode();
10 + setRootNodeInfo({
11 + nodeName: rootNode.nodeName,
12 + nodeType: rootNode.nodeType,
13 + nodeTypeLabel: getNodeTypeLabel(rootNode.nodeType),
14 + isDocument: rootNode === document,
15 + });
16 + };
17 +
18 + const getNodeTypeLabel = nodeType => {
19 + const types = {
20 + 1: 'ELEMENT_NODE',
21 + 3: 'TEXT_NODE',
22 + 9: 'DOCUMENT_NODE',
23 + 11: 'DOCUMENT_FRAGMENT_NODE',
24 + };
25 + return types[nodeType] || `UNKNOWN (${nodeType})`;
26 + };
27 +
28 + return (
29 + <Fragment>
30 + <div style={{marginBottom: '16px'}}>
31 + <button
32 + onClick={getRootNodeInfo}
33 + style={{
34 + padding: '8px 16px',
35 + fontSize: '14px',
36 + fontWeight: 'bold',
37 + cursor: 'pointer',
38 + }}>
39 + Get Root Node
40 + </button>
41 + </div>
42 +
43 + {rootNodeInfo && (
44 + <div
45 + style={{
46 + marginBottom: '16px',
47 + padding: '12px',
48 + backgroundColor: '#e8f4e8',
49 + border: '1px solid #9c9',
50 + borderRadius: '4px',
51 + fontFamily: 'monospace',
52 + fontSize: '13px',
53 + }}>
54 + <div style={{marginBottom: '4px'}}>
55 + <strong>Node Name:</strong> {rootNodeInfo.nodeName}
56 + </div>
57 + <div style={{marginBottom: '4px'}}>
58 + <strong>Node Type:</strong> {rootNodeInfo.nodeType} (
59 + {rootNodeInfo.nodeTypeLabel})
60 + </div>
61 + <div>
62 + <strong>Is Document:</strong>{' '}
63 + {rootNodeInfo.isDocument ? 'Yes' : 'No'}
64 + </div>
65 + </div>
66 + )}
67 +
68 + <div
69 + style={{
70 + padding: '12px',
71 + border: '1px dashed #ccc',
72 + borderRadius: '4px',
73 + backgroundColor: '#fff',
74 + }}>
75 + <Fragment ref={fragmentRef}>{children}</Fragment>
76 + </div>
77 + </Fragment>
78 + );
79 +}
fixtures/dom/src/components/fixtures/fragment-refs/TextNodesCase.js
+152 -1
@@ -1,6 +1,9 @@
1 import TestCase from '../../TestCase';
2 import Fixture from '../../Fixture';
3 import PrintRectsFragmentContainer from './PrintRectsFragmentContainer';
4 +import CompareDocumentPositionFragmentContainer from './CompareDocumentPositionFragmentContainer';
5 +import EventFragmentContainer from './EventFragmentContainer';
6 +import GetRootNodeFragmentContainer from './GetRootNodeFragmentContainer';
7
8 const React = window.React;
9 const {Fragment, useRef, useState} = React;
@@ -242,6 +245,28 @@ function ScrollIntoViewMixed() {
245 );
246 }
247
248 +function CompareDocumentPositionTextNodes() {
249 + return (
250 + <TestCase title="compareDocumentPosition - Text Only">
251 + <TestCase.Steps>
252 + <li>Click the "Compare All Positions" button</li>
253 + </TestCase.Steps>
254 + <TestCase.ExpectedResult>
255 + compareDocumentPosition should work correctly even when the fragment
256 + contains only text nodes. The "Before" element should be PRECEDING the
257 + fragment, and the "After" element should be FOLLOWING.
258 + </TestCase.ExpectedResult>
259 + <Fixture>
260 + <Fixture.Controls>
261 + <CompareDocumentPositionFragmentContainer>
262 + This is text-only content inside the fragment.
263 + </CompareDocumentPositionFragmentContainer>
264 + </Fixture.Controls>
265 + </Fixture>
266 + </TestCase>
267 + );
268 +}
269 +
270 function ObserveTextOnlyWarning() {
271 const fragmentRef = useRef(null);
272 const [message, setMessage] = useState('');
@@ -287,6 +312,126 @@ function ObserveTextOnlyWarning() {
312 );
313 }
314
315 +function EventTextOnly() {
316 + return (
317 + <TestCase title="Event Operations - Text Only">
318 + <TestCase.Steps>
319 + <li>
320 + Click "Add event listener" to attach a click handler to the fragment
321 + </li>
322 + <li>Click "Dispatch click event" to dispatch a click event</li>
323 + <li>Observe that the fragment's event listener fires</li>
324 + <li>Click "Remove event listener" and dispatch again</li>
325 + </TestCase.Steps>
326 + <TestCase.ExpectedResult>
327 + Event operations (addEventListener, removeEventListener, dispatchEvent)
328 + work on fragments with text-only content. The event is dispatched on the
329 + fragment's parent element since text nodes cannot be event targets.
330 + </TestCase.ExpectedResult>
331 + <Fixture>
332 + <Fixture.Controls>
333 + <EventFragmentContainer>
334 + This fragment contains only text. Events are handled via the parent.
335 + </EventFragmentContainer>
336 + </Fixture.Controls>
337 + </Fixture>
338 + </TestCase>
339 + );
340 +}
341 +
342 +function EventMixed() {
343 + return (
344 + <TestCase title="Event Operations - Mixed Content">
345 + <TestCase.Steps>
346 + <li>
347 + Click "Add event listener" to attach a click handler to the fragment
348 + </li>
349 + <li>Click "Dispatch click event" to dispatch a click event</li>
350 + <li>Observe that the fragment's event listener fires</li>
351 + <li>Click directly on the element or text content to see bubbling</li>
352 + </TestCase.Steps>
353 + <TestCase.ExpectedResult>
354 + Event operations work on fragments with mixed text and element content.
355 + dispatchEvent forwards to the parent element. Clicks on child elements
356 + or text bubble up through the DOM as normal.
357 + </TestCase.ExpectedResult>
358 + <Fixture>
359 + <Fixture.Controls>
360 + <EventFragmentContainer>
361 + Text node before element.
362 + <span
363 + style={{
364 + display: 'inline-block',
365 + padding: '5px 10px',
366 + margin: '0 5px',
367 + backgroundColor: 'lightblue',
368 + border: '1px solid blue',
369 + }}>
370 + Element
371 + </span>
372 + Text node after element.
373 + </EventFragmentContainer>
374 + </Fixture.Controls>
375 + </Fixture>
376 + </TestCase>
377 + );
378 +}
379 +
380 +function GetRootNodeTextOnly() {
381 + return (
382 + <TestCase title="getRootNode - Text Only">
383 + <TestCase.Steps>
384 + <li>Click the "Get Root Node" button</li>
385 + </TestCase.Steps>
386 + <TestCase.ExpectedResult>
387 + getRootNode should return the root of the DOM tree containing the
388 + fragment's text content. For a fragment in the main document, this
389 + should return the Document node.
390 + </TestCase.ExpectedResult>
391 + <Fixture>
392 + <Fixture.Controls>
393 + <GetRootNodeFragmentContainer>
394 + This fragment contains only text. getRootNode returns the document.
395 + </GetRootNodeFragmentContainer>
396 + </Fixture.Controls>
397 + </Fixture>
398 + </TestCase>
399 + );
400 +}
401 +
402 +function GetRootNodeMixed() {
403 + return (
404 + <TestCase title="getRootNode - Mixed Content">
405 + <TestCase.Steps>
406 + <li>Click the "Get Root Node" button</li>
407 + </TestCase.Steps>
408 + <TestCase.ExpectedResult>
409 + getRootNode should return the root of the DOM tree for fragments with
410 + mixed text and element content. The result is the same whether checking
411 + from text nodes or element nodes within the fragment.
412 + </TestCase.ExpectedResult>
413 + <Fixture>
414 + <Fixture.Controls>
415 + <GetRootNodeFragmentContainer>
416 + Text before element.
417 + <span
418 + style={{
419 + display: 'inline-block',
420 + padding: '5px 10px',
421 + margin: '0 5px',
422 + backgroundColor: 'lightyellow',
423 + border: '1px solid #cc0',
424 + }}>
425 + Element
426 + </span>
427 + Text after element.
428 + </GetRootNodeFragmentContainer>
429 + </Fixture.Controls>
430 + </Fixture>
431 + </TestCase>
432 + );
433 +}
434 +
435 export default function TextNodesCase() {
436 return (
437 <TestCase title="Text Node Support">
@@ -297,7 +442,8 @@ export default function TextNodesCase() {
442 </p>
443 <p>
444 <strong>Supported:</strong> getClientRects, compareDocumentPosition,
300 - scrollIntoView
445 + scrollIntoView, getRootNode, addEventListener, removeEventListener,
446 + dispatchEvent
447 </p>
448 <p>
449 <strong>No-op (silent):</strong> focus, focusLast (text nodes cannot
@@ -310,10 +456,15 @@ export default function TextNodesCase() {
456 </TestCase.ExpectedResult>
457 <GetClientRectsTextOnly />
458 <GetClientRectsMixed />
459 + <CompareDocumentPositionTextNodes />
460 <FocusTextOnlyNoop />
461 <ScrollIntoViewTextOnly />
462 <ScrollIntoViewMixed />
463 <ObserveTextOnlyWarning />
464 + <EventTextOnly />
465 + <EventMixed />
466 + <GetRootNodeTextOnly />
467 + <GetRootNodeMixed />
468 </TestCase>
469 );
470 }
fixtures/dom/src/components/fixtures/fragment-refs/index.js
+2
@@ -5,6 +5,7 @@ import IntersectionObserverCase from './IntersectionObserverCase';
5 import ResizeObserverCase from './ResizeObserverCase';
6 import FocusCase from './FocusCase';
7 import GetClientRectsCase from './GetClientRectsCase';
8 +import CompareDocumentPositionCase from './CompareDocumentPositionCase';
9 import ScrollIntoViewCase from './ScrollIntoViewCase';
10 import TextNodesCase from './TextNodesCase';
11
@@ -19,6 +20,7 @@ export default function FragmentRefsPage() {
20 <ResizeObserverCase />
21 <FocusCase />
22 <GetClientRectsCase />
23 + <CompareDocumentPositionCase />
24 <ScrollIntoViewCase />
25 <TextNodesCase />
26 </FixtureSet>