Repository navigation
Preserve TextInput selection color when changing multiline - #57748
Closed
fallintoplace wants to merge 1 commit into
Closed
fallintoplace wants to merge 1 commit into
fallintoplace wants to merge 1 commit into
Conversation
|
@Abbondanzo has imported this pull request. If you are a Meta employee, you can view this in D113980213. |
|
@Abbondanzo merged this pull request in 56c284e. |
meta-codesync Bot
pushed a commit
that referenced
this pull request
Sep 8, 2026
Summary: `RCTCopyBackedTextInput` is called when a `TextInput` switches between single-line and multiline and the Fabric component view swaps its backing UIKit view (`RCTUITextField` <-> `RCTUITextView`). It copies the keyboard traits across to the replacement view, but `returnKeyType` is not among them — `keyboardType` is copied on the line directly above it. Because the prop itself has not changed across the transition, the prop diff in `RCTTextInputComponentView` sees `done` -> `done` and does not re-run the setter. The replacement view therefore keeps its default Return key, so an input declared `returnKeyType="done"` renders the newline key instead of Done once it has flipped to multiline. This is the same class of omission that was fixed for `tintColor` in #57748, and the fix is the same shape: copy the property along with its neighbours. Repro: a `TextInput` with a stable key and `returnKeyType="done"` whose `multiline` prop goes `false` -> `true`. After the transition, focusing it shows the default Return key. Fabric view recycling can produce the same backing-view transition even for an input that is always multiline, which makes it intermittent in real apps. Verified against 0.86.3; the line is still missing on 0.87.1 and on `main`. ## Changelog: [IOS] [FIXED] - Preserve TextInput returnKeyType when switching between single-line and multiline backing views. Pull Request resolved: #58355 Test Plan: Added `testCopyBackedTextInputPreservesReturnKeyType` to `React/Tests/Text/RCTTextInputUtilsTest.mm`, alongside the existing `testCopyBackedTextInputPreservesTintColor` added in #57748. It sets `returnKeyType = UIReturnKeyDone` on an `RCTUITextField`, runs `RCTCopyBackedTextInput` into an `RCTUITextView`, and asserts the value survives. The test fails without the one-line change and passes with it. Also confirmed in a production app: a multiline `TextInput` with `returnKeyType="done"` intermittently displayed the newline key, and adding this single line fixed it. Reviewed By: christophpurrer Differential Revision: D119030795 Pulled By: Abbondanzo fbshipit-source-id: 0804a0b013f23b6c4533e4bb3d11cb13df81835c
shashikant-panchal
pushed a commit
to shashikant-panchal/react-native
that referenced
this pull request
Sep 10, 2026
Summary: `RCTCopyBackedTextInput` is called when a `TextInput` switches between single-line and multiline and the Fabric component view swaps its backing UIKit view (`RCTUITextField` <-> `RCTUITextView`). It copies the keyboard traits across to the replacement view, but `returnKeyType` is not among them — `keyboardType` is copied on the line directly above it. Because the prop itself has not changed across the transition, the prop diff in `RCTTextInputComponentView` sees `done` -> `done` and does not re-run the setter. The replacement view therefore keeps its default Return key, so an input declared `returnKeyType="done"` renders the newline key instead of Done once it has flipped to multiline. This is the same class of omission that was fixed for `tintColor` in react#57748, and the fix is the same shape: copy the property along with its neighbours. Repro: a `TextInput` with a stable key and `returnKeyType="done"` whose `multiline` prop goes `false` -> `true`. After the transition, focusing it shows the default Return key. Fabric view recycling can produce the same backing-view transition even for an input that is always multiline, which makes it intermittent in real apps. Verified against 0.86.3; the line is still missing on 0.87.1 and on `main`. ## Changelog: [IOS] [FIXED] - Preserve TextInput returnKeyType when switching between single-line and multiline backing views. Pull Request resolved: react#58355 Test Plan: Added `testCopyBackedTextInputPreservesReturnKeyType` to `React/Tests/Text/RCTTextInputUtilsTest.mm`, alongside the existing `testCopyBackedTextInputPreservesTintColor` added in react#57748. It sets `returnKeyType = UIReturnKeyDone` on an `RCTUITextField`, runs `RCTCopyBackedTextInput` into an `RCTUITextView`, and asserts the value survives. The test fails without the one-line change and passes with it. Also confirmed in a production app: a multiline `TextInput` with `returnKeyType="done"` intermittently displayed the newline key, and adding this single line fixed it. Reviewed By: christophpurrer Differential Revision: D119030795 Pulled By: Abbondanzo fbshipit-source-id: 0804a0b013f23b6c4533e4bb3d11cb13df81835c
tvinhas
pushed a commit
to tvinhas/react-native-macos
that referenced
this pull request
Oct 21, 2026
Summary: - Preserve the native text input tint color when switching between single-line and multiline backing views. - Add a native regression test for copying the tint from `RCTUITextField` to `RCTUITextView`. Fabric replaces the backing text input view when `multiline` changes. `RCTCopyBackedTextInput` preserved the other native presentation properties but omitted `tintColor`, which implements `selectionColor` on iOS. Since the React prop itself was unchanged, the normal prop diff did not apply it again and the selection color fell back to the system tint. Fixes react#57105 ## Changelog: [IOS] [FIXED] - Preserve TextInput selection color when changing multiline. Pull Request resolved: react#57748 Test Plan: - Added `RCTTextInputUtilsTest.testCopyBackedTextInputPreservesTintColor`. - Ran clang-format validation and `git diff --check`. Reviewed By: javache Differential Revision: D113980213 Pulled By: Abbondanzo fbshipit-source-id: 73a5f87e1b45b504f4640572b8c1a26b4d97f43f
meta-codesync Bot
pushed a commit
that referenced
this pull request
Oct 22, 2026
Summary: Fixes #59132. On iOS, a single-line `TextInput` with a `placeholder` and no `lineHeight` can draw its placeholder below the field's vertical centre. It happens when the field's native view was last used by a `TextInput` with a `lineHeight` (Fabric recycles `RCTTextInputComponentView`s), and when a `lineHeight` is removed from a mounted single-line field. Typed text is not affected. UITextField's placeholder label keeps the paragraph style of the previous `attributedPlaceholder` and applies it to a new placeholder that has none. `RCTNSTextAttributesFromTextAttributes` only adds an `NSParagraphStyle` when an alignment, writing direction, line break mode or strategy, or line height is set, so once the `lineHeight` is gone the new placeholder has no paragraph style, and the label keeps measuring and drawing it with the old line height. Setting `attributedPlaceholder` to `nil` first doesn't reset it. This change gives the placeholder a paragraph style of its own when the text attributes have none: an `NSMutableParagraphStyle` with `lineBreakMode = NSLineBreakByTruncatingTail`, which is what a fresh placeholder label uses. A paragraph style from the text attributes is used as before. In a right-to-left layout `RCTNSTextAttributesFromTextAttributes` always adds a paragraph style, so the new one only applies to left-to-right layouts without `textAlign`. In a plain UIKit test on the simulator, compared with a fresh `UITextField`, the placeholder renders to the same pixels (English and Hebrew text, with and without `semanticContentAttribute` forced to right-to-left), and `sizeWithAttributes:`, which `intrinsicContentSize` uses, returns the same size. The multiline `RCTUITextView` is unchanged: its placeholder is a plain `UILabel`, which doesn't keep the previous paragraph style. Related: #56487 changes `_placeholderTextAttributes` for fields whose `lineHeight` is larger than the font; this change is about fields without a `lineHeight`. #59184 (fixed in #59190) is another piece of state that outlives the recycling of `RCTTextInputComponentView`. #57748 and #58355 fixed other properties lost on the same single-line/multiline view swap. ## Changelog: [IOS] [FIXED] - Fix `TextInput` placeholder drawn below centre after its view is reused from a `TextInput` with `lineHeight` Pull Request resolved: #59195 Test Plan: Added `RCTUITextFieldTests` with two cases: - the placeholder loses a line height that was removed from the text attributes - a paragraph style from the text attributes is kept for the placeholder `React/Tests` isn't compiled by any open source target, so I added the file to the `RNTesterUnitTests` target locally (not part of this change) and ran it on an iPhone 17 Pro simulator, iOS 26.5: - without the fix: `testPlaceholderDropsLineHeightRemovedFromTextAttributes` fails (`((paragraphStyle) != nil) failed`), the other test passes - with the fix: `** TEST SUCCEEDED **`, both tests pass I tested manually in RNTester (Debug, new architecture, iPhone 17 Pro simulator, iOS 26.5) with this Playground code: <details> <summary>RNTesterPlayground.js</summary> ```js function Field({label, children}) { return ( <View style={styles.field}> <RNTesterText style={styles.label}>{label}</RNTesterText> <View style={styles.box}> {children} <View pointerEvents="none" style={styles.centreLine} /> </View> </View> ); } function Playground() { const [stage, setStage] = useState(0); const [lineHeight, setLineHeight] = useState(true); return ( <View style={styles.container}> <Field label="Reference (fresh view)"> <TextInput placeholder="Search…" style={styles.singleLine} /> </Field> <Button title="1. Mount multiline" onPress={() => setStage(1)} /> <Button title="2. Unmount it" onPress={() => setStage(2)} /> <Button title="3. Mount single-line" onPress={() => setStage(3)} /> {stage === 1 ? ( <TextInput key="multiline" multiline style={{fontSize: 17, lineHeight: 34, borderWidth: 1}} /> ) : null} {stage === 3 ? ( <Field label="Single-line (reused view)"> <TextInput key="singleline" placeholder="Search…" style={styles.singleLine} /> </Field> ) : null} <Button title={lineHeight ? 'Remove lineHeight' : 'Add lineHeight'} onPress={() => setLineHeight(value => !value)} /> <Field label={`Single-line, lineHeight ${lineHeight ? '34' : 'unset'}`}> <TextInput placeholder="Search…" style={[styles.singleLine, lineHeight ? {lineHeight: 34} : null]} /> </Field> </View> ); } const styles = StyleSheet.create({ box: {borderColor: '#888', borderWidth: StyleSheet.hairlineWidth}, centreLine: { backgroundColor: 'red', height: 1, left: 0, position: 'absolute', right: 0, top: 20.5, }, container: {gap: 8, padding: 10}, field: {gap: 4}, label: {fontSize: 12}, singleLine: {fontSize: 17, height: 42, paddingHorizontal: 12}, }); ``` </details> Steps: tap **1**, **2** and **3**. The single-line field mounted in step 3 reuses the view of the multiline field from step 1. Then tap **Remove lineHeight**. | Before | After | | --- | --- | | <img width="300" alt="59132-before" src="https://github.com/user-attachments/assets/34076c45-1736-4af2-98d4-396b57f82400" /> | <img width="300" alt="59132-after" src="https://github.com/user-attachments/assets/1c995d1c-9bfb-4efd-bbb1-c87a04e47d19" /> | Before: the placeholder of the reused field and the placeholder of the field whose `lineHeight` was removed sit below the red centre line. After: both are centred like the reference field. Tapping **Add lineHeight** again gives the field its `lineHeight` placeholder back, the same as without this change. Commands: ```sh yarn install yarn --cwd packages/react-native-codegen build cd packages/rn-tester pod install pod update hermes-engine --no-repo-update # Podfile.lock pinned an older Hermes cd ../.. xcodebuild -workspace packages/rn-tester/RNTesterPods.xcworkspace -scheme RNTester -configuration Debug \ -destination 'platform=iOS Simulator,id=<iPhone 17 Pro, iOS 26.5 simulator UDID>' build # ** BUILD SUCCEEDED ** xcodebuild test -workspace packages/rn-tester/RNTesterPods.xcworkspace -scheme RNTester -configuration Debug \ -destination 'platform=iOS Simulator,id=<iPhone 17 Pro, iOS 26.5 simulator UDID>' \ -only-testing:RNTesterUnitTests/RCTUITextFieldTests # with RCTUITextFieldTests.mm added to the target locally # ** TEST SUCCEEDED ** node scripts/clang-format.js --check \ packages/react-native/Libraries/Text/TextInput/Singleline/RCTUITextField.mm \ packages/react-native/React/Tests/Text/RCTUITextFieldTests.mm # no output, exit code 0 ``` Reviewed By: christophpurrer Differential Revision: D126309241 Pulled By: cipolleschi fbshipit-source-id: 04ffa2930e3884f7a60d2dd9407d779ffc5f7775
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary:
RCTUITextFieldtoRCTUITextView.Fabric replaces the backing text input view when
multilinechanges.RCTCopyBackedTextInputpreserved the other native presentation properties but omittedtintColor, which implementsselectionColoron iOS. Since the React prop itself was unchanged, the normal prop diff did not apply it again and the selection color fell back to the system tint.Fixes #57105
Changelog:
[IOS] [FIXED] - Preserve TextInput selection color when changing multiline.
Test Plan:
RCTTextInputUtilsTest.testCopyBackedTextInputPreservesTintColor.git diff --check.