From 42cd123ef2bbc689f99eccf269c08e732d610783 Mon Sep 17 00:00:00 2001 From: Nitin Chaudhary Date: Sun, 9 Nov 2025 18:05:39 +0530 Subject: [PATCH 1/4] Implement overflow property support for Fabric architecture --- .../CompositionViewComponentView.cpp | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp index b53b7057344..18a09332c0a 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp @@ -1142,6 +1142,21 @@ void ViewComponentView::updateProps( // update BaseComponentView props updateAccessibilityProps(oldViewProps, newViewProps); updateTransformProps(oldViewProps, newViewProps, Visual()); + + // Handle overflow property changes + if (oldViewProps.yogaStyle.overflow() != newViewProps.yogaStyle.overflow()) { + auto compVisual = + winrt::Microsoft::ReactNative::Composition::Experimental::MicrosoftCompositionContextHelper::InnerVisual( + Visual()); + if (compVisual) { + if (newViewProps.yogaStyle.overflow() == facebook::yoga::Overflow::Hidden) { + compVisual.Clip(Compositor().CreateInsetClip(0.0f, 0.0f, 0.0f, 0.0f)); + } else { + compVisual.Clip(nullptr); + } + } + } + base_type::updateProps(props, oldProps); m_props = std::static_pointer_cast(props); @@ -1319,6 +1334,23 @@ void ViewComponentView::updateLayoutMetrics( Visual().Size( {layoutMetrics.frame.size.width * layoutMetrics.pointScaleFactor, layoutMetrics.frame.size.height * layoutMetrics.pointScaleFactor}); + + // Apply overflow clipping + if (m_props && m_props->yogaStyle.overflow() == facebook::yoga::Overflow::Hidden) { + auto compVisual = + winrt::Microsoft::ReactNative::Composition::Experimental::MicrosoftCompositionContextHelper::InnerVisual( + Visual()); + if (compVisual) { + compVisual.Clip(Compositor().CreateInsetClip(0.0f, 0.0f, 0.0f, 0.0f)); + } + } else if (m_props) { + auto compVisual = + winrt::Microsoft::ReactNative::Composition::Experimental::MicrosoftCompositionContextHelper::InnerVisual( + Visual()); + if (compVisual) { + compVisual.Clip(nullptr); + } + } } void ViewComponentView::prepareForRecycle() noexcept {} From 85b860f7c04e3822f560b0b34d1dfdc280ba4584 Mon Sep 17 00:00:00 2001 From: Nitin Chaudhary Date: Sun, 9 Nov 2025 18:06:05 +0530 Subject: [PATCH 2/4] Change files --- ...ative-windows-8d24c2d8-08f9-4612-a46d-8bf25b0f020d.json | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 change/react-native-windows-8d24c2d8-08f9-4612-a46d-8bf25b0f020d.json diff --git a/change/react-native-windows-8d24c2d8-08f9-4612-a46d-8bf25b0f020d.json b/change/react-native-windows-8d24c2d8-08f9-4612-a46d-8bf25b0f020d.json new file mode 100644 index 00000000000..da6dd0cddc0 --- /dev/null +++ b/change/react-native-windows-8d24c2d8-08f9-4612-a46d-8bf25b0f020d.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "Implement overflow property support for Fabric architecture", + "packageName": "react-native-windows", + "email": "nitchaudhary@microsoft.com", + "dependentChangeType": "patch" +} From f68fbe60ff6e1fbbe70a8a70fc840ad099d83401 Mon Sep 17 00:00:00 2001 From: Nitin Chaudhary Date: Mon, 1 Dec 2025 10:45:55 +0530 Subject: [PATCH 3/4] Subject: ETW Provider Registration - Route to Existing 1DS Aria Tenant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hi OSG Instrumentation Team, I'm working on React Native Windows telemetry. We currently have: ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ EXISTING (working): - JavaScript CLI telemetry via 1DS SDK - Instrumentation Key: 49ff6d3ef12f4578a7b75a2573d9dba8-026332b2-2d50-452f-ad0d-50f921c97a9d-7145 - Data flows to: Aria → Kusto - Kusto cluster: [YOU NEED TO TELL ME THIS] NEW (want to add): - Native C++ telemetry via ETW/TraceLogging - Provider Name: Microsoft.ReactNativeWindows.Telemetry - Provider GUID: [WILL GENERATE] ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ QUESTION: Can you register the new ETW provider GUID and route it to the SAME Aria tenant as our existing 1DS instrumentation key? Goal: Have both JavaScript (1DS) and C++ (ETW) telemetry appear in the same Kusto database for unified querying. Is this possible? If yes, what info do you need from me? Thanks, Harini Malothu React Native Windows Team --- packages/playground/Samples/overflowTest.tsx | 1415 +++++++++++++++++ .../CompositionViewComponentView.cpp | 62 +- .../CompositionViewComponentView.h | 1 + 3 files changed, 1463 insertions(+), 15 deletions(-) create mode 100644 packages/playground/Samples/overflowTest.tsx diff --git a/packages/playground/Samples/overflowTest.tsx b/packages/playground/Samples/overflowTest.tsx new file mode 100644 index 00000000000..f6404766160 --- /dev/null +++ b/packages/playground/Samples/overflowTest.tsx @@ -0,0 +1,1415 @@ +/** + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + * @format + * + * OVERFLOW PROPERTY TEST SUITE + * + * This test demonstrates the overflow property implementation for React Native Windows Fabric. + * + * KEY CONCEPTS: + * - overflow: 'visible' (default) - Children can extend beyond parent's bounds + * - overflow: 'hidden' - Children are clipped to parent's bounds + * - Border radius affects the clipping shape when overflow is 'hidden' + * + * IMPLEMENTATION DETAILS: + * The implementation separates background rendering from clipping: + * 1. Background: Always rendered as rectangular SpriteVisual with solid brush + * 2. Border: Rendered separately with rounded corners (when borderRadius specified) + * 3. Clipping: Only applied when overflow='hidden' + * - If borderRadius exists: Uses SetClippingPath with rounded geometry + * - If no borderRadius: Uses InsetClip (rectangular) + * - If overflow='visible': No clipping applied + * + * VISUAL EXPECTATIONS: + * Each test shows a parent box with colored children positioned to extend beyond bounds: + * - overflow='visible' tests: Yellow/cyan/lime children should be fully visible outside parent + * - overflow='hidden' tests: Children should be clipped at parent's boundary + * - When borderRadius + overflow='hidden': Clipping follows rounded shape + */ + +import React from 'react'; +import {AppRegistry, ScrollView, StyleSheet, Text, View} from 'react-native'; + +// Debug component to show actual computed styles +const DebugBox = ({children, label, style}: any) => { + const viewRef = React.useRef(null); + const [info, setInfo] = React.useState(''); + + React.useEffect(() => { + if (viewRef.current) { + // Try to access the style prop + const styleObj = StyleSheet.flatten(style); + setInfo( + `overflow: ${styleObj?.overflow || 'undefined'}, borderRadius: ${ + styleObj?.borderRadius || 0 + }`, + ); + } + }, [style]); + + return ( + + {info} + + {children} + + + ); +}; + +export default class OverflowTest extends React.Component { + render() { + return ( + + + OVERFLOW PROPERTY TEST SUITE + + Testing overflow:visible vs overflow:hidden with various border + radius configurations + + + {/* TEST ROW 1: Uniform border radius */} + + 1. Uniform Border Radius (15px all corners) + + + {/* Test 1.1: Default overflow (should be 'visible') */} + + Default + + overflow: visible + + + Expected: Yellow box visible at top-left corner outside parent + bounds. Cyan box visible at top-right outside bounds. Lime box + visible at bottom outside bounds. + + + + + + + + + {/* Test 1.2: Explicit overflow visible */} + + Explicit + + overflow: visible + + + Expected: Same as default - all three colored boxes extend + beyond parent. + + + + + + + + + {/* Test 1.3: Overflow hidden */} + + Clipped + + overflow: hidden + + + Expected: Yellow, cyan, and lime boxes are clipped to rounded + rectangle boundary. Only portions inside the parent should be + visible. + + + + + + + + + + {/* TEST ROW 2: Mixed corner radii */} + + 2. Mixed Corner Radii (25px, 5px, 5px, 25px) + + + + Default + + overflow: visible + + + Expected: Red, orange, purple boxes extend outside asymmetric + rounded parent. + + + + + + + + + + Explicit + + overflow: visible + + + Expected: Same as default - colored boxes visible outside + bounds. + + + + + + + + + + Clipped + + overflow: hidden + + + Expected: Boxes clipped to asymmetric rounded shape - large + curves at top-left and bottom-right. + + + + + + + + + + {/* TEST ROW 3: No border radius (rectangular) */} + + 3. No Border Radius (Rectangular) + + + + Default + + overflow: visible + + + Expected: Pink, violet, gold boxes fully visible outside + rectangular parent. + + + + + + + + + + Explicit + + overflow: visible + + + Expected: Same as default - boxes extend beyond rectangular + bounds. + + + + + + + + + + Clipped + + overflow: hidden + + + Expected: Boxes clipped to sharp rectangular boundary (no + curves). + + + + + + + + + + {/* TEST ROW 4: Circle (full radius) */} + + 4. Circle (borderRadius = 50% of width/height) + + + + Default + + overflow: visible + + + Expected: Inner white circle fully visible even though parent is + circular border. + + + + + + + + Explicit + + overflow: visible + + + Expected: Same as default - white circle fully visible. + + + + + + + + Clipped + + overflow: hidden + + + Expected: White circle clipped to circular boundary (should be + nearly invisible except edges). + + + + + + + + {/* TEST ROW 5: Single corner radius */} + + 5. Single Corner Radius (top-left only = 20px) + + + + Default + + overflow: visible + + + Expected: Brown box at top-left corner extends outside. Tan box + at bottom-right extends outside. + + + + + + + + + Explicit + + overflow: visible + + + Expected: Same as default - boxes extend beyond bounds. + + + + + + + + + Clipped + + overflow: hidden + + + Expected: Brown box clipped by rounded top-left corner. Tan box + clipped by sharp bottom-right corner. + + + + + + + + + {/* TEST ROW 6: No border, only background with radius */} + + 6. Background Only (no border, borderRadius = 12px) + + + + Default + + overflow: visible + + + Expected: Navy box at top-left and teal box at bottom-right + extend beyond salmon background. Note: Background will appear + rectangular even with borderRadius (known limitation). + + + + + + + + + Explicit + + overflow: visible + + + Expected: Same as default - boxes extend beyond. + + + + + + + + + Clipped + + overflow: hidden + + + Expected: Boxes clipped to rounded boundary (clipping works even + though background is rectangular). + + + + + + + + + {/* TEST ROW 7: Text Content Overflow (RNTester-style) */} + + 7. Text Content Overflow (Wide text in narrow container) + + + + Default (undefined) + + overflow: undefined + + + Expected: Wide text extends beyond narrow container bounds to + the right. + + + + + This is wide text content + + + + + + + Hidden + + overflow: hidden + + + Expected: Text clipped at container boundary - only text within + the 95px width should be visible. + + + + + This is wide text content + + + + + + + Visible + + overflow: visible + + + Expected: Text extends beyond container - full 200px width + visible. + + + + + This is wide text content + + + + + + + {/* TEST ROW 8: Multiple Children with Overflow */} + + 8. Multiple Children Overflow Test + + + + Default + + overflow: visible + + + Expected: All three colored boxes visible extending in different + directions. + + + + Top + + + Left + + + Right + + + Bottom + + + + + + Hidden + + overflow: hidden + + + Expected: All text labels clipped at container edges - none + should extend outside. + + + + Top + + + Left + + + Right + + + Bottom + + + + + + Rounded + Hidden + + overflow: hidden + borderRadius + + + Expected: Text clipped to rounded corners - no text visible + outside rounded boundary. + + + + Top + + + Left + + + Right + + + Bottom + + + + + + {/* Summary Section */} + + Implementation Summary + + Platform Parity Achieved: + + + ✓ overflow: 'visible' (default) - Children extend beyond parent + bounds + + + ✓ overflow: 'hidden' - Children clipped to parent bounds + + + ✓ Rounded clipping - When overflow='hidden' + borderRadius, + clipping follows rounded shape + + + ✓ Rectangular clipping - When overflow='hidden' without + borderRadius + + + ✓ Works with Text components - Text content respects overflow + settings + + + ✓ Works with multiple children - All children clipped consistently + + + + Reviewer Feedback Addressed (PR #15338): + + + ✓ No conflict with border rendering - Uses separate + m_contentVisual for children + + + ✓ Overflow clipping independent of background - Background and + clipping are separate + + + ✓ Clean architecture - Virtual method pattern allows proper visual + separation + + + + Note: Background with borderRadius but no border appears + rectangular (known limitation). This is acceptable as an interim + solution. Clipping geometry still works correctly with rounded + corners when overflow='hidden'. + + + + Total Test Cases: 20 scenarios covering all combinations of + overflow, borderRadius, and content types + + + + + ); + } +} + +const styles = StyleSheet.create({ + scrollView: { + flex: 1, + backgroundColor: '#f0f0f0', + }, + container: { + padding: 20, + }, + title: { + fontSize: 24, + fontWeight: 'bold', + color: '#0066cc', + marginBottom: 10, + textAlign: 'center', + }, + subtitle: { + fontSize: 14, + color: '#666', + marginBottom: 20, + textAlign: 'center', + paddingHorizontal: 20, + }, + sectionHeader: { + fontSize: 16, + fontWeight: 'bold', + color: '#333', + marginTop: 20, + marginBottom: 10, + backgroundColor: '#e0e0e0', + padding: 8, + }, + row: { + flexDirection: 'row', + flexWrap: 'wrap', + justifyContent: 'space-around', + marginBottom: 15, + }, + testCase: { + alignItems: 'center', + margin: 10, + padding: 10, + backgroundColor: 'white', + borderRadius: 8, + borderWidth: 1, + borderColor: '#ccc', + width: 250, + }, + testLabel: { + fontSize: 12, + fontWeight: 'bold', + marginBottom: 2, + }, + visibleLabel: { + color: 'green', + }, + hiddenLabel: { + color: 'red', + }, + explanation: { + fontSize: 10, + color: '#555', + marginBottom: 10, + textAlign: 'center', + paddingHorizontal: 5, + }, + summaryBox: { + backgroundColor: '#fff8dc', + padding: 15, + marginTop: 30, + borderRadius: 8, + borderWidth: 2, + borderColor: '#daa520', + }, + summaryTitle: { + fontSize: 18, + fontWeight: 'bold', + color: '#333', + marginBottom: 10, + }, + summarySubtitle: { + fontSize: 15, + fontWeight: 'bold', + color: '#555', + marginTop: 12, + marginBottom: 6, + }, + summaryText: { + fontSize: 13, + color: '#333', + marginBottom: 5, + }, + testCount: { + fontSize: 14, + fontWeight: 'bold', + color: '#0066cc', + }, + noteText: { + fontStyle: 'italic', + color: '#666', + marginTop: 10, + }, +}); + +AppRegistry.registerComponent('Bootstrap', () => OverflowTest); diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp index 16a9c4764c0..db8ff2ca6b6 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp @@ -1070,6 +1070,12 @@ void ViewComponentView::ensureVisual() noexcept { } OuterVisual().InsertAt(m_visual, 0); } + + // Create m_contentVisual as a child of m_visual if not already created + if (!m_contentVisual) { + m_contentVisual = m_compContext.CreateSpriteVisual(); + m_visual.InsertAt(m_contentVisual, 0); // Insert at index 0 so it's below border visuals + } } winrt::Microsoft::ReactNative::ComponentView ViewComponentView::Create( @@ -1084,7 +1090,8 @@ winrt::Microsoft::ReactNative::Composition::Experimental::IVisual ViewComponentView::VisualToMountChildrenInto() noexcept { if (m_builder && m_builder->VisualToMountChildrenIntoHandler()) return m_builder->VisualToMountChildrenIntoHandler()(*this); - return Visual(); + // Mount children into m_contentVisual + return m_contentVisual ? m_contentVisual : Visual(); } void ViewComponentView::MountChildComponentView( @@ -1333,20 +1340,45 @@ void ViewComponentView::updateLayoutMetrics( {layoutMetrics.frame.size.width * layoutMetrics.pointScaleFactor, layoutMetrics.frame.size.height * layoutMetrics.pointScaleFactor}); - // Apply overflow clipping - if (m_props && m_props->yogaStyle.overflow() == facebook::yoga::Overflow::Hidden) { - auto compVisual = - winrt::Microsoft::ReactNative::Composition::Experimental::MicrosoftCompositionContextHelper::InnerVisual( - Visual()); - if (compVisual) { - compVisual.Clip(Compositor().CreateInsetClip(0.0f, 0.0f, 0.0f, 0.0f)); - } - } else if (m_props) { - auto compVisual = - winrt::Microsoft::ReactNative::Composition::Experimental::MicrosoftCompositionContextHelper::InnerVisual( - Visual()); - if (compVisual) { - compVisual.Clip(nullptr); + // Size and offset m_contentVisual to match the content area, excluding borders + if (m_contentVisual && m_props) { + auto borderMetrics = BorderPrimitive::resolveAndAlignBorderMetrics(layoutMetrics, *m_props); + float borderLeft = borderMetrics.borderWidths.left; + float borderTop = borderMetrics.borderWidths.top; + float borderRight = borderMetrics.borderWidths.right; + float borderBottom = borderMetrics.borderWidths.bottom; + float scale = layoutMetrics.pointScaleFactor; + float contentWidth = layoutMetrics.frame.size.width * scale - borderLeft - borderRight; + float contentHeight = layoutMetrics.frame.size.height * scale - borderTop - borderBottom; + m_contentVisual.Offset({borderLeft, borderTop, 0}); + m_contentVisual.Size({std::max(0.f, contentWidth), std::max(0.f, contentHeight)}); + + // Apply clipping to m_contentVisual for overflow: hidden + if (m_props->getClipsContentToBounds()) { + // Calculate inner border radii for clipping (0 for rectangular clip) + facebook::react::RectangleCorners innerRadii; + innerRadii.topLeft = { + std::max(0.f, borderMetrics.borderRadii.topLeft.horizontal - borderLeft), + std::max(0.f, borderMetrics.borderRadii.topLeft.vertical - borderTop)}; + innerRadii.topRight = { + std::max(0.f, borderMetrics.borderRadii.topRight.horizontal - borderRight), + std::max(0.f, borderMetrics.borderRadii.topRight.vertical - borderTop)}; + innerRadii.bottomLeft = { + std::max(0.f, borderMetrics.borderRadii.bottomLeft.horizontal - borderLeft), + std::max(0.f, borderMetrics.borderRadii.bottomLeft.vertical - borderBottom)}; + innerRadii.bottomRight = { + std::max(0.f, borderMetrics.borderRadii.bottomRight.horizontal - borderRight), + std::max(0.f, borderMetrics.borderRadii.bottomRight.vertical - borderBottom)}; + + winrt::com_ptr pathGeometry = BorderPrimitive::GenerateRoundedRectPathGeometry( + m_compContext, innerRadii, {0, 0, 0, 0}, {0, 0, std::max(0.f, contentWidth), std::max(0.f, contentHeight)}); + + m_contentVisual.as<::Microsoft::ReactNative::Composition::Experimental::IVisualInterop>()->SetClippingPath( + pathGeometry.get()); + } else { + // Clear any existing clip + m_contentVisual.as<::Microsoft::ReactNative::Composition::Experimental::IVisualInterop>()->SetClippingPath( + nullptr); } } } diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h index 588af7ad463..5de28793a03 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h @@ -231,6 +231,7 @@ struct ViewComponentView : public ViewComponentViewT< bool m_hasNonVisualChildren{false}; facebook::react::SharedViewProps m_props; winrt::Microsoft::ReactNative::Composition::Experimental::IVisual m_visual{nullptr}; + winrt::Microsoft::ReactNative::Composition::Experimental::IVisual m_contentVisual{nullptr}; winrt::Microsoft::ReactNative::Composition::Experimental::CreateInternalVisualDelegate m_createInternalVisualHandler{ nullptr}; }; From d82c4dd64cf1733ab13b1d426a5b3fe5106309ba Mon Sep 17 00:00:00 2001 From: Nitin Chaudhary Date: Mon, 1 Dec 2025 11:51:33 +0530 Subject: [PATCH 4/4] Remove overflowTest.tsx - will attach separately in PR --- packages/playground/Samples/overflowTest.tsx | 1415 ------------------ 1 file changed, 1415 deletions(-) delete mode 100644 packages/playground/Samples/overflowTest.tsx diff --git a/packages/playground/Samples/overflowTest.tsx b/packages/playground/Samples/overflowTest.tsx deleted file mode 100644 index f6404766160..00000000000 --- a/packages/playground/Samples/overflowTest.tsx +++ /dev/null @@ -1,1415 +0,0 @@ -/** - * Copyright (c) Microsoft Corporation. - * Licensed under the MIT License. - * @format - * - * OVERFLOW PROPERTY TEST SUITE - * - * This test demonstrates the overflow property implementation for React Native Windows Fabric. - * - * KEY CONCEPTS: - * - overflow: 'visible' (default) - Children can extend beyond parent's bounds - * - overflow: 'hidden' - Children are clipped to parent's bounds - * - Border radius affects the clipping shape when overflow is 'hidden' - * - * IMPLEMENTATION DETAILS: - * The implementation separates background rendering from clipping: - * 1. Background: Always rendered as rectangular SpriteVisual with solid brush - * 2. Border: Rendered separately with rounded corners (when borderRadius specified) - * 3. Clipping: Only applied when overflow='hidden' - * - If borderRadius exists: Uses SetClippingPath with rounded geometry - * - If no borderRadius: Uses InsetClip (rectangular) - * - If overflow='visible': No clipping applied - * - * VISUAL EXPECTATIONS: - * Each test shows a parent box with colored children positioned to extend beyond bounds: - * - overflow='visible' tests: Yellow/cyan/lime children should be fully visible outside parent - * - overflow='hidden' tests: Children should be clipped at parent's boundary - * - When borderRadius + overflow='hidden': Clipping follows rounded shape - */ - -import React from 'react'; -import {AppRegistry, ScrollView, StyleSheet, Text, View} from 'react-native'; - -// Debug component to show actual computed styles -const DebugBox = ({children, label, style}: any) => { - const viewRef = React.useRef(null); - const [info, setInfo] = React.useState(''); - - React.useEffect(() => { - if (viewRef.current) { - // Try to access the style prop - const styleObj = StyleSheet.flatten(style); - setInfo( - `overflow: ${styleObj?.overflow || 'undefined'}, borderRadius: ${ - styleObj?.borderRadius || 0 - }`, - ); - } - }, [style]); - - return ( - - {info} - - {children} - - - ); -}; - -export default class OverflowTest extends React.Component { - render() { - return ( - - - OVERFLOW PROPERTY TEST SUITE - - Testing overflow:visible vs overflow:hidden with various border - radius configurations - - - {/* TEST ROW 1: Uniform border radius */} - - 1. Uniform Border Radius (15px all corners) - - - {/* Test 1.1: Default overflow (should be 'visible') */} - - Default - - overflow: visible - - - Expected: Yellow box visible at top-left corner outside parent - bounds. Cyan box visible at top-right outside bounds. Lime box - visible at bottom outside bounds. - - - - - - - - - {/* Test 1.2: Explicit overflow visible */} - - Explicit - - overflow: visible - - - Expected: Same as default - all three colored boxes extend - beyond parent. - - - - - - - - - {/* Test 1.3: Overflow hidden */} - - Clipped - - overflow: hidden - - - Expected: Yellow, cyan, and lime boxes are clipped to rounded - rectangle boundary. Only portions inside the parent should be - visible. - - - - - - - - - - {/* TEST ROW 2: Mixed corner radii */} - - 2. Mixed Corner Radii (25px, 5px, 5px, 25px) - - - - Default - - overflow: visible - - - Expected: Red, orange, purple boxes extend outside asymmetric - rounded parent. - - - - - - - - - - Explicit - - overflow: visible - - - Expected: Same as default - colored boxes visible outside - bounds. - - - - - - - - - - Clipped - - overflow: hidden - - - Expected: Boxes clipped to asymmetric rounded shape - large - curves at top-left and bottom-right. - - - - - - - - - - {/* TEST ROW 3: No border radius (rectangular) */} - - 3. No Border Radius (Rectangular) - - - - Default - - overflow: visible - - - Expected: Pink, violet, gold boxes fully visible outside - rectangular parent. - - - - - - - - - - Explicit - - overflow: visible - - - Expected: Same as default - boxes extend beyond rectangular - bounds. - - - - - - - - - - Clipped - - overflow: hidden - - - Expected: Boxes clipped to sharp rectangular boundary (no - curves). - - - - - - - - - - {/* TEST ROW 4: Circle (full radius) */} - - 4. Circle (borderRadius = 50% of width/height) - - - - Default - - overflow: visible - - - Expected: Inner white circle fully visible even though parent is - circular border. - - - - - - - - Explicit - - overflow: visible - - - Expected: Same as default - white circle fully visible. - - - - - - - - Clipped - - overflow: hidden - - - Expected: White circle clipped to circular boundary (should be - nearly invisible except edges). - - - - - - - - {/* TEST ROW 5: Single corner radius */} - - 5. Single Corner Radius (top-left only = 20px) - - - - Default - - overflow: visible - - - Expected: Brown box at top-left corner extends outside. Tan box - at bottom-right extends outside. - - - - - - - - - Explicit - - overflow: visible - - - Expected: Same as default - boxes extend beyond bounds. - - - - - - - - - Clipped - - overflow: hidden - - - Expected: Brown box clipped by rounded top-left corner. Tan box - clipped by sharp bottom-right corner. - - - - - - - - - {/* TEST ROW 6: No border, only background with radius */} - - 6. Background Only (no border, borderRadius = 12px) - - - - Default - - overflow: visible - - - Expected: Navy box at top-left and teal box at bottom-right - extend beyond salmon background. Note: Background will appear - rectangular even with borderRadius (known limitation). - - - - - - - - - Explicit - - overflow: visible - - - Expected: Same as default - boxes extend beyond. - - - - - - - - - Clipped - - overflow: hidden - - - Expected: Boxes clipped to rounded boundary (clipping works even - though background is rectangular). - - - - - - - - - {/* TEST ROW 7: Text Content Overflow (RNTester-style) */} - - 7. Text Content Overflow (Wide text in narrow container) - - - - Default (undefined) - - overflow: undefined - - - Expected: Wide text extends beyond narrow container bounds to - the right. - - - - - This is wide text content - - - - - - - Hidden - - overflow: hidden - - - Expected: Text clipped at container boundary - only text within - the 95px width should be visible. - - - - - This is wide text content - - - - - - - Visible - - overflow: visible - - - Expected: Text extends beyond container - full 200px width - visible. - - - - - This is wide text content - - - - - - - {/* TEST ROW 8: Multiple Children with Overflow */} - - 8. Multiple Children Overflow Test - - - - Default - - overflow: visible - - - Expected: All three colored boxes visible extending in different - directions. - - - - Top - - - Left - - - Right - - - Bottom - - - - - - Hidden - - overflow: hidden - - - Expected: All text labels clipped at container edges - none - should extend outside. - - - - Top - - - Left - - - Right - - - Bottom - - - - - - Rounded + Hidden - - overflow: hidden + borderRadius - - - Expected: Text clipped to rounded corners - no text visible - outside rounded boundary. - - - - Top - - - Left - - - Right - - - Bottom - - - - - - {/* Summary Section */} - - Implementation Summary - - Platform Parity Achieved: - - - ✓ overflow: 'visible' (default) - Children extend beyond parent - bounds - - - ✓ overflow: 'hidden' - Children clipped to parent bounds - - - ✓ Rounded clipping - When overflow='hidden' + borderRadius, - clipping follows rounded shape - - - ✓ Rectangular clipping - When overflow='hidden' without - borderRadius - - - ✓ Works with Text components - Text content respects overflow - settings - - - ✓ Works with multiple children - All children clipped consistently - - - - Reviewer Feedback Addressed (PR #15338): - - - ✓ No conflict with border rendering - Uses separate - m_contentVisual for children - - - ✓ Overflow clipping independent of background - Background and - clipping are separate - - - ✓ Clean architecture - Virtual method pattern allows proper visual - separation - - - - Note: Background with borderRadius but no border appears - rectangular (known limitation). This is acceptable as an interim - solution. Clipping geometry still works correctly with rounded - corners when overflow='hidden'. - - - - Total Test Cases: 20 scenarios covering all combinations of - overflow, borderRadius, and content types - - - - - ); - } -} - -const styles = StyleSheet.create({ - scrollView: { - flex: 1, - backgroundColor: '#f0f0f0', - }, - container: { - padding: 20, - }, - title: { - fontSize: 24, - fontWeight: 'bold', - color: '#0066cc', - marginBottom: 10, - textAlign: 'center', - }, - subtitle: { - fontSize: 14, - color: '#666', - marginBottom: 20, - textAlign: 'center', - paddingHorizontal: 20, - }, - sectionHeader: { - fontSize: 16, - fontWeight: 'bold', - color: '#333', - marginTop: 20, - marginBottom: 10, - backgroundColor: '#e0e0e0', - padding: 8, - }, - row: { - flexDirection: 'row', - flexWrap: 'wrap', - justifyContent: 'space-around', - marginBottom: 15, - }, - testCase: { - alignItems: 'center', - margin: 10, - padding: 10, - backgroundColor: 'white', - borderRadius: 8, - borderWidth: 1, - borderColor: '#ccc', - width: 250, - }, - testLabel: { - fontSize: 12, - fontWeight: 'bold', - marginBottom: 2, - }, - visibleLabel: { - color: 'green', - }, - hiddenLabel: { - color: 'red', - }, - explanation: { - fontSize: 10, - color: '#555', - marginBottom: 10, - textAlign: 'center', - paddingHorizontal: 5, - }, - summaryBox: { - backgroundColor: '#fff8dc', - padding: 15, - marginTop: 30, - borderRadius: 8, - borderWidth: 2, - borderColor: '#daa520', - }, - summaryTitle: { - fontSize: 18, - fontWeight: 'bold', - color: '#333', - marginBottom: 10, - }, - summarySubtitle: { - fontSize: 15, - fontWeight: 'bold', - color: '#555', - marginTop: 12, - marginBottom: 6, - }, - summaryText: { - fontSize: 13, - color: '#333', - marginBottom: 5, - }, - testCount: { - fontSize: 14, - fontWeight: 'bold', - color: '#0066cc', - }, - noteText: { - fontStyle: 'italic', - color: '#666', - marginTop: 10, - }, -}); - -AppRegistry.registerComponent('Bootstrap', () => OverflowTest);