From 80421a346d7ca55b842f31c0f479c0be7d88137f Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel Date: Wed, 12 Aug 2026 09:22:05 +0200 Subject: [PATCH 1/2] [Android] Ellipsize single-line TextInput placeholders Long Android hints were hard-clipped; set TruncateAt.END for non-multiline inputs so placeholders match iOS trailing ellipsis. Co-authored-by: Cursor --- .../react/views/textinput/ReactEditText.kt | 20 +++++++++++++++++++ .../textinput/ReactTextInputPropertyTest.kt | 18 +++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt index 0415dca1126b..8f96527991cc 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt @@ -591,6 +591,10 @@ public open class ReactEditText public constructor(context: Context) : AppCompat } wasMultiline = isMultiline + // Keep placeholder ellipsize in sync when multiline flips. Single-line inputs should + // trail with "…" (matching iOS); multiline placeholders should still wrap. + updatePlaceholderEllipsize() + // We override the KeyListener so that all keys on the soft input keyboard as well as hardware // keyboards work. Some KeyListeners like DigitsKeyListener will display the keyboard but not // accept all input from it @@ -606,6 +610,22 @@ public open class ReactEditText public constructor(context: Context) : AppCompat this.placeholder = placeholder hint = placeholder } + updatePlaceholderEllipsize() + } + + /** + * On Android, long hints are clipped without an ellipsis unless [ellipsize] is set. Align with + * iOS by ellipsizing trailing placeholders on single-line inputs only. + * + * See https://github.com/facebook/react-native/issues/29663 + */ + private fun updatePlaceholderEllipsize() { + ellipsize = + if (!isMultiline) { + TextUtils.TruncateAt.END + } else { + null + } } public fun setFontFamily(fontFamily: String?) { diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/textinput/ReactTextInputPropertyTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/textinput/ReactTextInputPropertyTest.kt index f6e38cdcb250..c0253adb41f2 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/textinput/ReactTextInputPropertyTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/textinput/ReactTextInputPropertyTest.kt @@ -19,6 +19,7 @@ import android.text.InputType import android.text.Layout import android.text.SpannableString import android.text.Spanned +import android.text.TextUtils import android.util.DisplayMetrics import android.view.Gravity import android.view.View @@ -196,9 +197,26 @@ class ReactTextInputPropertyTest { manager.updateProperties(view, buildStyles("placeholder", "sometext")) assertThat(view.hint).isEqualTo("sometext") + assertThat(view.ellipsize).isEqualTo(TextUtils.TruncateAt.END) manager.updateProperties(view, buildStyles("placeholder", null)) assertThat(view.hint).isNull() + assertThat(view.ellipsize).isEqualTo(TextUtils.TruncateAt.END) + } + + @Test + fun testPlaceholderEllipsizeRespectsMultiline() { + manager.updateProperties(view, buildStyles("placeholder", "a very long placeholder string")) + assertThat(view.ellipsize).isEqualTo(TextUtils.TruncateAt.END) + + manager.updateProperties(view, buildStyles("multiline", true)) + // commitStagedInputType is normally called during view updates; apply it for the unit test. + view.commitStagedInputType() + assertThat(view.ellipsize).isNull() + + manager.updateProperties(view, buildStyles("multiline", false)) + view.commitStagedInputType() + assertThat(view.ellipsize).isEqualTo(TextUtils.TruncateAt.END) } @Test From b0da2960644a42359ff66ae081bf3d29d64ee4f4 Mon Sep 17 00:00:00 2001 From: Jakub Kosmydel Date: Wed, 12 Aug 2026 12:22:04 +0200 Subject: [PATCH 2/2] deslop --- .../com/facebook/react/views/textinput/ReactEditText.kt | 8 -------- .../react/views/textinput/ReactTextInputPropertyTest.kt | 1 - 2 files changed, 9 deletions(-) diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt index 8f96527991cc..2fb3f04614b3 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactEditText.kt @@ -591,8 +591,6 @@ public open class ReactEditText public constructor(context: Context) : AppCompat } wasMultiline = isMultiline - // Keep placeholder ellipsize in sync when multiline flips. Single-line inputs should - // trail with "…" (matching iOS); multiline placeholders should still wrap. updatePlaceholderEllipsize() // We override the KeyListener so that all keys on the soft input keyboard as well as hardware @@ -613,12 +611,6 @@ public open class ReactEditText public constructor(context: Context) : AppCompat updatePlaceholderEllipsize() } - /** - * On Android, long hints are clipped without an ellipsis unless [ellipsize] is set. Align with - * iOS by ellipsizing trailing placeholders on single-line inputs only. - * - * See https://github.com/facebook/react-native/issues/29663 - */ private fun updatePlaceholderEllipsize() { ellipsize = if (!isMultiline) { diff --git a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/textinput/ReactTextInputPropertyTest.kt b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/textinput/ReactTextInputPropertyTest.kt index c0253adb41f2..0aea6306c79f 100644 --- a/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/textinput/ReactTextInputPropertyTest.kt +++ b/packages/react-native/ReactAndroid/src/test/java/com/facebook/react/views/textinput/ReactTextInputPropertyTest.kt @@ -210,7 +210,6 @@ class ReactTextInputPropertyTest { assertThat(view.ellipsize).isEqualTo(TextUtils.TruncateAt.END) manager.updateProperties(view, buildStyles("multiline", true)) - // commitStagedInputType is normally called during view updates; apply it for the unit test. view.commitStagedInputType() assertThat(view.ellipsize).isNull()