Skip to content

Commit 334bf7c

Browse files
andrewdacenkofacebook-github-bot
authored andcommitted
Support getBoundingClientRect for TextShadowNode
Summary: When `getBoundingClientRect()` is called on a nested `<Text>` component (TextShadowNode), return the parent paragraph's bounding rect instead of empty/invalid metrics. TextShadowNode is a virtual node that doesn't have its own layout metrics. This matches web behavior where inline elements return their container's rect. Use `getClientRects()` (added in a follow-up diff) to get the individual fragment rects for text that spans multiple lines. Differential Revision: D91087220
1 parent afb6847 commit 334bf7c

1 file changed

Lines changed: 45 additions & 0 deletions

File tree

  • packages/react-native/ReactCommon/react/renderer/dom

packages/react-native/ReactCommon/react/renderer/dom/DOM.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66
*/
77

88
#include "DOM.h"
9+
#include <react/renderer/components/text/ParagraphShadowNode.h>
910
#include <react/renderer/components/text/RawTextShadowNode.h>
11+
#include <react/renderer/components/text/TextShadowNode.h>
1012
#include <react/renderer/core/LayoutMetrics.h>
1113
#include <react/renderer/graphics/Point.h>
1214
#include <react/renderer/graphics/Rect.h>
@@ -280,6 +282,49 @@ DOMRect getBoundingClientRect(
280282
return DOMRect{};
281283
}
282284

285+
// Check if this is a TextShadowNode (virtual text node nested in a paragraph)
286+
auto textShadowNode = dynamic_cast<const TextShadowNode*>(&shadowNode);
287+
if (textShadowNode != nullptr) {
288+
// TextShadowNode is a virtual node that doesn't have its own layout metrics
289+
// For getBoundingClientRect, we return the parent paragraph's bounding rect
290+
// (matching web behavior where inline elements return their container's
291+
// rect) Use getClientRects() to get the individual fragment rects
292+
auto ancestors = shadowNode.getFamily().getAncestors(*currentRevision);
293+
if (ancestors.empty()) {
294+
return DOMRect{};
295+
}
296+
297+
// Find the ParagraphShadowNode in the ancestors
298+
const ParagraphShadowNode* paragraphNode = nullptr;
299+
for (const auto& pair : ancestors) {
300+
paragraphNode =
301+
dynamic_cast<const ParagraphShadowNode*>(&pair.first.get());
302+
if (paragraphNode != nullptr) {
303+
break;
304+
}
305+
}
306+
307+
if (paragraphNode == nullptr) {
308+
return DOMRect{};
309+
}
310+
311+
// Return the paragraph's bounding rect
312+
auto paragraphLayoutMetrics = getLayoutMetricsFromRoot(
313+
*currentRevision,
314+
*paragraphNode,
315+
{.includeTransform = includeTransform, .includeViewportOffset = true});
316+
if (paragraphLayoutMetrics == EmptyLayoutMetrics) {
317+
return DOMRect{};
318+
}
319+
320+
auto frame = paragraphLayoutMetrics.frame;
321+
return DOMRect{
322+
.x = frame.origin.x,
323+
.y = frame.origin.y,
324+
.width = frame.size.width,
325+
.height = frame.size.height};
326+
}
327+
283328
auto layoutMetrics = getLayoutMetricsFromRoot(
284329
*currentRevision,
285330
shadowNode,

0 commit comments

Comments
 (0)