Skip to content

Commit 03bcb3b

Browse files
committed
fix: workaround for fixing textinput styling
1 parent 5d943df commit 03bcb3b

File tree

6 files changed

+45
-41
lines changed

6 files changed

+45
-41
lines changed

src/components/TextInput/TextInput.tsx

+7-16
Original file line numberDiff line numberDiff line change
@@ -241,9 +241,8 @@ const TextInput = forwardRef<TextInputHandles, Props>(
241241
new Animated.Value(errorProp ? 1 : 0)
242242
);
243243
const [focused, setFocused] = React.useState<boolean>(false);
244-
const [placeholder, setPlaceholder] = React.useState<string | undefined>(
245-
' '
246-
);
244+
const [displayPlaceholder, setDisplayPlaceholder] =
245+
React.useState<boolean>(false);
247246
const [uncontrolledValue, setUncontrolledValue] = React.useState<
248247
string | undefined
249248
>(validInputValue);
@@ -328,24 +327,16 @@ const TextInput = forwardRef<TextInputHandles, Props>(
328327
// If the user wants to use the contextMenu, when changing the placeholder, the contextMenu is closed
329328
// This is a workaround to mitigate this behavior in scenarios where the placeholder is not specified.
330329
if (rest.placeholder) {
331-
// Set the placeholder in a delay to offset the label animation
330+
// Display placeholder in a delay to offset the label animation
332331
// If we show it immediately, they'll overlap and look ugly
333332
timer.current = setTimeout(
334-
() => setPlaceholder(rest.placeholder),
333+
() => setDisplayPlaceholder(true),
335334
50
336335
) as unknown as NodeJS.Timeout;
337336
}
338337
} else {
339338
// hidePlaceholder
340-
341-
// Issue: https://github.com/callstack/react-native-paper/issues/3138
342-
// Description: Changing the placeholder text value dynamically,
343-
// within multiline input on iOS, doesn't work properly –
344-
// the placeholder is not displayed initially.
345-
// Root cause: Placeholder initial value, which has length 0.
346-
// More context: The issue was also reproduced in react-native, using its own TextInput.
347-
// Workaround: Set an empty space character in the default value.
348-
setPlaceholder(' ');
339+
setDisplayPlaceholder(false);
349340
}
350341

351342
return () => {
@@ -500,7 +491,7 @@ const TextInput = forwardRef<TextInputHandles, Props>(
500491
labeled,
501492
error,
502493
focused,
503-
placeholder,
494+
displayPlaceholder,
504495
value,
505496
labelTextLayout,
506497
labelLayout,
@@ -542,7 +533,7 @@ const TextInput = forwardRef<TextInputHandles, Props>(
542533
labeled,
543534
error,
544535
focused,
545-
placeholder,
536+
displayPlaceholder,
546537
value,
547538
labelTextLayout,
548539
labelLayout,

src/components/TextInput/TextInputFlat.tsx

+9-2
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,13 @@ const TextInputFlat = ({
229229
? parentState.labeled
230230
: placeholderOpacityAnims[parentState.labelLayout.measured ? 1 : 0];
231231

232+
// We don't want to show placeholder if label is displayed, because they overlap.
233+
// Before it was done by setting placeholder's value to " ", but inputs have the same props
234+
// what causes broken styles due to: https://github.com/facebook/react-native/issues/48249
235+
const placeholderTextColorBasedOnState = parentState.displayPlaceholder
236+
? placeholderTextColor ?? placeholderColor
237+
: 'transparent';
238+
232239
const minHeight =
233240
height ||
234241
(dense ? (label ? MIN_DENSE_HEIGHT_WL : MIN_DENSE_HEIGHT) : MIN_HEIGHT);
@@ -388,12 +395,12 @@ const TextInputFlat = ({
388395
...rest,
389396
ref: innerRef,
390397
onChangeText,
391-
placeholder: label ? parentState.placeholder : rest.placeholder,
398+
placeholder: rest.placeholder,
392399
editable: !disabled && editable,
393400
selectionColor,
394401
cursorColor:
395402
typeof cursorColor === 'undefined' ? activeColor : cursorColor,
396-
placeholderTextColor: placeholderTextColor ?? placeholderColor,
403+
placeholderTextColor: placeholderTextColorBasedOnState,
397404
onFocus,
398405
onBlur,
399406
underlineColorAndroid: 'transparent',

src/components/TextInput/TextInputOutlined.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@ const TextInputOutlined = ({
206206
paddingHorizontal: INPUT_PADDING_HORIZONTAL,
207207
};
208208

209+
const placeholderTextColorBasedOnState = parentState.displayPlaceholder
210+
? placeholderTextColor ?? placeholderColor
211+
: 'transparent';
212+
209213
const labelBackgroundColor: ColorValue =
210214
backgroundColor === 'transparent'
211215
? theme.colors.background
@@ -379,12 +383,12 @@ const TextInputOutlined = ({
379383
ref: innerRef,
380384
onLayout: onLayoutChange,
381385
onChangeText,
382-
placeholder: label ? parentState.placeholder : rest.placeholder,
386+
placeholder: rest.placeholder,
383387
editable: !disabled && editable,
384388
selectionColor,
385389
cursorColor:
386390
typeof cursorColor === 'undefined' ? activeColor : cursorColor,
387-
placeholderTextColor: placeholderTextColor || placeholderColor,
391+
placeholderTextColor: placeholderTextColorBasedOnState,
388392
onFocus,
389393
onBlur,
390394
underlineColorAndroid: 'transparent',

src/components/TextInput/types.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export type State = {
7272
labeled: Animated.Value;
7373
error: Animated.Value;
7474
focused: boolean;
75-
placeholder?: string;
75+
displayPlaceholder: boolean;
7676
value?: string;
7777
labelTextLayout: { width: number };
7878
labelLayout: { measured: boolean; width: number; height: number };

src/components/__tests__/TextInput.test.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,14 @@ it('renders label with correct color when inactive', () => {
245245
});
246246
});
247247

248-
it('renders input placeholder initially with an empty space character', () => {
248+
it('renders input placeholder initially with transparent placeholderTextColor', () => {
249249
const { getByTestId } = render(
250250
<TextInput multiline label="Multiline input" testID={'text-input'} />
251251
);
252252

253-
expect(getByTestId('text-input').props.placeholder).toBe(' ');
253+
expect(getByTestId('text-input').props.placeholderTextColor).toBe(
254+
'transparent'
255+
);
254256
});
255257

256258
it('correctly applies padding offset to input label on Android when RTL', () => {

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

+18-18
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,8 @@ exports[`call onPress when affix adornment pressed 1`] = `
182182
onBlur={[Function]}
183183
onChangeText={[Function]}
184184
onFocus={[Function]}
185-
placeholder=" "
186-
placeholderTextColor="rgba(73, 69, 79, 1)"
185+
placeholder="Enter your phone number"
186+
placeholderTextColor="transparent"
187187
selectionColor="rgba(103, 80, 164, 1)"
188188
style={
189189
[
@@ -494,8 +494,8 @@ exports[`correctly applies a component as the text label 1`] = `
494494
onBlur={[Function]}
495495
onChangeText={[Function]}
496496
onFocus={[Function]}
497-
placeholder=" "
498-
placeholderTextColor="rgba(73, 69, 79, 1)"
497+
placeholder="Type something"
498+
placeholderTextColor="transparent"
499499
selectionColor="rgba(103, 80, 164, 1)"
500500
style={
501501
[
@@ -718,8 +718,8 @@ exports[`correctly applies cursorColor prop 1`] = `
718718
onBlur={[Function]}
719719
onChangeText={[Function]}
720720
onFocus={[Function]}
721-
placeholder=" "
722-
placeholderTextColor="rgba(73, 69, 79, 1)"
721+
placeholder="Type something"
722+
placeholderTextColor="transparent"
723723
selectionColor="rgba(103, 80, 164, 1)"
724724
style={
725725
[
@@ -942,8 +942,8 @@ exports[`correctly applies default textAlign based on default RTL 1`] = `
942942
onBlur={[Function]}
943943
onChangeText={[Function]}
944944
onFocus={[Function]}
945-
placeholder=" "
946-
placeholderTextColor="rgba(73, 69, 79, 1)"
945+
placeholder="Type something"
946+
placeholderTextColor="transparent"
947947
selectionColor="rgba(103, 80, 164, 1)"
948948
style={
949949
[
@@ -1198,8 +1198,8 @@ exports[`correctly applies height to multiline Outline TextInput 1`] = `
11981198
onChangeText={[Function]}
11991199
onFocus={[Function]}
12001200
onLayout={[Function]}
1201-
placeholder=" "
1202-
placeholderTextColor="rgba(73, 69, 79, 1)"
1201+
placeholder="Type Something"
1202+
placeholderTextColor="transparent"
12031203
selectionColor="rgba(103, 80, 164, 1)"
12041204
style={
12051205
[
@@ -1423,8 +1423,8 @@ exports[`correctly applies paddingLeft from contentStyleProp 1`] = `
14231423
onBlur={[Function]}
14241424
onChangeText={[Function]}
14251425
onFocus={[Function]}
1426-
placeholder=" "
1427-
placeholderTextColor="rgba(73, 69, 79, 1)"
1426+
placeholder="Type something"
1427+
placeholderTextColor="transparent"
14281428
selectionColor="rgba(103, 80, 164, 1)"
14291429
style={
14301430
[
@@ -1649,8 +1649,8 @@ exports[`correctly applies textAlign center 1`] = `
16491649
onBlur={[Function]}
16501650
onChangeText={[Function]}
16511651
onFocus={[Function]}
1652-
placeholder=" "
1653-
placeholderTextColor="rgba(73, 69, 79, 1)"
1652+
placeholder="Type something"
1653+
placeholderTextColor="transparent"
16541654
selectionColor="rgba(103, 80, 164, 1)"
16551655
style={
16561656
[
@@ -1873,8 +1873,8 @@ exports[`correctly renders left-side affix adornment, and right-side icon adornm
18731873
onBlur={[Function]}
18741874
onChangeText={[Function]}
18751875
onFocus={[Function]}
1876-
placeholder=" "
1877-
placeholderTextColor="rgba(73, 69, 79, 1)"
1876+
placeholder="Type something"
1877+
placeholderTextColor="transparent"
18781878
selectionColor="rgba(103, 80, 164, 1)"
18791879
style={
18801880
[
@@ -2299,8 +2299,8 @@ exports[`correctly renders left-side icon adornment, and right-side affix adornm
22992299
onBlur={[Function]}
23002300
onChangeText={[Function]}
23012301
onFocus={[Function]}
2302-
placeholder=" "
2303-
placeholderTextColor="rgba(73, 69, 79, 1)"
2302+
placeholder="Type something"
2303+
placeholderTextColor="transparent"
23042304
selectionColor="rgba(103, 80, 164, 1)"
23052305
style={
23062306
[

0 commit comments

Comments
 (0)