mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Implement ReadOnlyText and ReadOnlyCharacterData
Summary: This adds supports for text instances in React Native using a DOM-like interface (as defined in https://github.com/react-native-community/discussions-and-proposals/pull/607). This reuses the `getTextContent` method from Fabric that we added in D44464637, which is supported for this use case as well. Changelog: [internal] bypass-github-export-checks Reviewed By: rshest Differential Revision: D44632362 fbshipit-source-id: edea99ce61fb17d33853c72196ece7bb06a01e41
This commit is contained in:
committed by
Facebook GitHub Bot
parent
26fdb44c57
commit
6dbc2ccdf9
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
// flowlint unsafe-getters-setters:off
|
||||
|
||||
import type ReadOnlyElement from './ReadOnlyElement';
|
||||
|
||||
import {getFabricUIManager} from '../../ReactNative/FabricUIManager';
|
||||
import ReadOnlyNode, {getShadowNode} from './ReadOnlyNode';
|
||||
import {getElementSibling} from './Utilities/Traversal';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
export default class ReadOnlyCharacterData extends ReadOnlyNode {
|
||||
get nextElementSibling(): ReadOnlyElement | null {
|
||||
return getElementSibling(this, 'next');
|
||||
}
|
||||
|
||||
get previousElementSibling(): ReadOnlyElement | null {
|
||||
return getElementSibling(this, 'previous');
|
||||
}
|
||||
|
||||
get data(): string {
|
||||
const shadowNode = getShadowNode(this);
|
||||
|
||||
if (shadowNode != null) {
|
||||
return nullthrows(getFabricUIManager()).getTextContent(shadowNode);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
get length(): number {
|
||||
return this.data.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
get textContent(): string | null {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
get nodeValue(): string {
|
||||
return this.data;
|
||||
}
|
||||
|
||||
substringData(offset: number, count: number): string {
|
||||
const data = this.data;
|
||||
if (offset < 0) {
|
||||
throw new TypeError(
|
||||
`Failed to execute 'substringData' on 'CharacterData': The offset ${offset} is negative.`,
|
||||
);
|
||||
}
|
||||
if (offset > data.length) {
|
||||
throw new TypeError(
|
||||
`Failed to execute 'substringData' on 'CharacterData': The offset ${offset} is greater than the node's length (${data.length}).`,
|
||||
);
|
||||
}
|
||||
let adjustedCount = count < 0 || count > data.length ? data.length : count;
|
||||
return data.substr(offset, adjustedCount);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import {getFabricUIManager} from '../../ReactNative/FabricUIManager';
|
||||
import DOMRect from '../Geometry/DOMRect';
|
||||
import {createHTMLCollection} from '../OldStyleCollections/HTMLCollection';
|
||||
import ReadOnlyNode, {getChildNodes, getShadowNode} from './ReadOnlyNode';
|
||||
import {getElementSibling} from './Utilities/Traversal';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
export default class ReadOnlyElement extends ReadOnlyNode {
|
||||
@@ -68,14 +69,7 @@ export default class ReadOnlyElement extends ReadOnlyNode {
|
||||
}
|
||||
|
||||
get nextElementSibling(): ReadOnlyElement | null {
|
||||
const [siblings, position] = getElementSiblingsAndPosition(this);
|
||||
|
||||
if (position === siblings.length - 1) {
|
||||
// this node is the last child of its parent, so there is no next sibling.
|
||||
return null;
|
||||
}
|
||||
|
||||
return siblings[position + 1];
|
||||
return getElementSibling(this, 'next');
|
||||
}
|
||||
|
||||
get nodeName(): string {
|
||||
@@ -93,14 +87,7 @@ export default class ReadOnlyElement extends ReadOnlyNode {
|
||||
set nodeValue(value: string): void {}
|
||||
|
||||
get previousElementSibling(): ReadOnlyElement | null {
|
||||
const [siblings, position] = getElementSiblingsAndPosition(this);
|
||||
|
||||
if (position === 0) {
|
||||
// this node is the last child of its parent, so there is no next sibling.
|
||||
return null;
|
||||
}
|
||||
|
||||
return siblings[position - 1];
|
||||
return getElementSibling(this, 'previous');
|
||||
}
|
||||
|
||||
get scrollHeight(): number {
|
||||
@@ -161,22 +148,3 @@ function getChildElements(node: ReadOnlyNode): $ReadOnlyArray<ReadOnlyElement> {
|
||||
childNode => childNode instanceof ReadOnlyElement,
|
||||
);
|
||||
}
|
||||
|
||||
export function getElementSiblingsAndPosition(
|
||||
element: ReadOnlyElement,
|
||||
): [$ReadOnlyArray<ReadOnlyElement>, number] {
|
||||
const parent = element.parentNode;
|
||||
if (parent == null) {
|
||||
// This node is the root or it's disconnected.
|
||||
return [[element], 0];
|
||||
}
|
||||
|
||||
const siblings = getChildElements(parent);
|
||||
const position = siblings.indexOf(element);
|
||||
|
||||
if (position === -1) {
|
||||
throw new TypeError("Missing node in parent's child node list");
|
||||
}
|
||||
|
||||
return [siblings, position];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
// flowlint unsafe-getters-setters:off
|
||||
|
||||
import ReadOnlyCharacterData from './ReadOnlyCharacterData';
|
||||
import ReadOnlyNode from './ReadOnlyNode';
|
||||
|
||||
export default class ReadOnlyText extends ReadOnlyCharacterData {
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
get nodeName(): string {
|
||||
return '#text';
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
get nodeType(): number {
|
||||
return ReadOnlyNode.TEXT_NODE;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @format
|
||||
* @flow strict-local
|
||||
*/
|
||||
|
||||
import type ReadOnlyElement from '../ReadOnlyElement';
|
||||
import type ReadOnlyNode from '../ReadOnlyNode';
|
||||
|
||||
import {getChildNodes} from '../ReadOnlyNode';
|
||||
|
||||
// We initialize this lazily to avoid a require cycle
|
||||
// (`ReadOnlyElement` also depends on `Traversal`).
|
||||
let ReadOnlyElementClass: Class<ReadOnlyElement>;
|
||||
|
||||
export function getElementSibling(
|
||||
node: ReadOnlyNode,
|
||||
direction: 'next' | 'previous',
|
||||
): ReadOnlyElement | null {
|
||||
const parent = node.parentNode;
|
||||
if (parent == null) {
|
||||
// This node is the root or it's disconnected.
|
||||
return null;
|
||||
}
|
||||
|
||||
const childNodes = getChildNodes(parent);
|
||||
|
||||
const startPosition = childNodes.indexOf(node);
|
||||
if (startPosition === -1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const increment = direction === 'next' ? 1 : -1;
|
||||
|
||||
let position = startPosition + increment;
|
||||
|
||||
if (ReadOnlyElementClass == null) {
|
||||
// We initialize this lazily to avoid a require cycle.
|
||||
ReadOnlyElementClass = require('../ReadOnlyElement').default;
|
||||
}
|
||||
|
||||
while (
|
||||
childNodes[position] != null &&
|
||||
!(childNodes[position] instanceof ReadOnlyElementClass)
|
||||
) {
|
||||
position = position + increment;
|
||||
}
|
||||
|
||||
return childNodes[position] ?? null;
|
||||
}
|
||||
@@ -0,0 +1,407 @@
|
||||
/**
|
||||
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*
|
||||
* @flow strict-local
|
||||
* @format
|
||||
* @oncall react_native
|
||||
*/
|
||||
|
||||
import type ReactNativeElement from '../ReactNativeElement';
|
||||
|
||||
import invariant from 'invariant';
|
||||
import nullthrows from 'nullthrows';
|
||||
import * as React from 'react';
|
||||
import {act} from 'react-test-renderer';
|
||||
|
||||
jest.mock('../../../ReactNative/FabricUIManager', () =>
|
||||
require('../../../ReactNative/__mocks__/FabricUIManager'),
|
||||
);
|
||||
|
||||
const MOCK_CONTAINER_TAG = 11;
|
||||
|
||||
describe('ReadOnlyText', () => {
|
||||
let ReactFabric;
|
||||
let NativeText;
|
||||
let ReadOnlyText;
|
||||
let ReadOnlyNode;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
|
||||
// Installs the global `nativeFabricUIManager` pointing to the mock.
|
||||
require('../../../ReactNative/__mocks__/FabricUIManager');
|
||||
require('../../../ReactNative/ReactNativeFeatureFlags').enableAccessToHostTreeInFabric =
|
||||
() => true;
|
||||
|
||||
ReactFabric = require('../../../Renderer/shims/ReactFabric');
|
||||
NativeText = require('../../../Text/TextNativeComponent').NativeText;
|
||||
ReadOnlyText = require('../ReadOnlyText').default;
|
||||
ReadOnlyNode = require('../ReadOnlyNode').default;
|
||||
});
|
||||
|
||||
it('should be used to create public text instances when the `enableAccessToHostTreeInFabric` feature flag is enabled', () => {
|
||||
let lastParentNode;
|
||||
|
||||
act(() => {
|
||||
ReactFabric.render(
|
||||
<NativeText
|
||||
ref={node => {
|
||||
lastParentNode = node;
|
||||
}}>
|
||||
Some text
|
||||
</NativeText>,
|
||||
MOCK_CONTAINER_TAG,
|
||||
);
|
||||
});
|
||||
|
||||
// $FlowExpectedError[incompatible-type]
|
||||
const parentNode: ReadOnlyNode = nullthrows(lastParentNode);
|
||||
const textNode = parentNode.childNodes[0];
|
||||
|
||||
expect(textNode).toBeInstanceOf(ReadOnlyText);
|
||||
});
|
||||
|
||||
describe('extends `ReadOnlyNode`', () => {
|
||||
describe('nodeName', () => {
|
||||
it('returns "#text"', () => {
|
||||
let lastParentNode;
|
||||
|
||||
act(() => {
|
||||
ReactFabric.render(
|
||||
<NativeText
|
||||
ref={node => {
|
||||
lastParentNode = node;
|
||||
}}>
|
||||
Some text
|
||||
</NativeText>,
|
||||
MOCK_CONTAINER_TAG,
|
||||
);
|
||||
});
|
||||
|
||||
// $FlowExpectedError[incompatible-type]
|
||||
const parentNode: ReadOnlyNode = nullthrows(lastParentNode);
|
||||
const textNode = parentNode.childNodes[0];
|
||||
|
||||
expect(textNode.nodeName).toBe('#text');
|
||||
});
|
||||
});
|
||||
|
||||
describe('nodeType', () => {
|
||||
it('returns ReadOnlyNode.TEXT_NODE', () => {
|
||||
let lastParentNode;
|
||||
|
||||
act(() => {
|
||||
ReactFabric.render(
|
||||
<NativeText
|
||||
ref={node => {
|
||||
lastParentNode = node;
|
||||
}}>
|
||||
Some text
|
||||
</NativeText>,
|
||||
MOCK_CONTAINER_TAG,
|
||||
);
|
||||
});
|
||||
|
||||
// $FlowExpectedError[incompatible-type]
|
||||
const parentNode: ReadOnlyNode = nullthrows(lastParentNode);
|
||||
const textNode = parentNode.childNodes[0];
|
||||
|
||||
expect(textNode.nodeType).toBe(ReadOnlyNode.TEXT_NODE);
|
||||
});
|
||||
});
|
||||
|
||||
describe('nodeValue / textContent', () => {
|
||||
it('returns the string data contained in the node', () => {
|
||||
let lastParentNode;
|
||||
|
||||
act(() => {
|
||||
ReactFabric.render(
|
||||
<NativeText
|
||||
ref={node => {
|
||||
lastParentNode = node;
|
||||
}}>
|
||||
Some text
|
||||
</NativeText>,
|
||||
MOCK_CONTAINER_TAG,
|
||||
);
|
||||
});
|
||||
|
||||
// $FlowExpectedError[incompatible-type]
|
||||
const parentNode: ReadOnlyNode = nullthrows(lastParentNode);
|
||||
const textNode = parentNode.childNodes[0];
|
||||
|
||||
expect(textNode.nodeValue).toBe('Some text');
|
||||
expect(textNode.textContent).toBe('Some text');
|
||||
});
|
||||
});
|
||||
|
||||
describe('traversal', () => {
|
||||
it('only preserves text nodes when their contents do not change', () => {
|
||||
let lastParentElement;
|
||||
let lastChildElementA;
|
||||
|
||||
act(() => {
|
||||
ReactFabric.render(
|
||||
<NativeText
|
||||
key="parent"
|
||||
ref={element => {
|
||||
lastParentElement = element;
|
||||
}}>
|
||||
Text A
|
||||
<NativeText
|
||||
key="childA"
|
||||
ref={element => {
|
||||
lastChildElementA = element;
|
||||
}}
|
||||
/>
|
||||
Text B
|
||||
</NativeText>,
|
||||
MOCK_CONTAINER_TAG,
|
||||
);
|
||||
});
|
||||
|
||||
// $FlowExpectedError[incompatible-type]
|
||||
const parentElement: ReactNativeElement = nullthrows(lastParentElement);
|
||||
// $FlowExpectedError[incompatible-type]
|
||||
const childElementA: ReactNativeElement = nullthrows(lastChildElementA);
|
||||
|
||||
// Get text nodes and refine them as text nodes for Flow
|
||||
const childTextA = parentElement.childNodes[0];
|
||||
invariant(
|
||||
childTextA instanceof ReadOnlyText,
|
||||
'expected instance of ReadOnlyText',
|
||||
);
|
||||
expect(childTextA.textContent).toBe('Text A');
|
||||
const childTextB = parentElement.childNodes[2];
|
||||
invariant(
|
||||
childTextB instanceof ReadOnlyText,
|
||||
'expected instance of ReadOnlyText',
|
||||
);
|
||||
expect(childTextB.textContent).toBe('Text B');
|
||||
|
||||
// Validate structure
|
||||
expect(parentElement.childNodes.length).toBe(3);
|
||||
expect(parentElement.childNodes[0]).toBe(childTextA);
|
||||
expect(parentElement.childNodes[1]).toBe(childElementA);
|
||||
expect(parentElement.childNodes[2]).toBe(childTextB);
|
||||
|
||||
// Change contents of the second text only
|
||||
act(() => {
|
||||
ReactFabric.render(
|
||||
<NativeText
|
||||
key="parent"
|
||||
ref={element => {
|
||||
lastParentElement = element;
|
||||
}}>
|
||||
Text A
|
||||
<NativeText
|
||||
key="childA"
|
||||
ref={element => {
|
||||
lastChildElementA = element;
|
||||
}}
|
||||
/>
|
||||
Text B modified
|
||||
</NativeText>,
|
||||
MOCK_CONTAINER_TAG,
|
||||
);
|
||||
});
|
||||
|
||||
expect(parentElement.childNodes.length).toBe(3);
|
||||
expect(parentElement.childNodes[0]).toBe(childTextA);
|
||||
expect(parentElement.childNodes[1]).toBe(childElementA);
|
||||
expect(parentElement.childNodes[2]).not.toBe(childTextB);
|
||||
expect(parentElement.childNodes[2]).toBeInstanceOf(ReadOnlyText);
|
||||
expect(parentElement.childNodes[2]).toMatchObject({
|
||||
data: 'Text B modified',
|
||||
});
|
||||
expect(childTextB.isConnected).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('extends `ReadOnlyCharacterData`', () => {
|
||||
describe('data / length', () => {
|
||||
it('returns the string data and its length, respectively', () => {
|
||||
let lastParentNode;
|
||||
|
||||
act(() => {
|
||||
ReactFabric.render(
|
||||
<NativeText
|
||||
ref={node => {
|
||||
lastParentNode = node;
|
||||
}}>
|
||||
Some text
|
||||
</NativeText>,
|
||||
MOCK_CONTAINER_TAG,
|
||||
);
|
||||
});
|
||||
|
||||
// $FlowExpectedError[incompatible-type]
|
||||
const parentNode: ReadOnlyNode = nullthrows(lastParentNode);
|
||||
// $FlowExpectedError[incompatible-type]
|
||||
const textNode: ReadOnlyText = parentNode.childNodes[0];
|
||||
|
||||
expect(textNode.data).toBe('Some text');
|
||||
expect(textNode.length).toBe('Some text'.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('previousElementSibling / nextElementSibling', () => {
|
||||
it('return updated relative elements', () => {
|
||||
let lastParentElement;
|
||||
let lastChildElementA;
|
||||
let lastChildElementB;
|
||||
let lastChildElementC;
|
||||
|
||||
act(() => {
|
||||
ReactFabric.render(
|
||||
<NativeText
|
||||
key="parent"
|
||||
ref={element => {
|
||||
lastParentElement = element;
|
||||
}}>
|
||||
Text A
|
||||
<NativeText
|
||||
key="childA"
|
||||
ref={element => {
|
||||
lastChildElementA = element;
|
||||
}}
|
||||
/>
|
||||
Text B
|
||||
<NativeText
|
||||
key="childB"
|
||||
ref={element => {
|
||||
lastChildElementB = element;
|
||||
}}
|
||||
/>
|
||||
Text C
|
||||
<NativeText
|
||||
key="childC"
|
||||
ref={element => {
|
||||
lastChildElementC = element;
|
||||
}}
|
||||
/>
|
||||
Text D
|
||||
</NativeText>,
|
||||
MOCK_CONTAINER_TAG,
|
||||
);
|
||||
});
|
||||
|
||||
// $FlowExpectedError[incompatible-type]
|
||||
const parentElement: ReactNativeElement = nullthrows(lastParentElement);
|
||||
// $FlowExpectedError[incompatible-type]
|
||||
const childElementA: ReactNativeElement = nullthrows(lastChildElementA);
|
||||
// $FlowExpectedError[incompatible-type]
|
||||
const childElementB: ReactNativeElement = nullthrows(lastChildElementB);
|
||||
// $FlowExpectedError[incompatible-type]
|
||||
const childElementC: ReactNativeElement = nullthrows(lastChildElementC);
|
||||
|
||||
// Get text nodes and refine them as text nodes for Flow
|
||||
const childTextA = parentElement.childNodes[0];
|
||||
invariant(
|
||||
childTextA instanceof ReadOnlyText,
|
||||
'expected instance of ReadOnlyText',
|
||||
);
|
||||
const childTextB = parentElement.childNodes[2];
|
||||
invariant(
|
||||
childTextB instanceof ReadOnlyText,
|
||||
'expected instance of ReadOnlyText',
|
||||
);
|
||||
const childTextC = parentElement.childNodes[4];
|
||||
invariant(
|
||||
childTextC instanceof ReadOnlyText,
|
||||
'expected instance of ReadOnlyText',
|
||||
);
|
||||
const childTextD = parentElement.childNodes[6];
|
||||
invariant(
|
||||
childTextD instanceof ReadOnlyText,
|
||||
'expected instance of ReadOnlyText',
|
||||
);
|
||||
|
||||
// Validate structure
|
||||
expect(parentElement.childNodes.length).toBe(7);
|
||||
expect(parentElement.childNodes[0]).toBe(childTextA);
|
||||
expect(parentElement.childNodes[1]).toBe(childElementA);
|
||||
expect(parentElement.childNodes[2]).toBe(childTextB);
|
||||
expect(parentElement.childNodes[3]).toBe(childElementB);
|
||||
expect(parentElement.childNodes[4]).toBe(childTextC);
|
||||
expect(parentElement.childNodes[5]).toBe(childElementC);
|
||||
expect(parentElement.childNodes[6]).toBe(childTextD);
|
||||
|
||||
expect(childTextA.previousElementSibling).toBe(null);
|
||||
expect(childTextA.nextElementSibling).toBe(childElementA);
|
||||
|
||||
expect(childTextB.previousElementSibling).toBe(childElementA);
|
||||
expect(childTextB.nextElementSibling).toBe(childElementB);
|
||||
|
||||
expect(childTextC.previousElementSibling).toBe(childElementB);
|
||||
expect(childTextC.nextElementSibling).toBe(childElementC);
|
||||
|
||||
expect(childTextD.previousElementSibling).toBe(childElementC);
|
||||
expect(childTextD.nextElementSibling).toBe(null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('substringData', () => {
|
||||
it('returns a slice of the text content', () => {
|
||||
let lastParentElement;
|
||||
|
||||
act(() => {
|
||||
ReactFabric.render(
|
||||
<NativeText
|
||||
key="parent"
|
||||
ref={element => {
|
||||
lastParentElement = element;
|
||||
}}>
|
||||
Text A
|
||||
</NativeText>,
|
||||
MOCK_CONTAINER_TAG,
|
||||
);
|
||||
});
|
||||
|
||||
// $FlowExpectedError[incompatible-type]
|
||||
const parentElement: ReactNativeElement = nullthrows(lastParentElement);
|
||||
|
||||
// Get text nodes and refine them as text nodes for Flow
|
||||
const childTextA = parentElement.childNodes[0];
|
||||
invariant(
|
||||
childTextA instanceof ReadOnlyText,
|
||||
'expected instance of ReadOnlyText',
|
||||
);
|
||||
|
||||
expect(childTextA.substringData(0, 1)).toBe('T');
|
||||
expect(childTextA.substringData(0, 5)).toBe('Text ');
|
||||
// Count > length
|
||||
expect(childTextA.substringData(0, 10)).toBe('Text A');
|
||||
// Count = length
|
||||
expect(childTextA.substringData(0, 6)).toBe('Text A');
|
||||
expect(childTextA.substringData(0, 0)).toBe('');
|
||||
// Negative count
|
||||
expect(childTextA.substringData(0, -1)).toBe('Text A');
|
||||
expect(childTextA.substringData(0, -10)).toBe('Text A');
|
||||
|
||||
expect(childTextA.substringData(1, 3)).toBe('ext');
|
||||
expect(childTextA.substringData(5, 1)).toBe('A');
|
||||
// Offset + count > length
|
||||
expect(childTextA.substringData(5, 2)).toBe('A');
|
||||
// Offset = length
|
||||
expect(childTextA.substringData(6, 1)).toBe('');
|
||||
// Offset = length & negative count
|
||||
expect(childTextA.substringData(6, -1)).toBe('');
|
||||
// Negative count
|
||||
expect(childTextA.substringData(5, -1)).toBe('A');
|
||||
|
||||
// Out of bounds offset
|
||||
expect(() => {
|
||||
childTextA.substringData(-1, 0);
|
||||
}).toThrow();
|
||||
expect(() => {
|
||||
childTextA.substringData(7, 0);
|
||||
}).toThrow();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Vendored
+10
-5
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
import type ReactNativeElement from '../../DOM/Nodes/ReactNativeElement';
|
||||
import type ReadOnlyText from '../../DOM/Nodes/ReadOnlyText';
|
||||
import typeof ReactFabricType from '../../Renderer/shims/ReactFabric';
|
||||
import type {
|
||||
InternalInstanceHandle,
|
||||
@@ -23,6 +24,7 @@ import ReactNativeFeatureFlags from '../ReactNativeFeatureFlags';
|
||||
let PublicInstanceClass:
|
||||
| Class<ReactFabricHostComponent>
|
||||
| Class<ReactNativeElement>;
|
||||
let ReadOnlyTextClass: Class<ReadOnlyText>;
|
||||
|
||||
// Lazy loaded to avoid evaluating the module when using the legacy renderer.
|
||||
let ReactFabric: ReactFabricType;
|
||||
@@ -46,11 +48,14 @@ export function createPublicInstance(
|
||||
return new PublicInstanceClass(tag, viewConfig, internalInstanceHandle);
|
||||
}
|
||||
|
||||
export function createPublicTextInstance(internalInstanceHandle: mixed): {} {
|
||||
// React will call this method to create text instances but we'll return an
|
||||
// empty object for now. These instances are only created lazily when
|
||||
// traversing the tree, and that's not enabled yet.
|
||||
return {};
|
||||
export function createPublicTextInstance(
|
||||
internalInstanceHandle: InternalInstanceHandle,
|
||||
): ReadOnlyText {
|
||||
if (ReadOnlyTextClass == null) {
|
||||
ReadOnlyTextClass = require('../../DOM/Nodes/ReadOnlyText').default;
|
||||
}
|
||||
|
||||
return new ReadOnlyTextClass(internalInstanceHandle);
|
||||
}
|
||||
|
||||
export function getNativeTagFromPublicInstance(
|
||||
|
||||
@@ -903,6 +903,9 @@ jsi::Value UIManagerBinding::get(
|
||||
// concatenates all the text contents. Otherwise, it returns an empty
|
||||
// string.
|
||||
|
||||
// This is also used to access the text content of text nodes, which does
|
||||
// not need any traversal.
|
||||
|
||||
// getTextContent(shadowNode: ShadowNode): string
|
||||
return jsi::Function::createFromHostFunction(
|
||||
runtime,
|
||||
|
||||
Reference in New Issue
Block a user