diff --git a/packages/react-native/Libraries/Components/TextInput/TextInput.d.ts b/packages/react-native/Libraries/Components/TextInput/TextInput.d.ts index 97fe9371065..91d64ce7965 100644 --- a/packages/react-native/Libraries/Components/TextInput/TextInput.d.ts +++ b/packages/react-native/Libraries/Components/TextInput/TextInput.d.ts @@ -205,13 +205,6 @@ export interface TextInputIOSProps { * Give the keyboard and the system information about the expected * semantic meaning for the content that users enter. * - * For iOS 11+ you can set `textContentType` to `username` or `password` to - * enable autofill of login details from the device keychain. - * - * For iOS 12+ `newPassword` can be used to indicate a new password input the - * user may want to save in the keychain, and `oneTimeCode` can be used to indicate - * that a field can be autofilled by a code arriving in an SMS. - * * To disable autofill, set textContentType to `none`. * * Possible values for `textContentType` are: @@ -223,6 +216,15 @@ export interface TextInputIOSProps { * - `'addressState'` * - `'countryName'` * - `'creditCardNumber'` + * - `'creditCardExpiration'` (iOS 17+) + * - `'creditCardExpirationMonth'` (iOS 17+) + * - `'creditCardExpirationYear'` (iOS 17+) + * - `'creditCardSecurityCode'` (iOS 17+) + * - `'creditCardType'` (iOS 17+) + * - `'creditCardName'` (iOS 17+) + * - `'creditCardGivenName'` (iOS 17+) + * - `'creditCardMiddleName'` (iOS 17+) + * - `'creditCardFamilyName'` (iOS 17+) * - `'emailAddress'` * - `'familyName'` * - `'fullStreetAddress'` @@ -244,6 +246,10 @@ export interface TextInputIOSProps { * - `'password'` * - `'newPassword'` * - `'oneTimeCode'` + * - `'birthdate'` (iOS 17+) + * - `'birthdateDay'` (iOS 17+) + * - `'birthdateMonth'` (iOS 17+) + * - `'birthdateYear'` (iOS 17+) * */ textContentType?: @@ -254,6 +260,15 @@ export interface TextInputIOSProps { | 'addressState' | 'countryName' | 'creditCardNumber' + | 'creditCardExpiration' + | 'creditCardExpirationMonth' + | 'creditCardExpirationYear' + | 'creditCardSecurityCode' + | 'creditCardType' + | 'creditCardName' + | 'creditCardGivenName' + | 'creditCardMiddleName' + | 'creditCardFamilyName' | 'emailAddress' | 'familyName' | 'fullStreetAddress' @@ -275,6 +290,10 @@ export interface TextInputIOSProps { | 'password' | 'newPassword' | 'oneTimeCode' + | 'birthdate' + | 'birthdateDay' + | 'birthdateMonth' + | 'birthdateYear' | undefined; /** @@ -569,6 +588,11 @@ export interface TextInputProps | 'cc-exp-month' | 'cc-exp-year' | 'cc-number' + | 'cc-name' + | 'cc-given-name' + | 'cc-middle-name' + | 'cc-family-name' + | 'cc-type' | 'country' | 'current-password' | 'email' diff --git a/packages/react-native/Libraries/Components/TextInput/TextInput.flow.js b/packages/react-native/Libraries/Components/TextInput/TextInput.flow.js index be702737024..9adbfe9f6f1 100644 --- a/packages/react-native/Libraries/Components/TextInput/TextInput.flow.js +++ b/packages/react-native/Libraries/Components/TextInput/TextInput.flow.js @@ -162,6 +162,15 @@ export type TextContentType = | 'addressState' | 'countryName' | 'creditCardNumber' + | 'creditCardExpiration' + | 'creditCardExpirationMonth' + | 'creditCardExpirationYear' + | 'creditCardSecurityCode' + | 'creditCardType' + | 'creditCardName' + | 'creditCardGivenName' + | 'creditCardMiddleName' + | 'creditCardFamilyName' | 'emailAddress' | 'familyName' | 'fullStreetAddress' @@ -182,7 +191,11 @@ export type TextContentType = | 'username' | 'password' | 'newPassword' - | 'oneTimeCode'; + | 'oneTimeCode' + | 'birthdate' + | 'birthdateDay' + | 'birthdateMonth' + | 'birthdateYear'; export type enterKeyHintType = | 'enter' @@ -418,7 +431,16 @@ export type Props = $ReadOnly<{| * - `additional-name` * - `address-line1` * - `address-line2` + * - `birthdate-day` (iOS 17+) + * - `birthdate-full` (iOS 17+) + * - `birthdate-month` (iOS 17+) + * - `birthdate-year` (iOS 17+) * - `cc-number` + * - `cc-csc` (iOS 17+) + * - `cc-exp` (iOS 17+) + * - `cc-exp-day` (iOS 17+) + * - `cc-exp-month` (iOS 17+) + * - `cc-exp-year` (iOS 17+) * - `country` * - `current-password` * - `email` @@ -437,6 +459,11 @@ export type Props = $ReadOnly<{| * * The following values work on iOS only: * + * - `cc-name` (iOS 17+) + * - `cc-given-name` (iOS 17+) + * - `cc-middle-name` (iOS 17+) + * - `cc-family-name` (iOS 17+) + * - `cc-type` (iOS 17+) * - `nickname` * - `organization` * - `organization-title` @@ -444,15 +471,6 @@ export type Props = $ReadOnly<{| * * The following values work on Android only: * - * - `birthdate-day` - * - `birthdate-full` - * - `birthdate-month` - * - `birthdate-year` - * - `cc-csc` - * - `cc-exp` - * - `cc-exp-day` - * - `cc-exp-month` - * - `cc-exp-year` * - `gender` * - `name-family` * - `name-given` @@ -488,6 +506,11 @@ export type Props = $ReadOnly<{| | 'cc-exp-month' | 'cc-exp-year' | 'cc-number' + | 'cc-name' + | 'cc-given-name' + | 'cc-middle-name' + | 'cc-family-name' + | 'cc-type' | 'country' | 'current-password' | 'email' diff --git a/packages/react-native/Libraries/Components/TextInput/TextInput.js b/packages/react-native/Libraries/Components/TextInput/TextInput.js index 657145ef2ad..b8d86021e17 100644 --- a/packages/react-native/Libraries/Components/TextInput/TextInput.js +++ b/packages/react-native/Libraries/Components/TextInput/TextInput.js @@ -200,6 +200,15 @@ export type TextContentType = | 'addressState' | 'countryName' | 'creditCardNumber' + | 'creditCardExpiration' + | 'creditCardExpirationMonth' + | 'creditCardExpirationYear' + | 'creditCardSecurityCode' + | 'creditCardType' + | 'creditCardName' + | 'creditCardGivenName' + | 'creditCardMiddleName' + | 'creditCardFamilyName' | 'emailAddress' | 'familyName' | 'fullStreetAddress' @@ -220,7 +229,11 @@ export type TextContentType = | 'username' | 'password' | 'newPassword' - | 'oneTimeCode'; + | 'oneTimeCode' + | 'birthdate' + | 'birthdateDay' + | 'birthdateMonth' + | 'birthdateYear'; export type enterKeyHintType = // Cross Platform @@ -462,7 +475,16 @@ export type Props = $ReadOnly<{| * - `additional-name` * - `address-line1` * - `address-line2` + * - `birthdate-day` (iOS 17+) + * - `birthdate-full` (iOS 17+) + * - `birthdate-month` (iOS 17+) + * - `birthdate-year` (iOS 17+) * - `cc-number` + * - `cc-csc` (iOS 17+) + * - `cc-exp` (iOS 17+) + * - `cc-exp-day` (iOS 17+) + * - `cc-exp-month` (iOS 17+) + * - `cc-exp-year` (iOS 17+) * - `country` * - `current-password` * - `email` @@ -481,6 +503,11 @@ export type Props = $ReadOnly<{| * * The following values work on iOS only: * + * - `cc-name` (iOS 17+) + * - `cc-given-name` (iOS 17+) + * - `cc-middle-name` (iOS 17+) + * - `cc-family-name` (iOS 17+) + * - `cc-type` (iOS 17+) * - `nickname` * - `organization` * - `organization-title` @@ -488,15 +515,6 @@ export type Props = $ReadOnly<{| * * The following values work on Android only: * - * - `birthdate-day` - * - `birthdate-full` - * - `birthdate-month` - * - `birthdate-year` - * - `cc-csc` - * - `cc-exp` - * - `cc-exp-day` - * - `cc-exp-month` - * - `cc-exp-year` * - `gender` * - `name-family` * - `name-given` @@ -532,6 +550,11 @@ export type Props = $ReadOnly<{| | 'cc-exp-month' | 'cc-exp-year' | 'cc-number' + | 'cc-name' + | 'cc-given-name' + | 'cc-middle-name' + | 'cc-family-name' + | 'cc-type' | 'country' | 'current-password' | 'email' @@ -1572,7 +1595,20 @@ const autoCompleteWebToAutoCompleteAndroidMap = { const autoCompleteWebToTextContentTypeMap = { 'address-line1': 'streetAddressLine1', 'address-line2': 'streetAddressLine2', + bday: 'birthdate', + 'bday-day': 'birthdateDay', + 'bday-month': 'birthdateMonth', + 'bday-year': 'birthdateYear', + 'cc-csc': 'creditCardSecurityCode', + 'cc-exp-month': 'creditCardExpirationMonth', + 'cc-exp-year': 'creditCardExpirationYear', + 'cc-exp': 'creditCardExpiration', + 'cc-given-name': 'creditCardGivenName', + 'cc-additional-name': 'creditCardMiddleName', + 'cc-family-name': 'creditCardFamilyName', + 'cc-name': 'creditCardName', 'cc-number': 'creditCardNumber', + 'cc-type': 'creditCardType', 'current-password': 'password', country: 'countryName', email: 'emailAddress', diff --git a/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.m b/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.m index cb293db6441..abd071bc8b9 100644 --- a/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.m +++ b/packages/react-native/Libraries/Text/TextInput/RCTBaseTextInputView.m @@ -235,7 +235,7 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame) static NSDictionary *contentTypeMap; dispatch_once(&onceToken, ^{ - contentTypeMap = @{ + NSDictionary *mutableContentTypeMap = @{ @"none" : @"", @"URL" : UITextContentTypeURL, @"addressCity" : UITextContentTypeAddressCity, @@ -265,6 +265,28 @@ RCT_NOT_IMPLEMENTED(-(instancetype)initWithFrame : (CGRect)frame) @"newPassword" : UITextContentTypeNewPassword, @"oneTimeCode" : UITextContentTypeOneTimeCode, }; + +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 170000 /* __IPHONE_17_0 */ + if (@available(iOS 17.0, *)) { + mutableContentTypeMap = [[mutableContentTypeMap mutableCopy] addEntriesFromDictionary:@{ + @"creditCardExpiration" : UITextContentTypeCreditCardExpiration, + @"creditCardExpirationMonth" : UITextContentTypeCreditCardExpirationMonth, + @"creditCardExpirationYear" : UITextContentTypeCreditCardExpirationYear, + @"creditCardSecurityCode" : UITextContentTypeCreditCardSecurityCode, + @"creditCardType" : UITextContentTypeCreditCardType, + @"creditCardName" : UITextContentTypeCreditCardName, + @"creditCardGivenName" : UITextContentTypeCreditCardGivenName, + @"creditCardMiddleName" : UITextContentTypeCreditCardMiddleName, + @"creditCardFamilyName" : UITextContentTypeCreditCardFamilyName, + @"birthdate" : UITextContentTypeBirthdate, + @"birthdateDay" : UITextContentTypeBirthdateDay, + @"birthdateMonth" : UITextContentTypeBirthdateMonth, + @"birthdateYear" : UITextContentTypeBirthdateYear, + }]; + } +#endif + + contentTypeMap = mutableContentTypeMap; }); // Setting textContentType to an empty string will disable any diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.mm index 06862324203..6e3d662f15a 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputUtils.mm @@ -181,7 +181,7 @@ UITextContentType RCTUITextContentTypeFromString(std::string const &contentType) static NSDictionary *contentTypeMap; dispatch_once(&onceToken, ^{ - contentTypeMap = @{ + NSDictionary *mutableContentTypeMap = @{ @"" : @"", @"none" : @"", @"URL" : UITextContentTypeURL, @@ -212,6 +212,28 @@ UITextContentType RCTUITextContentTypeFromString(std::string const &contentType) @"newPassword" : UITextContentTypeNewPassword, @"oneTimeCode" : UITextContentTypeOneTimeCode, }; + +#if defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 170000 /* __IPHONE_17_0 */ + if (@available(iOS 17.0, *)) { + mutableContentTypeMap = [[mutableContentTypeMap mutableCopy] addEntriesFromDictionary:@{ + @"creditCardExpiration" : UITextContentTypeCreditCardExpiration, + @"creditCardExpirationMonth" : UITextContentTypeCreditCardExpirationMonth, + @"creditCardExpirationYear" : UITextContentTypeCreditCardExpirationYear, + @"creditCardSecurityCode" : UITextContentTypeCreditCardSecurityCode, + @"creditCardType" : UITextContentTypeCreditCardType, + @"creditCardName" : UITextContentTypeCreditCardName, + @"creditCardGivenName" : UITextContentTypeCreditCardGivenName, + @"creditCardMiddleName" : UITextContentTypeCreditCardMiddleName, + @"creditCardFamilyName" : UITextContentTypeCreditCardFamilyName, + @"birthdate" : UITextContentTypeBirthdate, + @"birthdateDay" : UITextContentTypeBirthdateDay, + @"birthdateMonth" : UITextContentTypeBirthdateMonth, + @"birthdateYear" : UITextContentTypeBirthdateYear, + }]; + } +#endif + + contentTypeMap = mutableContentTypeMap; }); return contentTypeMap[RCTNSStringFromString(contentType)] ?: @""; diff --git a/packages/react-native/package.json b/packages/react-native/package.json index 73daf6a064c..c07d30b2d95 100644 --- a/packages/react-native/package.json +++ b/packages/react-native/package.json @@ -105,7 +105,7 @@ "anser": "^1.4.9", "ansi-regex": "^5.0.0", "base64-js": "^1.5.1", - "deprecated-react-native-prop-types": "4.1.0", + "deprecated-react-native-prop-types": "4.2.0", "event-target-shim": "^5.0.1", "flow-enums-runtime": "^0.0.6", "invariant": "^2.2.4", diff --git a/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js b/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js index 9daea74eb4b..bb8ce78e864 100644 --- a/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js +++ b/packages/rn-tester/js/examples/TextInput/TextInputExample.ios.js @@ -807,6 +807,12 @@ exports.examples = ([ + + + + + + ); }, @@ -829,6 +835,15 @@ exports.examples = ([ style={styles.default} /> + + + + + + ); }, diff --git a/yarn.lock b/yarn.lock index 433b884164c..f196e8098f5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4133,10 +4133,10 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= -deprecated-react-native-prop-types@4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-4.1.0.tgz#8ed03a64c21b7fbdd2d000957b6838d4f38d2c66" - integrity sha512-WfepZHmRbbdTvhcolb8aOKEvQdcmTMn5tKLbqbXmkBvjFjRVWAYqsXk/DBsV8TZxws8SdGHLuHaJrHSQUPRdfw== +deprecated-react-native-prop-types@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-4.2.0.tgz#2d7f2a2bb3cf39fb873286a85eb04bc3e1f2e4d5" + integrity sha512-DjFQDPM2osEm9CEjxMSjAIGFf6D9kXiPZ8+EhHf4oU0PK2KNktp0Rv1kobdbOS0B9kQQ9QpqmmZQuZN7lI3P5g== dependencies: "@react-native/normalize-colors" "*" invariant "*"