Skip to content

Commit 8dfd548

Browse files
sammy-SCmeta-codesync[bot]
authored andcommitted
fix a regression introduced by fixYogaFlexBasisFitContentInMainAxis feature flag (#56839)
Summary: X-link: react/yoga#1962 Pull Request resolved: #56839 changelog: [internal] Limit the FixFlexBasisFitContent height optimization to non-measure container children inside scroll subtrees. This keeps Marketplace Home wrappers outside the ScrollView viewport-bounded while preserving the scroll remeasurement optimization. This regression was discovered during a recent QE where I tried to run enable this optimisation. Reviewed By: christophpurrer Differential Revision: D105167981 fbshipit-source-id: bc988005507ebc1fc1688a8dc77c233e12ac2834
1 parent 94a28a4 commit 8dfd548

2 files changed

Lines changed: 101 additions & 4 deletions

File tree

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/**
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow strict-local
8+
* @fantom_flags fixYogaFlexBasisFitContentInMainAxis:*
9+
* @format
10+
*/
11+
12+
import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';
13+
14+
import type {HostInstance} from 'react-native';
15+
16+
import ensureInstance from '../../../../src/private/__tests__/utilities/ensureInstance';
17+
import * as Fantom from '@react-native/fantom';
18+
import * as React from 'react';
19+
import {createRef} from 'react';
20+
import {ScrollView, StyleSheet, View} from 'react-native';
21+
import ReactNativeElement from 'react-native/src/private/webapis/dom/nodes/ReactNativeElement';
22+
23+
const VIEWPORT_WIDTH = 402;
24+
const VIEWPORT_HEIGHT = 760;
25+
const FEED_CONTENT_HEIGHT = 1800;
26+
27+
test('auto-height wrapper around a feed ScrollView stays bounded by the viewport', () => {
28+
const root = Fantom.createRoot({
29+
viewportWidth: VIEWPORT_WIDTH,
30+
viewportHeight: VIEWPORT_HEIGHT,
31+
});
32+
33+
const wrapperRef = createRef<HostInstance>();
34+
const scrollViewRef = createRef<HostInstance>();
35+
const feedContentRef = createRef<HostInstance>();
36+
37+
Fantom.runTask(() => {
38+
root.render(
39+
<View collapsable={false} style={styles.screen}>
40+
<View collapsable={false} ref={wrapperRef} style={styles.feedWrapper}>
41+
<ScrollView
42+
collapsable={false}
43+
ref={scrollViewRef}
44+
style={styles.feed}>
45+
<View
46+
collapsable={false}
47+
ref={feedContentRef}
48+
style={styles.feedContent}
49+
/>
50+
</ScrollView>
51+
</View>
52+
</View>,
53+
);
54+
});
55+
56+
const wrapper = ensureInstance(wrapperRef.current, ReactNativeElement);
57+
const scrollView = ensureInstance(scrollViewRef.current, ReactNativeElement);
58+
const feedContent = ensureInstance(
59+
feedContentRef.current,
60+
ReactNativeElement,
61+
);
62+
63+
const wrapperRect = wrapper.getBoundingClientRect();
64+
const scrollViewRect = scrollView.getBoundingClientRect();
65+
const feedContentRect = feedContent.getBoundingClientRect();
66+
67+
expect(wrapperRect.height).toBe(VIEWPORT_HEIGHT);
68+
expect(scrollViewRect.height).toBe(VIEWPORT_HEIGHT);
69+
expect(feedContentRect.height).toBe(FEED_CONTENT_HEIGHT);
70+
});
71+
72+
const styles = StyleSheet.create({
73+
feed: {
74+
width: VIEWPORT_WIDTH,
75+
},
76+
feedContent: {
77+
height: FEED_CONTENT_HEIGHT,
78+
width: VIEWPORT_WIDTH,
79+
},
80+
feedWrapper: {
81+
flexGrow: 1,
82+
},
83+
screen: {
84+
height: VIEWPORT_HEIGHT,
85+
width: VIEWPORT_WIDTH,
86+
},
87+
});

packages/react-native/ReactCommon/yoga/yoga/algorithm/CalculateLayout.cpp

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,9 +177,11 @@ static void computeFlexBasisForChild(
177177

178178
// For height in the main axis (column direction): when the
179179
// FixFlexBasisFitContent feature is enabled, skip FitContent for
180-
// non-measure container children. This makes the flex basis independent
181-
// of the parent's content-determined height, preventing unnecessary
182-
// re-measurement cascades when a sibling changes size in a ScrollView.
180+
// non-measure container children inside scroll subtrees. This makes the
181+
// flex basis independent of content-determined heights, preventing
182+
// unnecessary re-measurement cascades when a sibling changes size in a
183+
// ScrollView, while preserving viewport bounds for wrappers outside the
184+
// scroll subtree.
183185
//
184186
// We only optimize the height (column) axis because text wrapping depends
185187
// on width constraints propagating through container nodes. Removing
@@ -188,8 +190,16 @@ static void computeFlexBasisForChild(
188190
bool applyHeightFitContent =
189191
isMainAxisRow || node->style().overflow() != Overflow::Scroll;
190192
if (fixFlexBasisFitContent) {
193+
bool nodeHasScrollAncestor = false;
194+
for (auto owner = node->getOwner(); owner != nullptr;
195+
owner = owner->getOwner()) {
196+
if (owner->style().overflow() == Overflow::Scroll) {
197+
nodeHasScrollAncestor = true;
198+
break;
199+
}
200+
}
191201
applyHeightFitContent = isMainAxisRow ||
192-
(child->hasMeasureFunc() &&
202+
((child->hasMeasureFunc() || !nodeHasScrollAncestor) &&
193203
node->style().overflow() != Overflow::Scroll);
194204
}
195205
if (applyHeightFitContent && yoga::isUndefined(childHeight) &&

0 commit comments

Comments
 (0)