Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Unify TextInput autoComplete and textContentType props #34523

Closed
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 125 additions & 0 deletions Libraries/Components/TextInput/TextInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,36 @@ export type enterKeyHintType =
type PasswordRules = string;

type IOSProps = $ReadOnly<{|
/**
* Give the keyboard and the system information about the
* expected semantic meaning for the content that users enter.
* @platform ios
*/
autoComplete?: ?(
| 'address-line1'
| 'address-line2'
| 'cc-number'
| 'current-password'
| 'country'
| 'email'
| 'name'
| 'additional-name'
| 'family-name'
| 'given-name'
| 'nickname'
| 'honorific-prefix'
| 'honorific-suffix'
| 'new-password'
| 'off'
| 'one-time-code'
| 'organization'
| 'organization-title'
| 'postal-code'
| 'street-address'
| 'tel'
| 'url'
| 'username'
),
/**
* When the clear button should appear on the right side of the text view.
* This property is supported only for single-line TextInput component.
Expand Down Expand Up @@ -411,6 +441,23 @@ type AndroidProps = $ReadOnly<{|
| 'username'
| 'username-new'
| 'off'
// additional HTML autocomplete values
| 'address-line1'
| 'address-line2'
| 'bday'
| 'bday-day'
| 'bday-month'
| 'bday-year'
| 'country'
| 'current-password'
| 'honorific-prefix'
| 'honorific-suffix'
| 'additional-name'
| 'family-name'
| 'given-name'
| 'new-password'
| 'one-time-code'
| 'sex'
),

/**
Expand Down Expand Up @@ -1460,6 +1507,67 @@ const inputModeToKeyboardTypeMap = {
url: 'url',
};

// Map HTML autocomplete values to Android autoComplete values
const autoCompleteWebToAutoCompleteAndroidMap = {
'address-line1': 'postal-address-region',
'address-line2': 'postal-address-locality',
bday: 'birthdate-full',
'bday-day': 'birthdate-day',
'bday-month': 'birthdate-month',
'bday-year': 'birthdate-year',
'cc-csc': 'cc-csc',
'cc-exp': 'cc-exp',
'cc-exp-month': 'cc-exp-month',
'cc-exp-year': 'cc-exp-year',
'cc-number': 'cc-number',
country: 'postal-address-country',
'current-password': 'password',
email: 'email',
'honorific-prefix': 'name-prefix',
'honorific-suffix': 'name-suffix',
name: 'name',
'additional-name': 'name-middle',
'family-name': 'name-family',
'given-name': 'name-given',
'new-password': 'password-new',
off: 'off',
'one-time-code': 'sms-otp',
'postal-code': 'postal-code',
sex: 'gender',
'street-address': 'street-address',
tel: 'tel',
'tel-country-code': 'tel-country-code',
'tel-national': 'tel-national',
username: 'username',
};

// Map HTML autocomplete values to iOS textContentType values
const autoCompleteWebToTextContentTypeMap = {
'address-line1': 'streetAddressLine1',
'address-line2': 'streetAddressLine2',
'cc-number': 'creditCardNumber',
'current-password': 'password',
country: 'countryName',
email: 'emailAddress',
name: 'name',
'additional-name': 'middleName',
'family-name': 'familyName',
'given-name': 'givenName',
nickname: 'nickname',
'honorific-prefix': 'namePrefix',
'honorific-suffix': 'nameSuffix',
'new-password': 'newPassword',
off: 'none',
'one-time-code': 'oneTimeCode',
organization: 'organizationName',
'organization-title': 'jobTitle',
'postal-code': 'postalCode',
'street-address': 'fullStreetAddress',
tel: 'telephoneNumber',
url: 'URL',
username: 'username',
};

const ExportedForwardRef: React.AbstractComponent<
React.ElementConfig<typeof InternalTextInput>,
React.ElementRef<HostComponent<mixed>> & ImperativeMethods,
Expand All @@ -1468,6 +1576,8 @@ const ExportedForwardRef: React.AbstractComponent<
allowFontScaling = true,
rejectResponderTermination = true,
underlineColorAndroid = 'transparent',
autoComplete,
textContentType,
readOnly,
editable,
enterKeyHint,
Expand All @@ -1492,6 +1602,21 @@ const ExportedForwardRef: React.AbstractComponent<
keyboardType={
inputMode ? inputModeToKeyboardTypeMap[inputMode] : keyboardType
}
autoComplete={
Platform.OS === 'android'
? // $FlowFixMe
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this suppressing? If we do add a suppression, could we suppress the specific error?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @NickGerleman this is suppressing the prop-missing error because the autoCompleteWebToAutoCompleteAndroidMap object does not include all autoComplete keys/values. The reason for this is just because it would make the object super huge and we can just fallback to autoComplete when autoCompleteWebToTextContentTypeMap[autoComplete] is undefined. I've just updated the suppression to specify the prop-missing error tho

autoCompleteWebToAutoCompleteAndroidMap[autoComplete] ??
autoComplete
: undefined
}
textContentType={
Platform.OS === 'ios' &&
autoComplete &&
autoComplete in autoCompleteWebToTextContentTypeMap
? // $FlowFixMe
autoCompleteWebToTextContentTypeMap[autoComplete]
: textContentType
}
{...restProps}
forwardedRef={forwardedRef}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,35 @@ exports.examples = ([
);
},
},
{
title: 'Text Auto Complete',
render: function (): React.Node {
return (
<View>
<TextInput
autoComplete="country"
placeholder="country"
style={styles.default}
/>
<TextInput
autoComplete="postal-address-country"
placeholder="postal-address-country"
style={styles.default}
/>
<TextInput
autoComplete="one-time-code"
placeholder="one-time-code"
style={styles.default}
/>
<TextInput
autoComplete="sms-otp"
placeholder="sms-otp"
style={styles.default}
/>
</View>
);
},
},
{
title: 'Return key',
render: function (): React.Node {
Expand Down
15 changes: 15 additions & 0 deletions packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,21 @@ exports.examples = ([
);
},
},
{
title: 'Text Auto Complete',
render: function (): React.Node {
return (
<View>
<WithLabel label="country">
<TextInput autoComplete="country" style={styles.default} />
</WithLabel>
<WithLabel label="one-time-code">
<TextInput autoComplete="one-time-code" style={styles.default} />
</WithLabel>
</View>
);
},
},
{
title: 'Text Content Type',
render: function (): React.Node {
Expand Down