Skip to content

Commit 2111bec

Browse files
NickGerlemanmeta-codesync[bot]
authored andcommitted
Formalize Text in JS as overflow: hidden by default (fixes overflow: visible text with props 2.0) (facebook#55553)
Summary: Pull Request resolved: facebook#55553 RN components on the native side currently act like a mix of both overflow: hidden and overflow: visible, veering closer to the former in the ways that matter most. PreparedLayoutTextView lets us implement this more correctly, and either clip text to padding box, or not clip the text at all. For compat, PreparedLayoutTextView/PreparedLayoutTextViewManager defaulted to `overflow: hidden`. Props 2.0, broke this, since we need to see overflow state change in Fabric props, which defaults to Yoga blank style defaults (always `overflow: visible`). The sanest solution I could find was to change Android native view to keep same defaults as Fabric style layer, then explicitly set `overflow: hidden` at the style layer, as the default behavior. This does slightly change behavior, so that components which previously clipped at the bounding rect of their border box, now clip at their padding box. I.e. text will not be drawn over borders, or rounded corners, by default after this change. Changelog: [General][Changed] - Text is no longer drawn on top of borders by default [General][Changed] - Text outside the bounds of a borderRadius is now hidden by default Reviewed By: mdvacca Differential Revision: D93324626 fbshipit-source-id: 955975f01ca0333dd135e236f26f5dab828a7491
1 parent 21ab455 commit 2111bec

23 files changed

Lines changed: 234 additions & 104 deletions

packages/react-native/Libraries/Text/Text.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ import type {GestureResponderEvent} from '../Types/CoreEventTypes';
1414
import type {NativeTextProps} from './TextNativeComponent';
1515
import type {PressRetentionOffset, TextProps} from './TextProps';
1616

17+
import * as ReactNativeFeatureFlags from '../../src/private/featureflags/ReactNativeFeatureFlags';
1718
import * as PressabilityDebug from '../Pressability/PressabilityDebug';
1819
import usePressability from '../Pressability/usePressability';
1920
import flattenStyle from '../StyleSheet/flattenStyle';
2021
import processColor from '../StyleSheet/processColor';
22+
import StyleSheet from '../StyleSheet/StyleSheet';
2123
import Platform from '../Utilities/Platform';
2224
import TextAncestorContext from './TextAncestorContext';
2325
import {NativeText, NativeVirtualText} from './TextNativeComponent';
@@ -209,6 +211,10 @@ const TextImpl: component(
209211
}
210212
}
211213

214+
if (ReactNativeFeatureFlags.defaultTextToOverflowHidden()) {
215+
_style = [styles.default, _style];
216+
}
217+
212218
const _nativeID = id ?? nativeID;
213219

214220
if (_accessibilityLabel !== undefined) {
@@ -541,4 +547,13 @@ const verticalAlignToTextAlignVerticalMap = {
541547
middle: 'center',
542548
} as const;
543549

550+
const styles = StyleSheet.create({
551+
// Native components have historically acted like overflow: 'hidden'. We set
552+
// this, as part of the default style, to let client differentiate with
553+
// overflow: 'visible'.
554+
default: {
555+
overflow: 'hidden',
556+
},
557+
});
558+
544559
export default TextImpl;

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<9a521f1f6989bb2735ae87d0a0c2f5fe>>
7+
* @generated SignedSource<<e3509361f95f3681f0bb583da5540d31>>
88
*/
99

1010
/**
@@ -48,6 +48,12 @@ public object ReactNativeFeatureFlags {
4848
@JvmStatic
4949
public fun cxxNativeAnimatedEnabled(): Boolean = accessor.cxxNativeAnimatedEnabled()
5050

51+
/**
52+
* When enabled, sets the default overflow style for Text components to hidden instead of visible.
53+
*/
54+
@JvmStatic
55+
public fun defaultTextToOverflowHidden(): Boolean = accessor.defaultTextToOverflowHidden()
56+
5157
/**
5258
* Dispatch view commands in mount item order.
5359
*/

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<5a92af0a54f8aae7ba6f36a952c64df4>>
7+
* @generated SignedSource<<ae86a1485b81df483dad1108b39fae31>>
88
*/
99

1010
/**
@@ -23,6 +23,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
2323
private var commonTestFlagCache: Boolean? = null
2424
private var cdpInteractionMetricsEnabledCache: Boolean? = null
2525
private var cxxNativeAnimatedEnabledCache: Boolean? = null
26+
private var defaultTextToOverflowHiddenCache: Boolean? = null
2627
private var disableEarlyViewCommandExecutionCache: Boolean? = null
2728
private var disableImageViewPreallocationAndroidCache: Boolean? = null
2829
private var disableMountItemReorderingAndroidCache: Boolean? = null
@@ -130,6 +131,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
130131
return cached
131132
}
132133

134+
override fun defaultTextToOverflowHidden(): Boolean {
135+
var cached = defaultTextToOverflowHiddenCache
136+
if (cached == null) {
137+
cached = ReactNativeFeatureFlagsCxxInterop.defaultTextToOverflowHidden()
138+
defaultTextToOverflowHiddenCache = cached
139+
}
140+
return cached
141+
}
142+
133143
override fun disableEarlyViewCommandExecution(): Boolean {
134144
var cached = disableEarlyViewCommandExecutionCache
135145
if (cached == null) {

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<cabb5ba7c0486e777be721f6ff880d05>>
7+
* @generated SignedSource<<b57dc37b228c3d622be039686ea11471>>
88
*/
99

1010
/**
@@ -34,6 +34,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
3434

3535
@DoNotStrip @JvmStatic public external fun cxxNativeAnimatedEnabled(): Boolean
3636

37+
@DoNotStrip @JvmStatic public external fun defaultTextToOverflowHidden(): Boolean
38+
3739
@DoNotStrip @JvmStatic public external fun disableEarlyViewCommandExecution(): Boolean
3840

3941
@DoNotStrip @JvmStatic public external fun disableImageViewPreallocationAndroid(): Boolean

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<c302baa5e3dcdf6655da02cd9850e653>>
7+
* @generated SignedSource<<9ae90142061f1ea0766058d9cf6f3dc6>>
88
*/
99

1010
/**
@@ -29,6 +29,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
2929

3030
override fun cxxNativeAnimatedEnabled(): Boolean = false
3131

32+
override fun defaultTextToOverflowHidden(): Boolean = false
33+
3234
override fun disableEarlyViewCommandExecution(): Boolean = false
3335

3436
override fun disableImageViewPreallocationAndroid(): Boolean = false

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<07501a0c5936c8fe26a432d57c541d7e>>
7+
* @generated SignedSource<<7d8e2872030e38ccb038d9d7aab214a2>>
88
*/
99

1010
/**
@@ -27,6 +27,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
2727
private var commonTestFlagCache: Boolean? = null
2828
private var cdpInteractionMetricsEnabledCache: Boolean? = null
2929
private var cxxNativeAnimatedEnabledCache: Boolean? = null
30+
private var defaultTextToOverflowHiddenCache: Boolean? = null
3031
private var disableEarlyViewCommandExecutionCache: Boolean? = null
3132
private var disableImageViewPreallocationAndroidCache: Boolean? = null
3233
private var disableMountItemReorderingAndroidCache: Boolean? = null
@@ -137,6 +138,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
137138
return cached
138139
}
139140

141+
override fun defaultTextToOverflowHidden(): Boolean {
142+
var cached = defaultTextToOverflowHiddenCache
143+
if (cached == null) {
144+
cached = currentProvider.defaultTextToOverflowHidden()
145+
accessedFeatureFlags.add("defaultTextToOverflowHidden")
146+
defaultTextToOverflowHiddenCache = cached
147+
}
148+
return cached
149+
}
150+
140151
override fun disableEarlyViewCommandExecution(): Boolean {
141152
var cached = disableEarlyViewCommandExecutionCache
142153
if (cached == null) {

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<34a625414f22a7c36ff9db90e76b1254>>
7+
* @generated SignedSource<<460d442da5dc25a441b671e7b30e7e56>>
88
*/
99

1010
/**
@@ -29,6 +29,8 @@ public interface ReactNativeFeatureFlagsProvider {
2929

3030
@DoNotStrip public fun cxxNativeAnimatedEnabled(): Boolean
3131

32+
@DoNotStrip public fun defaultTextToOverflowHidden(): Boolean
33+
3234
@DoNotStrip public fun disableEarlyViewCommandExecution(): Boolean
3335

3436
@DoNotStrip public fun disableImageViewPreallocationAndroid(): Boolean

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/PreparedLayoutTextView.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,7 @@ internal class PreparedLayoutTextView(context: Context) : ViewGroup(context), Re
6868
}
6969
}
7070

71-
// T221698007: This is closest to existing behavior, but does not align with web. We may want to
72-
// change in the future if not too breaking.
73-
var overflow: Overflow = Overflow.HIDDEN
71+
var overflow: Overflow = Overflow.VISIBLE
7472
set(value) {
7573
if (field != value) {
7674
field = value
@@ -92,7 +90,7 @@ internal class PreparedLayoutTextView(context: Context) : ViewGroup(context), Re
9290

9391
fun recycleView(): Unit {
9492
BackgroundStyleApplicator.reset(this)
95-
overflow = Overflow.HIDDEN
93+
overflow = Overflow.VISIBLE
9694
clickableSpans = emptyList()
9795
selection = null
9896
selectionColor = null

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/PreparedLayoutTextViewManager.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ internal class PreparedLayoutTextViewManager :
108108

109109
@ReactProp(name = "overflow")
110110
fun setOverflow(view: PreparedLayoutTextView, overflow: String?): Unit {
111-
view.overflow = overflow?.let { Overflow.fromString(it) } ?: Overflow.HIDDEN
111+
view.overflow = overflow?.let { Overflow.fromString(it) } ?: Overflow.VISIBLE
112112
}
113113

114114
@ReactProp(name = "accessible")

packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<4471e7ab5f05b6b15c4e54e54d7c0fda>>
7+
* @generated SignedSource<<8d00a6310e0fae475007f11fa56a5a6f>>
88
*/
99

1010
/**
@@ -57,6 +57,12 @@ class ReactNativeFeatureFlagsJavaProvider
5757
return method(javaProvider_);
5858
}
5959

60+
bool defaultTextToOverflowHidden() override {
61+
static const auto method =
62+
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("defaultTextToOverflowHidden");
63+
return method(javaProvider_);
64+
}
65+
6066
bool disableEarlyViewCommandExecution() override {
6167
static const auto method =
6268
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("disableEarlyViewCommandExecution");
@@ -550,6 +556,11 @@ bool JReactNativeFeatureFlagsCxxInterop::cxxNativeAnimatedEnabled(
550556
return ReactNativeFeatureFlags::cxxNativeAnimatedEnabled();
551557
}
552558

559+
bool JReactNativeFeatureFlagsCxxInterop::defaultTextToOverflowHidden(
560+
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
561+
return ReactNativeFeatureFlags::defaultTextToOverflowHidden();
562+
}
563+
553564
bool JReactNativeFeatureFlagsCxxInterop::disableEarlyViewCommandExecution(
554565
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
555566
return ReactNativeFeatureFlags::disableEarlyViewCommandExecution();
@@ -985,6 +996,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
985996
makeNativeMethod(
986997
"cxxNativeAnimatedEnabled",
987998
JReactNativeFeatureFlagsCxxInterop::cxxNativeAnimatedEnabled),
999+
makeNativeMethod(
1000+
"defaultTextToOverflowHidden",
1001+
JReactNativeFeatureFlagsCxxInterop::defaultTextToOverflowHidden),
9881002
makeNativeMethod(
9891003
"disableEarlyViewCommandExecution",
9901004
JReactNativeFeatureFlagsCxxInterop::disableEarlyViewCommandExecution),

0 commit comments

Comments
 (0)