diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm index 03d3b23c64c0..b1af0887b352 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm @@ -684,6 +684,8 @@ - (void)prepareForRecycle _scrollView.contentOffset = RCTCGPointFromPoint(props.contentOffset); // Reset zoom scale to default _scrollView.zoomScale = 1.0; + // Reset contentInset to prevent stale insets leaking into recycled scroll views. + _scrollView.contentInset = UIEdgeInsetsZero; // We set the default behavior to "never" so that iOS // doesn't do weird things to UIScrollView insets automatically // and keeps it as an opt-in behavior. diff --git a/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewState.h b/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewState.h index 25a02f60fdb4..72d706a1abd1 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewState.h +++ b/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewState.h @@ -28,7 +28,7 @@ class ScrollViewState final { Point contentOffset; Rect contentBoundingRect; - int scrollAwayPaddingTop; + int scrollAwayPaddingTop{0}; /* * View Culling has to be disabled when accessibility features are used. diff --git a/packages/react-native/ReactCommon/react/renderer/components/scrollview/tests/ScrollViewTest.cpp b/packages/react-native/ReactCommon/react/renderer/components/scrollview/tests/ScrollViewTest.cpp index 1b64ed7ac06c..8912b5373bc3 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/scrollview/tests/ScrollViewTest.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/scrollview/tests/ScrollViewTest.cpp @@ -5,10 +5,90 @@ * LICENSE file in the root directory of this source tree. */ -#include +#include #include -TEST(ScrollViewTest, testSomething) { - // TODO +namespace facebook::react { + +TEST(ScrollViewStateTest, defaultConstructor) { + ScrollViewState state; + + EXPECT_EQ(state.contentOffset.x, 0); + EXPECT_EQ(state.contentOffset.y, 0); + EXPECT_EQ(state.scrollAwayPaddingTop, 0); + EXPECT_FALSE(state.disableViewCulling); +} + +TEST(ScrollViewStateTest, parameterizedConstructor) { + Point contentOffset{.x = 10.0f, .y = 20.0f}; + Rect contentBoundingRect{ + .origin = {.x = 0.0f, .y = 0.0f}, + .size = {.width = 100.0f, .height = 200.0f}}; + int scrollAwayPaddingTop = 5; + + ScrollViewState state( + contentOffset, contentBoundingRect, scrollAwayPaddingTop); + + EXPECT_EQ(state.contentOffset.x, 10.0f); + EXPECT_EQ(state.contentOffset.y, 20.0f); + EXPECT_EQ(state.scrollAwayPaddingTop, 5); + EXPECT_FALSE(state.disableViewCulling); } + +TEST(ScrollViewStateTest, getContentSize) { + Point contentOffset{.x = 0.0f, .y = 0.0f}; + Rect contentBoundingRect{ + .origin = {.x = 0.0f, .y = 0.0f}, + .size = {.width = 150.0f, .height = 300.0f}}; + int scrollAwayPaddingTop = 0; + + ScrollViewState state( + contentOffset, contentBoundingRect, scrollAwayPaddingTop); + + Size contentSize = state.getContentSize(); + EXPECT_EQ(contentSize.width, 150.0f); + EXPECT_EQ(contentSize.height, 300.0f); +} + +TEST(ScrollViewStateTest, disableViewCulling) { + ScrollViewState state; + + // Default should be false + EXPECT_FALSE(state.disableViewCulling); + + // Can be set to true + state.disableViewCulling = true; + EXPECT_TRUE(state.disableViewCulling); +} + +TEST(ScrollViewStateTest, contentOffsetWithNegativeValues) { + Point contentOffset{.x = -10.0f, .y = -20.0f}; + Rect contentBoundingRect{ + .origin = {.x = 0.0f, .y = 0.0f}, + .size = {.width = 100.0f, .height = 200.0f}}; + int scrollAwayPaddingTop = 0; + + ScrollViewState state( + contentOffset, contentBoundingRect, scrollAwayPaddingTop); + + EXPECT_EQ(state.contentOffset.x, -10.0f); + EXPECT_EQ(state.contentOffset.y, -20.0f); +} + +TEST(ScrollViewStateTest, zeroSizeContentBoundingRect) { + Point contentOffset{.x = 0.0f, .y = 0.0f}; + Rect contentBoundingRect{ + .origin = {.x = 0.0f, .y = 0.0f}, + .size = {.width = 0.0f, .height = 0.0f}}; + int scrollAwayPaddingTop = 0; + + ScrollViewState state( + contentOffset, contentBoundingRect, scrollAwayPaddingTop); + + Size contentSize = state.getContentSize(); + EXPECT_EQ(contentSize.width, 0.0f); + EXPECT_EQ(contentSize.height, 0.0f); +} + +} // namespace facebook::react