Skip to content

Commit ca29d38

Browse files
SJvaca30meta-codesync[bot]
authored andcommitted
fix(text): support start and end text alignment (#57201)
Summary: Closes #45255. Adds `textAlign: 'start' | 'end'` support for Text and TextInput across the JS types, Android, iOS, and Fabric text conversion paths. - Android legacy Text and TextInput now accept logical `start`/`end` alignment values. - Fabric preserves `start`/`end` as distinct `TextAlignment` values and resolves them against layout direction for iOS paragraph layout. - Existing `left`/`right` behavior is left unchanged to avoid changing current RTL semantics. This replaces #57007 because the original fork became locked and could not be updated after the upstream conflict. ## Changelog: [GENERAL] [ADDED] - Add support for `textAlign: 'start'` and `textAlign: 'end'`. Pull Request resolved: #57201 Test Plan: - `yarn build-types` - `yarn test-typescript` - `yarn flow-check` - `./node_modules/.bin/prettier --check packages/react-native/Libraries/Components/TextInput/TextInput.d.ts packages/react-native/Libraries/Components/TextInput/TextInput.flow.js packages/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js packages/react-native/types/__typetests__/index.tsx packages/react-native/ReactNativeApi.d.ts` - `./gradlew ktfmtCheck -Preact.internal.useHermesStable=true --no-daemon` - `./gradlew :packages:react-native:ReactAndroid:testDebugUnitTest --tests com.facebook.react.views.textinput.ReactTextInputPropertyTest.testTextAlign --tests com.facebook.react.views.text.TextAttributePropsTest -Preact.internal.useHermesStable=true --no-daemon` - `./gradlew ':packages:react-native:ReactAndroid:buildCMakeDebug[arm64-v8a][hermestooling,jsi,etc]' -Preact.internal.useHermesStable=true --no-daemon` - `git diff --check` The Android unit test and CMake checks were run from an ASCII-only temporary worktree because Kotlin unit test compilation in my main checkout fails before running these tests when the workspace path contains non-ASCII characters. Reviewed By: christophpurrer Differential Revision: D108628602 Pulled By: javache fbshipit-source-id: 28111d0553451d7de0424a07e956f71bda8787ca
1 parent fd10410 commit ca29d38

27 files changed

Lines changed: 196 additions & 27 deletions

File tree

packages/react-native/Libraries/Components/TextInput/TextInput.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -979,9 +979,9 @@ export interface TextInputProps
979979
style?: StyleProp<TextStyle> | undefined;
980980

981981
/**
982-
* Align the input text to the left, center, or right sides of the input field.
982+
* Align the input text to the left, center, right, start, or end side of the input field.
983983
*/
984-
textAlign?: 'left' | 'center' | 'right' | undefined;
984+
textAlign?: 'left' | 'center' | 'right' | 'start' | 'end' | undefined;
985985

986986
/**
987987
* Used to locate this view in end-to-end tests

packages/react-native/Libraries/Components/TextInput/TextInput.flow.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1051,9 +1051,9 @@ type TextInputBaseProps = Readonly<{
10511051
value?: ?Stringish,
10521052

10531053
/**
1054-
* Align the input text to the left, center, or right sides of the input field.
1054+
* Align the input text to the left, center, right, start, or end side of the input field.
10551055
*/
1056-
textAlign?: ?('left' | 'center' | 'right'),
1056+
textAlign?: ?('left' | 'center' | 'right' | 'start' | 'end'),
10571057
}>;
10581058

10591059
/** @build-types emit-as-interface Uniwind compatibility */

packages/react-native/Libraries/StyleSheet/StyleSheetTypes.d.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,15 @@ export interface TextStyle extends TextStyleIOS, TextStyleAndroid, ViewStyle {
634634
| undefined;
635635
letterSpacing?: number | undefined;
636636
lineHeight?: number | undefined;
637-
textAlign?: 'auto' | 'left' | 'right' | 'center' | 'justify' | undefined;
637+
textAlign?:
638+
| 'auto'
639+
| 'left'
640+
| 'right'
641+
| 'center'
642+
| 'justify'
643+
| 'start'
644+
| 'end'
645+
| undefined;
638646
textDecorationLine?:
639647
| 'none'
640648
| 'underline'

packages/react-native/Libraries/StyleSheet/StyleSheetTypes.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1031,7 +1031,14 @@ type ____TextStyle_InternalBase = Readonly<{
10311031
textShadowColor?: ____ColorValue_Internal,
10321032
letterSpacing?: number,
10331033
lineHeight?: number,
1034-
textAlign?: 'auto' | 'left' | 'right' | 'center' | 'justify',
1034+
textAlign?:
1035+
| 'auto'
1036+
| 'left'
1037+
| 'right'
1038+
| 'center'
1039+
| 'justify'
1040+
| 'start'
1041+
| 'end',
10351042
textAlignVertical?: 'auto' | 'top' | 'bottom' | 'center',
10361043
includeFontPadding?: boolean,
10371044
textDecorationLine?:

packages/react-native/React/Base/RCTConvert.mm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,8 @@ + (NSLocale *)NSLocale:(id)json
316316
NSTextAlignment,
317317
(@{
318318
@"auto" : @(NSTextAlignmentNatural),
319+
@"start" : @(NSTextAlignmentLeft),
320+
@"end" : @(NSTextAlignmentRight),
319321
@"left" : @(NSTextAlignmentLeft),
320322
@"center" : @(NSTextAlignmentCenter),
321323
@"right" : @(NSTextAlignmentRight),

packages/react-native/React/Tests/Text/RCTAttributedTextUtilsTest.mm

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010

1111
#import <react/renderer/textlayoutmanager/RCTAttributedTextUtils.h>
1212

13+
#include <react/renderer/attributedstring/conversions.h>
14+
#include <react/renderer/core/RawValue.h>
15+
1316
using namespace facebook::react;
1417

1518
@interface RCTAttributedTextUtilsTest : XCTestCase
@@ -18,6 +21,30 @@ @interface RCTAttributedTextUtilsTest : XCTestCase
1821

1922
@implementation RCTAttributedTextUtilsTest
2023

24+
static NSTextAlignment NSTextAlignmentFromTextAlign(NSString *textAlign, LayoutDirection layoutDirection)
25+
{
26+
ContextContainer contextContainer{};
27+
PropsParserContext parserContext{-1, contextContainer};
28+
TextAlignment textAlignment = TextAlignment::Natural;
29+
fromRawValue(parserContext, RawValue{folly::dynamic{textAlign.UTF8String}}, textAlignment);
30+
31+
TextAttributes textAttributes;
32+
textAttributes.alignment = textAlignment;
33+
textAttributes.layoutDirection = layoutDirection;
34+
35+
NSDictionary<NSAttributedStringKey, id> *attributes = RCTNSTextAttributesFromTextAttributes(textAttributes);
36+
NSParagraphStyle *paragraphStyle = attributes[NSParagraphStyleAttributeName];
37+
return paragraphStyle.alignment;
38+
}
39+
40+
- (void)testTextAlignmentStartAndEndResolveWithLayoutDirection
41+
{
42+
XCTAssertEqual(NSTextAlignmentFromTextAlign(@"start", LayoutDirection::LeftToRight), NSTextAlignmentLeft);
43+
XCTAssertEqual(NSTextAlignmentFromTextAlign(@"start", LayoutDirection::RightToLeft), NSTextAlignmentRight);
44+
XCTAssertEqual(NSTextAlignmentFromTextAlign(@"end", LayoutDirection::LeftToRight), NSTextAlignmentRight);
45+
XCTAssertEqual(NSTextAlignmentFromTextAlign(@"end", LayoutDirection::RightToLeft), NSTextAlignmentLeft);
46+
}
47+
2148
- (void)testSamenessOfEmptyAttributedStrings
2249
{
2350
NSAttributedString *attributedString1 = [[NSAttributedString alloc] initWithString:@""];

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,8 @@ public class TextAttributeProps private constructor() {
527527
"justify" -> Gravity.LEFT
528528
null,
529529
"auto" -> Gravity.NO_GRAVITY
530+
"start" -> if (isRTL) Gravity.RIGHT else Gravity.LEFT
531+
"end" -> if (isRTL) Gravity.LEFT else Gravity.RIGHT
530532
"left" -> if (isRTL) Gravity.RIGHT else Gravity.LEFT
531533
"right" -> if (isRTL) Gravity.LEFT else Gravity.RIGHT
532534
"center" -> Gravity.CENTER_HORIZONTAL

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ internal object TextLayoutManager {
211211

212212
if (alignmentAttr == "center") {
213213
alignment = Layout.Alignment.ALIGN_CENTER
214-
} else if (alignmentAttr == "right") {
214+
} else if (alignmentAttr == "right" || alignmentAttr == "end") {
215215
alignment =
216216
if (swapNormalAndOpposite) Layout.Alignment.ALIGN_NORMAL
217217
else Layout.Alignment.ALIGN_OPPOSITE

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/textinput/ReactTextInputManager.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,8 @@ public open class ReactTextInputManager public constructor() :
558558
when (textAlign) {
559559
null,
560560
"auto" -> view.gravityHorizontal = Gravity.NO_GRAVITY
561+
"start" -> view.gravityHorizontal = Gravity.START
562+
"end" -> view.gravityHorizontal = Gravity.END
561563
"left" -> view.gravityHorizontal = Gravity.LEFT
562564
"right" -> view.gravityHorizontal = Gravity.RIGHT
563565
"center" -> view.gravityHorizontal = Gravity.CENTER_HORIZONTAL
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
package com.facebook.react.views.text
9+
10+
import android.view.Gravity
11+
import com.facebook.react.bridge.JavaOnlyMap
12+
import com.facebook.react.uimanager.ReactStylesDiffMap
13+
import org.assertj.core.api.Assertions.assertThat
14+
import org.junit.Test
15+
16+
class TextAttributePropsTest {
17+
18+
@Test
19+
fun textAlignStartUsesStartSide() {
20+
assertThat(textAlignment("start", isRTL = false)).isEqualTo(Gravity.LEFT)
21+
assertThat(textAlignment("start", isRTL = true)).isEqualTo(Gravity.RIGHT)
22+
}
23+
24+
@Test
25+
fun textAlignEndUsesEndSide() {
26+
assertThat(textAlignment("end", isRTL = false)).isEqualTo(Gravity.RIGHT)
27+
assertThat(textAlignment("end", isRTL = true)).isEqualTo(Gravity.LEFT)
28+
}
29+
30+
private fun textAlignment(textAlign: String, isRTL: Boolean): Int {
31+
return TextAttributeProps.getTextAlignment(
32+
ReactStylesDiffMap(JavaOnlyMap.of("textAlign", textAlign)),
33+
isRTL,
34+
Gravity.CENTER_HORIZONTAL,
35+
)
36+
}
37+
}

0 commit comments

Comments
 (0)