Skip to content

Commit db7fc6d

Browse files
adityasharatmeta-codesync[bot]
authored andcommitted
Include a leaf's padding and border in its §4.5 automatic minimum size (#1979)
Summary: Pull Request resolved: #1979 X-link: react/react-native#57279 Pull Request resolved: #1978 When a flex item with a measure function (such as text) participates in CSS Flexbox §4.5 automatic minimum sizing, its min-content contribution must include the item's own padding and border — the same box model the container-recursion branch and the normal measure pass already apply. The measure-function leaf branch previously returned only the measured content size, so a padded item was floored below its true minimum. Downstream this let content be clipped or wrapped even when there was room for it — for example a single unbroken word breaking across two lines because the floor was one padding-width too small. Both the canonical Yoga copy and the React Native vendored copy are updated, and `YGAutoMinSizeTest` gains coverage on the width (padding + border) and height (padding) axes. Changelog: [General][Fixed] - Include a node's padding and border in its automatic minimum size when it has a measure function Reviewed By: astreet Differential Revision: D108958715 fbshipit-source-id: f20fa159d9f5cff26f43e2f917109e20637a85e9
1 parent ef2efc5 commit db7fc6d

2 files changed

Lines changed: 77 additions & 1 deletion

File tree

tests/YGAutoMinSizeTest.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,75 @@ TEST(YogaAutoMinSize, auto_min_floors_text_at_min_content_width) {
129129
EXPECT_FLOAT_EQ(10.0f, YGNodeLayoutGetWidth(row.spacer));
130130
}
131131

132+
// A measure-func leaf's auto-min floor must include the leaf's own padding and
133+
// border on the main axis, just like the container branch and the normal
134+
// measure pass. Regression test: the original probe omitted them, flooring a
135+
// padded text at its bare longest-word width.
136+
TEST(YogaAutoMinSize, auto_min_includes_leaf_padding_and_border_width) {
137+
YGConfigRef config = makeWebConfig(/*useAutoMinSize=*/true);
138+
YGNodeRef root = YGNodeNewWithConfig(config);
139+
YGNodeStyleSetFlexDirection(root, YGFlexDirectionRow);
140+
YGNodeStyleSetWidth(root, 20);
141+
YGNodeStyleSetHeight(root, 50);
142+
143+
YGNodeRef text = YGNodeNewWithConfig(config);
144+
YGNodeSetMeasureFunc(text, measureWordWrappingText);
145+
YGNodeStyleSetFlexBasis(text, kNaturalWidth);
146+
YGNodeStyleSetFlexGrow(text, 0);
147+
YGNodeStyleSetFlexShrink(text, 1);
148+
YGNodeStyleSetPadding(text, YGEdgeLeft, 4);
149+
YGNodeStyleSetPadding(text, YGEdgeRight, 4);
150+
YGNodeStyleSetBorder(text, YGEdgeLeft, 1);
151+
YGNodeStyleSetBorder(text, YGEdgeRight, 1);
152+
YGNodeInsertChild(root, text, 0);
153+
154+
YGNodeRef spacer = YGNodeNewWithConfig(config);
155+
YGNodeStyleSetWidth(spacer, 10);
156+
YGNodeStyleSetFlexShrink(spacer, 0);
157+
YGNodeInsertChild(root, spacer, 1);
158+
159+
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
160+
161+
// Floor = content kWordWidth(30) + padding(4+4) + border(1+1) = 40. Without
162+
// the padding/border contribution the leaf would be wrongly floored at 30.
163+
EXPECT_FLOAT_EQ(40.0f, YGNodeLayoutGetWidth(text));
164+
165+
YGNodeFreeRecursive(root);
166+
YGConfigFree(config);
167+
}
168+
169+
// Same fix on the column (cross) axis: vertical padding must be included in the
170+
// height min-content.
171+
TEST(YogaAutoMinSize, auto_min_includes_leaf_padding_height) {
172+
YGConfigRef config = makeWebConfig(/*useAutoMinSize=*/true);
173+
YGNodeRef root = YGNodeNewWithConfig(config);
174+
YGNodeStyleSetFlexDirection(root, YGFlexDirectionColumn);
175+
YGNodeStyleSetWidth(root, 200);
176+
YGNodeStyleSetHeight(root, 20);
177+
178+
YGNodeRef text = YGNodeNewWithConfig(config);
179+
YGNodeSetMeasureFunc(text, measureWordWrappingText);
180+
YGNodeStyleSetFlexBasis(text, kNaturalWidth); // tall basis forces shrink
181+
YGNodeStyleSetFlexGrow(text, 0);
182+
YGNodeStyleSetFlexShrink(text, 1);
183+
YGNodeStyleSetPadding(text, YGEdgeTop, 4);
184+
YGNodeStyleSetPadding(text, YGEdgeBottom, 4);
185+
YGNodeInsertChild(root, text, 0);
186+
187+
YGNodeRef spacer = YGNodeNewWithConfig(config);
188+
YGNodeStyleSetHeight(spacer, 10);
189+
YGNodeStyleSetFlexShrink(spacer, 0);
190+
YGNodeInsertChild(root, spacer, 1);
191+
192+
YGNodeCalculateLayout(root, YGUndefined, YGUndefined, YGDirectionLTR);
193+
194+
// Column probe height = natural kLineHeight(16) + padding(4+4) = 24.
195+
EXPECT_FLOAT_EQ(24.0f, YGNodeLayoutGetHeight(text));
196+
197+
YGNodeFreeRecursive(root);
198+
YGConfigFree(config);
199+
}
200+
132201
// flex-basis: 0 with intrinsic content (the under-protection case from the
133202
// critique). With auto-min on, an item with `flex: 1` is still floored at
134203
// its min-content even though basis is 0.

yoga/algorithm/CalculateLayout.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -729,7 +729,14 @@ static float computeMinContentMainSize(
729729
wantRow ? MeasureMode::AtMost : MeasureMode::Undefined,
730730
wantRow ? YGUndefined : 0.0f,
731731
wantRow ? MeasureMode::Undefined : MeasureMode::AtMost);
732-
return wantRow ? size.width : size.height;
732+
// Add the leaf's own padding and border, like the container branch below.
733+
const Direction leafDirection = node->resolveDirection(ownerDirection);
734+
const float paddingAndBorder =
735+
node->style().computeFlexStartPaddingAndBorder(
736+
requestedAxis, leafDirection, ownerWidth) +
737+
node->style().computeFlexEndPaddingAndBorder(
738+
requestedAxis, leafDirection, ownerWidth);
739+
return (wantRow ? size.width : size.height) + paddingAndBorder;
733740
}
734741

735742
if (node->getChildCount() == 0) {

0 commit comments

Comments
 (0)