Skip to content

Commit 252b1a6

Browse files
authored
fix: input's adornment and helper disabled colors (#3726)
1 parent 43a9b17 commit 252b1a6

File tree

15 files changed

+146
-56
lines changed

15 files changed

+146
-56
lines changed

docs/docusaurus.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ const config = {
121121
AnimatedFAB: 'FAB/AnimatedFAB',
122122
FABGroup: 'FAB/FABGroup',
123123
},
124-
HelperText: 'HelperText',
124+
HelperText: { HelperText: 'HelperText/HelperText' },
125125
IconButton: {
126126
IconButton: 'IconButton/IconButton',
127127
},

example/src/Examples/TextInputExample.tsx

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,22 @@ const TextInputExample = () => {
303303
label="Disabled flat input with value"
304304
value="Disabled flat input value"
305305
/>
306+
<TextInput
307+
style={styles.inputContainerStyle}
308+
label="Flat input"
309+
disabled
310+
value="Disabled flat input with adornments"
311+
left={
312+
<TextInput.Icon
313+
icon="magnify"
314+
color={flatLeftIcon}
315+
onPress={() => {
316+
changeIconColor('flatLeftIcon');
317+
}}
318+
/>
319+
}
320+
right={<TextInput.Affix text="/100" />}
321+
/>
306322
<TextInput
307323
mode="outlined"
308324
disabled
@@ -316,6 +332,23 @@ const TextInputExample = () => {
316332
label="Disabled outlined input"
317333
value="Disabled outlined input with value"
318334
/>
335+
<TextInput
336+
style={styles.inputContainerStyle}
337+
label="Flat input"
338+
disabled
339+
mode="outlined"
340+
value="Disabled flat input with adornments"
341+
left={
342+
<TextInput.Icon
343+
icon="magnify"
344+
color={flatLeftIcon}
345+
onPress={() => {
346+
changeIconColor('flatLeftIcon');
347+
}}
348+
/>
349+
}
350+
right={<TextInput.Affix text="/100" />}
351+
/>
319352
</List.Section>
320353
<List.Section title="Dense inputs">
321354
<TextInput

src/components/HelperText.tsx renamed to src/components/HelperText/HelperText.tsx

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@ import {
77
TextStyle,
88
} from 'react-native';
99

10-
import color from 'color';
11-
12-
import { useInternalTheme } from '../core/theming';
13-
import type { $Omit, ThemeProp } from '../types';
14-
import AnimatedText from './Typography/AnimatedText';
10+
import { useInternalTheme } from '../../core/theming';
11+
import type { $Omit, ThemeProp } from '../../types';
12+
import AnimatedText from '../Typography/AnimatedText';
13+
import { getTextColor } from './utils';
1514

1615
export type Props = $Omit<
1716
$Omit<React.ComponentPropsWithRef<typeof AnimatedText>, 'padding'>,
@@ -21,6 +20,10 @@ export type Props = $Omit<
2120
* Type of the helper text.
2221
*/
2322
type: 'error' | 'info';
23+
/**
24+
* Text content of the HelperText.
25+
*/
26+
children: React.ReactNode;
2427
/**
2528
* Whether to display the helper text.
2629
*/
@@ -30,9 +33,9 @@ export type Props = $Omit<
3033
*/
3134
padding?: 'none' | 'normal';
3235
/**
33-
* Text content of the HelperText.
36+
* Whether the text input tied with helper text is disabled.
3437
*/
35-
children: React.ReactNode;
38+
disabled?: boolean;
3639
style?: StyleProp<TextStyle>;
3740
/**
3841
* @optional
@@ -86,6 +89,7 @@ const HelperText = ({
8689
theme: themeOverrides,
8790
onLayout,
8891
padding = 'normal',
92+
disabled,
8993
...rest
9094
}: Props) => {
9195
const theme = useInternalTheme(themeOverrides);
@@ -122,15 +126,7 @@ const HelperText = ({
122126
textHeight = e.nativeEvent.layout.height;
123127
};
124128

125-
const { colors, dark } = theme;
126-
127-
const infoTextColor = theme.isV3
128-
? theme.colors.onSurfaceVariant
129-
: color(theme?.colors?.text)
130-
.alpha(dark ? 0.7 : 0.54)
131-
.rgb()
132-
.string();
133-
const textColor = type === 'error' ? colors?.error : infoTextColor;
129+
const textColor = getTextColor({ theme, disabled, type });
134130

135131
return (
136132
<AnimatedText

src/components/HelperText/utils.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import color from 'color';
2+
3+
import type { InternalTheme } from '../../types';
4+
5+
type BaseProps = {
6+
theme: InternalTheme;
7+
disabled?: boolean;
8+
type?: 'error' | 'info';
9+
};
10+
11+
export function getTextColor({ theme, disabled, type }: BaseProps) {
12+
const { colors, dark } = theme;
13+
14+
if (type === 'error') {
15+
return colors?.error;
16+
}
17+
18+
if (theme.isV3) {
19+
if (disabled) {
20+
return theme.colors.onSurfaceDisabled;
21+
} else {
22+
return theme.colors.onSurfaceVariant;
23+
}
24+
}
25+
26+
return color(theme?.colors?.text)
27+
.alpha(dark ? 0.7 : 0.54)
28+
.rgb()
29+
.string();
30+
}

src/components/TextInput/Adornment/TextInputAdornment.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ export interface TextInputAdornmentProps {
133133
paddingHorizontal?: number | string;
134134
maxFontSizeMultiplier?: number | undefined | null;
135135
theme?: ThemeProp;
136+
disabled?: boolean;
136137
}
137138

138139
const TextInputAdornment: React.FunctionComponent<TextInputAdornmentProps> = ({
@@ -148,6 +149,7 @@ const TextInputAdornment: React.FunctionComponent<TextInputAdornmentProps> = ({
148149
paddingHorizontal,
149150
maxFontSizeMultiplier,
150151
theme,
152+
disabled,
151153
}) => {
152154
if (adornmentConfig.length) {
153155
return (
@@ -165,6 +167,7 @@ const TextInputAdornment: React.FunctionComponent<TextInputAdornmentProps> = ({
165167
testID: `${side}-${type}-adornment`,
166168
isTextInputFocused,
167169
paddingHorizontal,
170+
disabled,
168171
};
169172
if (type === AdornmentType.Icon) {
170173
return (

src/components/TextInput/Adornment/TextInputAffix.tsx

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,11 @@ import {
99
ViewStyle,
1010
} from 'react-native';
1111

12-
import color from 'color';
13-
1412
import { useInternalTheme } from '../../../core/theming';
1513
import type { ThemeProp } from '../../../types';
1614
import { getConstants } from '../helpers';
1715
import { AdornmentSide } from './enums';
16+
import { getTextColor } from './utils';
1817

1918
export type Props = {
2019
/**
@@ -41,6 +40,7 @@ type ContextState = {
4140
paddingHorizontal?: number | string;
4241
maxFontSizeMultiplier?: number | undefined | null;
4342
testID?: string;
43+
disabled?: boolean;
4444
};
4545

4646
const AffixContext = React.createContext<ContextState>({
@@ -64,6 +64,7 @@ const AffixAdornment: React.FunctionComponent<
6464
paddingHorizontal,
6565
maxFontSizeMultiplier,
6666
testID,
67+
disabled,
6768
}) => {
6869
return (
6970
<AffixContext.Provider
@@ -76,6 +77,7 @@ const AffixAdornment: React.FunctionComponent<
7677
paddingHorizontal,
7778
maxFontSizeMultiplier,
7879
testID,
80+
disabled,
7981
}}
8082
>
8183
{affix}
@@ -132,15 +134,9 @@ const TextInputAffix = ({
132134
paddingHorizontal,
133135
maxFontSizeMultiplier,
134136
testID,
137+
disabled,
135138
} = React.useContext(AffixContext);
136139

137-
const textColor = color(
138-
theme.isV3 ? theme.colors.onSurface : theme.colors?.text
139-
)
140-
.alpha(theme.dark ? 0.7 : 0.54)
141-
.rgb()
142-
.string();
143-
144140
const offset =
145141
typeof paddingHorizontal === 'number' ? paddingHorizontal : AFFIX_OFFSET;
146142

@@ -149,6 +145,8 @@ const TextInputAffix = ({
149145
[side]: offset,
150146
} as ViewStyle;
151147

148+
const textColor = getTextColor({ theme, disabled });
149+
152150
return (
153151
<Animated.View
154152
style={[

src/components/TextInput/Adornment/TextInputIcon.tsx

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import type { IconSource } from '../../Icon';
1313
import IconButton from '../../IconButton/IconButton';
1414
import { ICON_SIZE } from '../constants';
1515
import { getConstants } from '../helpers';
16+
import { getIconColor } from './utils';
1617

1718
export type Props = $Omit<
1819
React.ComponentProps<typeof IconButton>,
@@ -47,6 +48,7 @@ type StyleContextType = {
4748
isTextInputFocused: boolean;
4849
forceFocus: () => void;
4950
testID: string;
51+
disabled?: boolean;
5052
};
5153

5254
const StyleContext = React.createContext<StyleContextType>({
@@ -63,6 +65,7 @@ const IconAdornment: React.FunctionComponent<
6365
topPosition: number;
6466
side: 'left' | 'right';
6567
theme?: ThemeProp;
68+
disabled?: boolean;
6669
} & Omit<StyleContextType, 'style'>
6770
> = ({
6871
icon,
@@ -72,6 +75,7 @@ const IconAdornment: React.FunctionComponent<
7275
forceFocus,
7376
testID,
7477
theme: themeOverrides,
78+
disabled,
7579
}) => {
7680
const { isV3 } = useInternalTheme(themeOverrides);
7781
const { ICON_OFFSET } = getConstants(isV3);
@@ -80,7 +84,13 @@ const IconAdornment: React.FunctionComponent<
8084
top: topPosition,
8185
[side]: ICON_OFFSET,
8286
};
83-
const contextState = { style, isTextInputFocused, forceFocus, testID };
87+
const contextState = {
88+
style,
89+
isTextInputFocused,
90+
forceFocus,
91+
testID,
92+
disabled,
93+
};
8494

8595
return (
8696
<StyleContext.Provider value={contextState}>{icon}</StyleContext.Provider>
@@ -125,7 +135,7 @@ const TextInputIcon = ({
125135
theme: themeOverrides,
126136
...rest
127137
}: Props) => {
128-
const { style, isTextInputFocused, forceFocus, testID } =
138+
const { style, isTextInputFocused, forceFocus, testID, disabled } =
129139
React.useContext(StyleContext);
130140

131141
const onPressWithFocusControl = React.useCallback(
@@ -141,16 +151,7 @@ const TextInputIcon = ({
141151

142152
const theme = useInternalTheme(themeOverrides);
143153

144-
let iconColor = color;
145-
146-
if (theme.isV3) {
147-
if (rest.disabled) {
148-
iconColor = theme.colors.onSurface;
149-
}
150-
iconColor = theme.colors.onSurfaceVariant;
151-
} else {
152-
iconColor = theme.colors.text;
153-
}
154+
const iconColor = getIconColor({ theme, disabled });
154155

155156
return (
156157
<View style={[styles.container, style]}>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import color from 'color';
2+
3+
import type { InternalTheme } from '../../../types';
4+
5+
type BaseProps = {
6+
theme: InternalTheme;
7+
disabled?: boolean;
8+
};
9+
10+
export function getTextColor({ theme, disabled }: BaseProps) {
11+
if (theme.isV3) {
12+
if (disabled) {
13+
return theme.colors.onSurfaceDisabled;
14+
}
15+
return theme.colors.onSurfaceVariant;
16+
}
17+
return color(theme.colors?.text)
18+
.alpha(theme.dark ? 0.7 : 0.54)
19+
.rgb()
20+
.string();
21+
}
22+
23+
export function getIconColor({ theme, disabled }: BaseProps) {
24+
if (!theme.isV3) {
25+
return theme.colors.text;
26+
}
27+
28+
if (disabled) {
29+
return theme.colors.onSurfaceDisabled;
30+
}
31+
32+
return theme.colors.onSurfaceVariant;
33+
}

src/components/TextInput/TextInputFlat.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ const TextInputFlat = ({
309309
onAffixChange,
310310
isTextInputFocused: parentState.focused,
311311
maxFontSizeMultiplier: rest.maxFontSizeMultiplier,
312+
disabled,
312313
};
313314
if (adornmentConfig.length) {
314315
adornmentProps = {
@@ -340,7 +341,7 @@ const TextInputFlat = ({
340341
},
341342
]}
342343
>
343-
{!isAndroid && multiline && !!label && (
344+
{!isAndroid && multiline && !!label && !disabled && (
344345
// Workaround for: https://github.com/callstack/react-native-paper/issues/2799
345346
// Patch for a multiline TextInput with fixed height, which allow to avoid covering input label with its value.
346347
<View

src/components/TextInput/TextInputOutlined.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ const TextInputOutlined = ({
279279
onAffixChange,
280280
isTextInputFocused: parentState.focused,
281281
maxFontSizeMultiplier: rest.maxFontSizeMultiplier,
282+
disabled,
282283
};
283284
if (adornmentConfig.length) {
284285
adornmentProps = {

0 commit comments

Comments
 (0)