Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**
* 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
* @fantom_flags fixYogaFlexBasisFitContentInMainAxis:*
* @format
*/

import '@react-native/fantom/src/setUpDefaultReactNativeEnvironment';

import type {HostInstance} from 'react-native';

import ensureInstance from '../../../../src/private/__tests__/utilities/ensureInstance';
import * as Fantom from '@react-native/fantom';
import * as React from 'react';
import {createRef} from 'react';
import {ScrollView, StyleSheet, View} from 'react-native';
import ReactNativeElement from 'react-native/src/private/webapis/dom/nodes/ReactNativeElement';

const VIEWPORT_WIDTH = 402;
const VIEWPORT_HEIGHT = 760;
const FEED_CONTENT_HEIGHT = 1800;

test('auto-height wrapper around a feed ScrollView stays bounded by the viewport', () => {
const root = Fantom.createRoot({
viewportWidth: VIEWPORT_WIDTH,
viewportHeight: VIEWPORT_HEIGHT,
});

const wrapperRef = createRef<HostInstance>();
const scrollViewRef = createRef<HostInstance>();
const feedContentRef = createRef<HostInstance>();

Fantom.runTask(() => {
root.render(
<View collapsable={false} style={styles.screen}>
<View collapsable={false} ref={wrapperRef} style={styles.feedWrapper}>
<ScrollView
collapsable={false}
ref={scrollViewRef}
style={styles.feed}>
<View
collapsable={false}
ref={feedContentRef}
style={styles.feedContent}
/>
</ScrollView>
</View>
</View>,
);
});

const wrapper = ensureInstance(wrapperRef.current, ReactNativeElement);
const scrollView = ensureInstance(scrollViewRef.current, ReactNativeElement);
const feedContent = ensureInstance(
feedContentRef.current,
ReactNativeElement,
);

const wrapperRect = wrapper.getBoundingClientRect();
const scrollViewRect = scrollView.getBoundingClientRect();
const feedContentRect = feedContent.getBoundingClientRect();

expect(wrapperRect.height).toBe(VIEWPORT_HEIGHT);
expect(scrollViewRect.height).toBe(VIEWPORT_HEIGHT);
expect(feedContentRect.height).toBe(FEED_CONTENT_HEIGHT);
});

const styles = StyleSheet.create({
feed: {
width: VIEWPORT_WIDTH,
},
feedContent: {
height: FEED_CONTENT_HEIGHT,
width: VIEWPORT_WIDTH,
},
feedWrapper: {
flexGrow: 1,
},
screen: {
height: VIEWPORT_HEIGHT,
width: VIEWPORT_WIDTH,
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,11 @@ static void computeFlexBasisForChild(

// For height in the main axis (column direction): when the
// FixFlexBasisFitContent feature is enabled, skip FitContent for
// non-measure container children. This makes the flex basis independent
// of the parent's content-determined height, preventing unnecessary
// re-measurement cascades when a sibling changes size in a ScrollView.
// non-measure container children inside scroll subtrees. This makes the
// flex basis independent of content-determined heights, preventing
// unnecessary re-measurement cascades when a sibling changes size in a
// ScrollView, while preserving viewport bounds for wrappers outside the
// scroll subtree.
//
// We only optimize the height (column) axis because text wrapping depends
// on width constraints propagating through container nodes. Removing
Expand All @@ -188,8 +190,16 @@ static void computeFlexBasisForChild(
bool applyHeightFitContent =
isMainAxisRow || node->style().overflow() != Overflow::Scroll;
if (fixFlexBasisFitContent) {
bool nodeHasScrollAncestor = false;
for (auto owner = node->getOwner(); owner != nullptr;
owner = owner->getOwner()) {
if (owner->style().overflow() == Overflow::Scroll) {
nodeHasScrollAncestor = true;
break;
}
}
applyHeightFitContent = isMainAxisRow ||
(child->hasMeasureFunc() &&
((child->hasMeasureFunc() || !nodeHasScrollAncestor) &&
node->style().overflow() != Overflow::Scroll);
}
if (applyHeightFitContent && yoga::isUndefined(childHeight) &&
Expand Down
Loading