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,90 @@
/**
* 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 enableFlexboxAutoMinSizeInStrictMode:true
* @format
*/

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

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

import * as Fantom from '@react-native/fantom';
import nullthrows from 'nullthrows';
import * as React from 'react';
import {createRef} from 'react';
import {
View,
experimental_LayoutConformance as LayoutConformance,
} from 'react-native';

type ColumnProps = Readonly<{
contentPadding: number,
itemRef: React.RefSetter<HostInstance>,
minHeight?: number,
}>;

// Two items asking for 200 in a column of 100 shrink to 50 each, unless the
// first one's automatic minimum size, the padding of the view it holds, is
// larger.
function Column({contentPadding, itemRef, minHeight}: ColumnProps): React.Node {
return (
<LayoutConformance mode="strict">
<View style={{height: 100, minHeight}}>
<View ref={itemRef} style={{flexShrink: 1, height: 200, minHeight}}>
<View style={{paddingTop: contentPadding}}>
<View />
</View>
</View>
<View style={{flexShrink: 1, height: 200}} />
</View>
</LayoutConformance>
);
}

describe('the automatic minimum size of a flex item under strict layout conformance', () => {
it('follows a change to its content', () => {
const root = Fantom.createRoot();
const itemRef = createRef<HostInstance>();

const render = (contentPadding: number) => {
Fantom.runTask(() => {
root.render(
<Column contentPadding={contentPadding} itemRef={itemRef} />,
);
});
return nullthrows(itemRef.current).getBoundingClientRect().height;
};

expect(render(30)).toBe(50);
expect(render(80)).toBe(80);
});

it('follows a change to its content laid out under an explicit minimum height', () => {
const root = Fantom.createRoot();
const itemRef = createRef<HostInstance>();

const render = (contentPadding: number, minHeight?: number) => {
Fantom.runTask(() => {
root.render(
<Column
contentPadding={contentPadding}
itemRef={itemRef}
minHeight={minHeight}
/>,
);
});
return nullthrows(itemRef.current).getBoundingClientRect().height;
};

expect(render(30)).toBe(50);
// With explicit minimum heights nothing asks for the content's minimum
// size, so the change is laid out without it.
expect(render(80, 0)).toBe(50);
expect(render(80)).toBe(80);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/**
* 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 enableFlexboxAutoMinSizeInStrictMode:true
* @format
*/

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

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

import * as Fantom from '@react-native/fantom';
import nullthrows from 'nullthrows';
import * as React from 'react';
import {createRef} from 'react';
import {
Text,
View,
experimental_LayoutConformance as LayoutConformance,
} from 'react-native';

const paragraph = (
<Text>
Open <View style={{height: 8, width: 8}} />
</Text>
);

const paragraphWithMinHeight = (
<Text style={{minHeight: 0}}>
Open <View style={{height: 8, width: 8}} />
</Text>
);

type FooterScreenProps = Readonly<{
footerHeight: number,
footerRef: React.RefSetter<HostInstance>,
}>;

function FooterScreen({
footerHeight,
footerRef,
}: FooterScreenProps): React.Node {
return (
<LayoutConformance mode="strict">
<View style={{flex: 1}}>
<View>{paragraph}</View>
<View ref={footerRef} style={{height: footerHeight}} />
</View>
</LayoutConformance>
);
}

type ItemScreenProps = Readonly<{
itemMinHeight?: number,
itemRef: React.RefSetter<HostInstance>,
}>;

function ItemScreen({itemMinHeight, itemRef}: ItemScreenProps): React.Node {
return (
<LayoutConformance mode="strict">
<View style={{flex: 1, minHeight: 0}}>
<View ref={itemRef} style={{height: 30, minHeight: itemMinHeight}}>
{paragraphWithMinHeight}
</View>
</View>
</LayoutConformance>
);
}

describe('<Text> holding a <View> under strict layout conformance', () => {
it('lays out again after a sibling of the view holding it changes', () => {
const root = Fantom.createRoot();
const footerRef = createRef<HostInstance>();

const render = (footerHeight: number) => {
Fantom.runTask(() => {
root.render(
<FooterScreen footerHeight={footerHeight} footerRef={footerRef} />,
);
});
};

render(10);
// Only the footer changes, so the paragraph stays as the first render
// committed it while its container is laid out again.
render(20);

expect(nullthrows(footerRef.current).getBoundingClientRect().height).toBe(
20,
);
});

it('lays out again after the view holding it drops its minimum height', () => {
const root = Fantom.createRoot();
const itemRef = createRef<HostInstance>();

const render = (itemMinHeight?: number) => {
Fantom.runTask(() => {
root.render(
<ItemScreen itemMinHeight={itemMinHeight} itemRef={itemRef} />,
);
});
};

// Explicit minimum heights keep the min-content pass out of the paragraph,
// so the pass first reaches it once the first render has committed it.
render(0);
render();

expect(nullthrows(itemRef.current).getBoundingClientRect().height).toBe(30);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,8 @@ YGSize YogaLayoutableShadowNode::yogaNodeMeasureCallbackConnector(
TraceSection s("YogaLayoutableShadowNode::yogaNodeMeasureCallbackConnector");

auto& shadowNode = shadowNodeFromContext(yogaNode);
// Measuring may write to the node, so it must not be a committed one.
shadowNode.ensureUnsealed();

auto minimumSize = Size{.width = 0, .height = 0};
auto maximumSize = Size{
Expand Down
Loading
Loading