You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit f736393
Browse filesBrowse the repository at this point in the historyBrowse files
Add lineBreak and wordBreak props to Text on Android (#59002)
Summary:
Android 13 (API 33) added [`LineBreakConfig`](https://developer.android.com/reference/android/graphics/text/LineBreakConfig), which controls two things CJK text needs and React Native cannot reach today:
- **Line-break strictness** — CSS `line-break`. Strict kinsoku keeps characters such as small kana (`ッ`, `ャ`) and `ー` off the start of a line.
- **Phrase-based breaking** — CSS `word-break: auto-phrase`. Japanese breaks at phrase (文節) boundaries instead of between any two characters, which reads much better in short lines such as buttons and headings.
This cannot be done from a library. React Native builds the `StaticLayout` that both measures text for Yoga and is drawn, inside `TextLayoutManager`. A native module can only change the drawn `TextView`, which then disagrees with the measured height (clipped or extra lines). So the setting has to go through React Native's own text pipeline.
This PR adds two `Text` props named after the CSS properties, documented as Android-only for now:
```ts
lineBreak?: 'auto' | 'loose' | 'normal' | 'strict' // CSS line-break
wordBreak?: 'normal' | 'auto-phrase' // CSS word-break
```
CSS values with no Android equivalent (`anywhere`, `keep-all`, `break-all`) are left out for now.
- **JS / types:** `TextProps`, `TextNativeComponent` view config, `Text.d.ts`, regenerated `ReactNativeApi.d.ts`.
- **C++:** `LineBreak` / `WordBreak` enums, conversions, `ParagraphAttributes` fields (equality, hash, debug props), `BaseParagraphProps` parsing, Android props diffing, and two new paragraph MapBuffer keys. C++ API snapshots regenerated.
- **Android, measurement:** `TextLayoutManager` applies the config to the `StaticLayout.Builder` on API 33+.
- **Android, drawing:** `ReactTextViewManager` `ReactProp` setters apply the same values to `ReactTextView`, so drawing matches measurement; recycled views reset them.
**Behavior is unchanged unless the props are set.** With the defaults (`'auto'` / `'normal'`) no `LineBreakConfig` is set at all, and below API 33 the props are ignored. iOS has no equivalent API, so this is Android-only for now. `TextInput` is intentionally left out to keep the change reviewable.
**`'auto-phrase'` depends on locale.** Per the [Android docs](https://developer.android.com/reference/android/graphics/text/LineBreakConfig#LINE_BREAK_WORD_STYLE_PHRASE), it has no effect in a locale without phrase-based breaking. Android `Text` is laid out in the device locale, so in practice it takes effect only when that locale supports it (e.g. Japanese). The prop docs say so.
Naming follows review feedback: CSS names and values rather than `lineBreakStrategyAndroid`, since `lineBreakStrategyIOS` and `textBreakStrategy` control different things.
Motivation and design notes: iray-tno/hozo#813
## Changelog:
[ANDROID] [ADDED] - Add `lineBreak` and `wordBreak` props to `Text` (CSS `line-break` / `word-break`) for strict/loose line breaking and phrase-based breaking on Android 13+
Pull Request resolved: #59002
Test Plan:
Ran locally:
- `yarn flow-check` — 0 errors
- `yarn build-types` then `yarn test-generated-typescript` — passes
- `yarn test` — 246 suites, 6154 tests passed
- `yarn format-check-{javascript,cpp,kotlin,java}` — clean
- ESLint on the changed JS files — clean
- `yarn cxx-api-validate` (Doxygen 1.16.1, as in CI) — all 12 snapshots match
Added:
- C++ unit tests in `ParagraphAttributesTest.cpp` (equality/hash include the new fields, defaults are `Auto` / `Normal`, `toString` values match the JS prop values).
- Updated the existing Robolectric tests that call `TextLayoutManager` internals for the new parameters. They pass the default values only, so they cover the plumbing, not the effect of `strict` / `auto-phrase`.
- RNTester: **Text → Line break (lineBreak, wordBreak)** shows the same Japanese paragraph under each value at a fixed width, with a background so any measurement/drawing mismatch is visible.
**Not run locally:** native compilation (Kotlin/Java/C++), the C++ and Robolectric unit tests, and an on-device check. The environment had no Android SDK and Gradle could not resolve its plugins. Relying on CI for the native build and tests.
Still to verify on device, with the RNTester example:
- [ ] API 33+, Japanese device locale: `lineBreak` (`strict` / `loose`) and `wordBreak="auto-phrase"` change the break positions
- [ ] Measured height matches the drawn lines for each value (no clipped or blank lines)
- [ ] Removing the prop returns to the default layout
- [ ] A recycled `Text` does not keep a previous value
- [ ] Non-Japanese device locale: `auto-phrase` has no effect, and nothing breaks
- [ ] API 31/32: the props are ignored
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Reviewed By: andrewdacenko
Differential Revision: D124966798
Pulled By: javache
fbshipit-source-id: 5c66d1200b4b7af31adf3df5cc111f47996fec7f
Copy file name to clipboardExpand all lines: packages/react-native/ReactAndroid/api/ReactAndroid.api
+4Lines changed: 4 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -6042,6 +6042,8 @@ public class com/facebook/react/views/text/ReactTextView : androidx/appcompat/wi
6042
6042
public fun setHyphenationFrequency (I)V
6043
6043
public fun setIncludeFontPadding (Z)V
6044
6044
public fun setLetterSpacing (F)V
6045
+
public fun setLineBreakStyle (I)V
6046
+
public fun setLineBreakWordStyle (I)V
6045
6047
public fun setLinkifyMask (I)V
6046
6048
public fun setMinimumFontScale (F)V
6047
6049
public fun setMinimumFontSize (F)V
@@ -6088,6 +6090,7 @@ public class com/facebook/react/views/text/ReactTextViewManager : com/facebook/r
6088
6090
public final fun setFontSize (Lcom/facebook/react/views/text/ReactTextView;F)V
6089
6091
public final fun setIncludeFontPadding (Lcom/facebook/react/views/text/ReactTextView;Z)V
6090
6092
public final fun setLetterSpacing (Lcom/facebook/react/views/text/ReactTextView;F)V
6093
+
public final fun setLineBreak (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/String;)V
6091
6094
public final fun setNumberOfLines (Lcom/facebook/react/views/text/ReactTextView;I)V
6092
6095
public final fun setOverflow (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/String;)V
6093
6096
public synthetic fun setPadding (Landroid/view/View;IIII)V
@@ -6096,6 +6099,7 @@ public class com/facebook/react/views/text/ReactTextViewManager : com/facebook/r
6096
6099
public final fun setSelectable (Lcom/facebook/react/views/text/ReactTextView;Z)V
6097
6100
public final fun setSelectionColor (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/Integer;)V
6098
6101
public final fun setTextAlignVertical (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/String;)V
6102
+
public final fun setWordBreak (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/String;)V
6099
6103
public synthetic fun updateExtraData (Landroid/view/View;Ljava/lang/Object;)V
6100
6104
public fun updateExtraData (Lcom/facebook/react/views/text/ReactTextView;Ljava/lang/Object;)V
6101
6105
public synthetic fun updateState (Landroid/view/View;Lcom/facebook/react/uimanager/ReactStylesDiffMap;Lcom/facebook/react/uimanager/StateWrapper;)Ljava/lang/Object;
Copy file name to clipboardExpand all lines: packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/PreparedLayoutTextViewAccessibilityTextTest.kt
+3Lines changed: 3 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -8,6 +8,7 @@
8
8
packagecom.facebook.react.views.text
9
9
10
10
importandroid.annotation.SuppressLint
11
+
importandroid.graphics.text.LineBreakConfig
11
12
importandroid.text.Layout
12
13
importandroid.text.SpannableString
13
14
importandroid.text.TextPaint
@@ -80,6 +81,8 @@ class PreparedLayoutTextViewAccessibilityTextTest {
Copy file name to clipboardExpand all lines: packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/text/TextLayoutManagerAbsoluteLayoutWithFractionalPixelTest.kt
+3Lines changed: 3 additions & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -8,6 +8,7 @@
8
8
packagecom.facebook.react.views.text
9
9
10
10
importandroid.annotation.SuppressLint
11
+
importandroid.graphics.text.LineBreakConfig
11
12
importandroid.text.BoringLayout
12
13
importandroid.text.Layout
13
14
importandroid.text.SpannableString
@@ -97,6 +98,8 @@ class TextLayoutManagerAbsoluteLayoutWithFractionalPixelTest {
0 commit comments