Skip to content

Commit e5fa427

Browse files
committed
feat: outline style override
1 parent 0050bfb commit e5fa427

5 files changed

Lines changed: 37 additions & 16 deletions

File tree

src/components/TextField/TextField.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,14 @@ export interface TextFieldProps extends TextInputProps {
180180
* the label and TextInput, excluding accessories).
181181
*/
182182
containerStyle?: StyleProp<ViewStyle>;
183+
/**
184+
* Style overrides for the indicator layer (the purely visual border or line
185+
* that shows state, not the interactive input).
186+
* - `filled` — applied to both the always-visible bottom edge and the
187+
* animated bar that expands on focus.
188+
* - `outlined` — applied to the rounded border around the field for both states.
189+
*/
190+
outlineStyle?: StyleProp<ViewStyle>;
183191
theme?: ThemeProp;
184192
/**
185193
* An optional component to render on the start side of the input (leading in LTR).
@@ -251,6 +259,7 @@ function TextField(props: TextFieldProps) {
251259
pressableStyle: $pressableStyleOverride,
252260
fieldStyle,
253261
containerStyle,
262+
outlineStyle,
254263
theme,
255264
StartAccessory,
256265
EndAccessory,
@@ -364,9 +373,9 @@ function TextField(props: TextFieldProps) {
364373
onBlur={onBlurHandler}
365374
selectionColor={$selectionColor}
366375
cursorColor={$cursorColor}
376+
placeholderTextColor={$placeholderTextColor}
367377
{...textInputProps}
368378
placeholder={placeholder}
369-
placeholderTextColor={$placeholderTextColor}
370379
style={$inputStyles}
371380
/>
372381

src/components/TextField/filled/logic.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,14 @@ export const getFilledTextFieldData = (
3737
style: $inputStyleOverride,
3838
fieldStyle: $fieldStyleOverride,
3939
containerStyle: $containerStyleOverride,
40+
outlineStyle: $outlineStyleOverride,
4041
...textInputProps
4142
} = props;
4243

4344
const {
4445
input,
4546
theme,
47+
isFocused,
4648
hasSuffix,
4749
disabled,
4850
hasAccessory,
@@ -62,14 +64,7 @@ export const getFilledTextFieldData = (
6264
const outlineColor = getOutlineColor({
6365
theme,
6466
hasError,
65-
isFocused: false,
66-
disabled,
67-
});
68-
69-
const activeOutlineColor = getOutlineColor({
70-
theme,
71-
hasError,
72-
isFocused: true,
67+
isFocused,
7368
disabled,
7469
});
7570

@@ -126,13 +121,14 @@ export const getFilledTextFieldData = (
126121
]
127122
: undefined;
128123

129-
const $outlineStyles = [
124+
const $outlineStyles: StyleProp<ViewStyle> = [
130125
$outlineStyle,
131126
{
132127
height: INACTIVE_INDICATOR_SIZE,
133128
backgroundColor: outlineColor,
134129
},
135130
disabled && $disabledStyle,
131+
$outlineStyleOverride,
136132
];
137133

138134
const $animatedActiveOutlineStyles: StyleProp<
@@ -141,9 +137,10 @@ export const getFilledTextFieldData = (
141137
$outlineStyle,
142138
{
143139
height: ACTIVE_INDICATOR_SIZE,
144-
backgroundColor: activeOutlineColor,
140+
backgroundColor: outlineColor,
145141
},
146142
disabled && $disabledStyle,
143+
$outlineStyleOverride,
147144
$animatedActiveOutlineStyle,
148145
];
149146

src/components/TextField/outlined/logic.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export const getOutlinedTextFieldData = (
3131
style: $inputStyleOverride,
3232
fieldStyle: $fieldStyleOverride,
3333
containerStyle: $containerStyleOverride,
34+
outlineStyle: $outlineStyleOverride,
3435
...textInputProps
3536
} = props;
3637

@@ -88,14 +89,15 @@ export const getOutlinedTextFieldData = (
8889
/* The outline is a childless absolutely-positioned View, so applying
8990
`opacity` here is safe and lets us pass `outlineColor` through unchanged
9091
(including PlatformColor values on Android). */
91-
const $outlineStyles = [
92+
const $outlineStyles: StyleProp<ViewStyle> = [
9293
$outlineStyle,
9394
{
9495
borderWidth: isFocused ? 2 : 1,
9596
borderColor: outlineColor,
9697
},
9798
disabled && { opacity: DISABLED_OUTLINE_OPACITY },
9899
$fieldStyleOverride,
100+
$outlineStyleOverride,
99101
];
100102

101103
const $animatedLabelWrapperStyles: StyleProp<

src/components/__tests__/TextField.test.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ it('does not pass TextField-only props through to TextInput', () => {
329329
expect(input.props.pressableStyle).toBeUndefined();
330330
expect(input.props.fieldStyle).toBeUndefined();
331331
expect(input.props.containerStyle).toBeUndefined();
332+
expect(input.props.outlineStyle).toBeUndefined();
332333
expect(input.props.supportingText).toBeUndefined();
333334
expect(input.props.supportingTextProps).toBeUndefined();
334335
expect(input.props.prefix).toBeUndefined();

src/components/__tests__/__snapshots__/TextField.test.tsx.snap

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ exports[`renders filled TextField with TextField.Icon accessories 1`] = `
6666
"height": 1,
6767
},
6868
false,
69+
undefined,
6970
]
7071
}
7172
/>
@@ -90,10 +91,11 @@ exports[`renders filled TextField with TextField.Icon accessories 1`] = `
9091
"right": 0,
9192
},
9293
{
93-
"backgroundColor": "rgba(103, 80, 164, 1)",
94+
"backgroundColor": "rgba(121, 116, 126, 1)",
9495
"height": 2,
9596
},
9697
false,
98+
undefined,
9799
{
98100
"transform": [
99101
{
@@ -115,10 +117,11 @@ exports[`renders filled TextField with TextField.Icon accessories 1`] = `
115117
"right": 0,
116118
},
117119
{
118-
"backgroundColor": "rgba(103, 80, 164, 1)",
120+
"backgroundColor": "rgba(121, 116, 126, 1)",
119121
"height": 2,
120122
},
121123
false,
124+
undefined,
122125
{
123126
"transform": [
124127
{
@@ -711,6 +714,7 @@ exports[`renders filled TextField with TextField.Icon accessories when error is
711714
"height": 1,
712715
},
713716
false,
717+
undefined,
714718
]
715719
}
716720
/>
@@ -739,6 +743,7 @@ exports[`renders filled TextField with TextField.Icon accessories when error is
739743
"height": 2,
740744
},
741745
false,
746+
undefined,
742747
{
743748
"transform": [
744749
{
@@ -764,6 +769,7 @@ exports[`renders filled TextField with TextField.Icon accessories when error is
764769
"height": 2,
765770
},
766771
false,
772+
undefined,
767773
{
768774
"transform": [
769775
{
@@ -1356,6 +1362,7 @@ exports[`renders filled TextField with label and value 1`] = `
13561362
"height": 1,
13571363
},
13581364
false,
1365+
undefined,
13591366
]
13601367
}
13611368
/>
@@ -1380,10 +1387,11 @@ exports[`renders filled TextField with label and value 1`] = `
13801387
"right": 0,
13811388
},
13821389
{
1383-
"backgroundColor": "rgba(103, 80, 164, 1)",
1390+
"backgroundColor": "rgba(121, 116, 126, 1)",
13841391
"height": 2,
13851392
},
13861393
false,
1394+
undefined,
13871395
{
13881396
"transform": [
13891397
{
@@ -1405,10 +1413,11 @@ exports[`renders filled TextField with label and value 1`] = `
14051413
"right": 0,
14061414
},
14071415
{
1408-
"backgroundColor": "rgba(103, 80, 164, 1)",
1416+
"backgroundColor": "rgba(121, 116, 126, 1)",
14091417
"height": 2,
14101418
},
14111419
false,
1420+
undefined,
14121421
{
14131422
"transform": [
14141423
{
@@ -1676,6 +1685,7 @@ exports[`renders outlined TextField with TextField.Icon accessories 1`] = `
16761685
},
16771686
false,
16781687
undefined,
1688+
undefined,
16791689
]
16801690
}
16811691
/>
@@ -2279,6 +2289,7 @@ exports[`renders outlined TextField with TextField.Icon accessories when error i
22792289
},
22802290
false,
22812291
undefined,
2292+
undefined,
22822293
]
22832294
}
22842295
/>
@@ -2882,6 +2893,7 @@ exports[`renders outlined TextField with label and value 1`] = `
28822893
},
28832894
false,
28842895
undefined,
2896+
undefined,
28852897
]
28862898
}
28872899
/>

0 commit comments

Comments
 (0)