From b026fdf467b13cb44cd7d2a0b39c651dfdee3ac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rub=C3=A9n=20Norte?= Date: Wed, 29 Jan 2025 09:46:25 -0800 Subject: [PATCH] Implement additional traversal methods on ReactNativeDocument (#49013) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/49013 Changelog: [internal] Adds `Document`-specific traversal methods to `ReactNativeDocument`: * `childElementCount` * `children` * `firstElementChild` * `lastElementChild` Differential Revision: D67693032 --- .../__snapshots__/public-api-test.js.snap | 4 ++++ .../src/private/setup/setUpDOM.js | 5 ++++ .../webapis/dom/nodes/ReactNativeDocument.js | 20 ++++++++++++++++ .../__tests__/ReactNativeDocument-itest.js | 24 +++++++++++++++++++ 4 files changed, 53 insertions(+) diff --git a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap index 406818b0ce01ea..d2fc150229b07c 100644 --- a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap +++ b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap @@ -11323,7 +11323,11 @@ exports[`public API should not change unintentionally src/private/webapis/dom/no rootTag: RootTag, instanceHandle: ReactNativeDocumentInstanceHandle ): void; + get childElementCount(): number; + get children(): HTMLCollection; get documentElement(): ReactNativeElement; + get firstElementChild(): ReadOnlyElement | null; + get lastElementChild(): ReadOnlyElement | null; get nodeName(): string; get nodeType(): number; get nodeValue(): null; diff --git a/packages/react-native/src/private/setup/setUpDOM.js b/packages/react-native/src/private/setup/setUpDOM.js index f815c86c4e842f..f4fc5552239157 100644 --- a/packages/react-native/src/private/setup/setUpDOM.js +++ b/packages/react-native/src/private/setup/setUpDOM.js @@ -29,6 +29,11 @@ export default function setUpDOM() { () => require('../webapis/dom/geometry/DOMRectReadOnly').default, ); + polyfillGlobal( + 'HTMLCollection', + () => require('../webapis/dom/oldstylecollections/HTMLCollection').default, + ); + polyfillGlobal( 'NodeList', () => require('../webapis/dom/oldstylecollections/NodeList').default, diff --git a/packages/react-native/src/private/webapis/dom/nodes/ReactNativeDocument.js b/packages/react-native/src/private/webapis/dom/nodes/ReactNativeDocument.js index 97ed4dfe2c0667..09e227be4e72b2 100644 --- a/packages/react-native/src/private/webapis/dom/nodes/ReactNativeDocument.js +++ b/packages/react-native/src/private/webapis/dom/nodes/ReactNativeDocument.js @@ -12,8 +12,11 @@ import type {RootTag} from '../../../../../Libraries/ReactNative/RootTag'; import type {ViewConfig} from '../../../../../Libraries/Renderer/shims/ReactNativeTypes'; +import type HTMLCollection from '../oldstylecollections/HTMLCollection'; import type {ReactNativeDocumentInstanceHandle} from './internals/ReactNativeDocumentInstanceHandle'; +import type ReadOnlyElement from './ReadOnlyElement'; +import {createHTMLCollection} from '../oldstylecollections/HTMLCollection'; import { createReactNativeDocumentElementInstanceHandle, setNativeElementReferenceForReactNativeDocumentElementInstanceHandle, @@ -35,10 +38,27 @@ export default class ReactNativeDocument extends ReadOnlyNode { this._documentElement = createDocumentElement(rootTag, this); } + get childElementCount(): number { + // just `documentElement`. + return 1; + } + + get children(): HTMLCollection { + return createHTMLCollection([this.documentElement]); + } + get documentElement(): ReactNativeElement { return this._documentElement; } + get firstElementChild(): ReadOnlyElement | null { + return this.documentElement; + } + + get lastElementChild(): ReadOnlyElement | null { + return this.documentElement; + } + get nodeName(): string { return '#document'; } diff --git a/packages/react-native/src/private/webapis/dom/nodes/__tests__/ReactNativeDocument-itest.js b/packages/react-native/src/private/webapis/dom/nodes/__tests__/ReactNativeDocument-itest.js index b4e8fd0a77882a..8071a493e3ed31 100644 --- a/packages/react-native/src/private/webapis/dom/nodes/__tests__/ReactNativeDocument-itest.js +++ b/packages/react-native/src/private/webapis/dom/nodes/__tests__/ReactNativeDocument-itest.js @@ -80,6 +80,30 @@ describe('ReactNativeDocument', () => { expect(element.parentNode).toBe(document.documentElement); }); + it('allows traversal through document-specific methods', () => { + let lastNode; + + const root = Fantom.createRoot(); + Fantom.runTask(() => { + root.render( + { + lastNode = node; + }} + />, + ); + }); + + const element = ensureInstance(lastNode, ReactNativeElement); + const document = ensureInstance(element.ownerDocument, ReactNativeDocument); + + expect(document.childElementCount).toBe(1); + expect(document.firstElementChild).toBe(document.documentElement); + expect(document.lastElementChild).toBe(document.documentElement); + expect(document.children).toBeInstanceOf(HTMLCollection); + expect([...document.children]).toEqual([document.documentElement]); + }); + it('implements the abstract methods from ReadOnlyNode', () => { let lastNode;