From 9fac88574e2f8c2f46b7f081273845f833fe1b75 Mon Sep 17 00:00:00 2001 From: Gabriel Donadel Dall'Agnol Date: Tue, 30 Aug 2022 08:19:51 -0700 Subject: [PATCH] feat: Add inputMode prop to TextInput component (#34460) Summary: This adds the `inputMode` prop to the TextInput component as requested on https://github.com/facebook/react-native/issues/34424, mapping web [inputMode types](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inputmode) to equivalent [keyboardType](https://reactnative.dev/docs/textinput#keyboardtype) values. This PR also updates RNTester TextInputExample in order to facilitate the manual QA. ### Caveats ~~This only adds support to `text`, `decimal`, `numeric`, `tel`, `search`, `email`, and `url` types.~~ #### `inputMode="none"` **Currently mapped to `default` keyboard type.** The main problem with this input mode is that it's not supported natively neither on Android or iOS. Android `TextView` does accept `none` as `android:inputType` but that makes the text not editable, which wouldn't really solve our problem. `UITextInput` on iOS on the other hand doesn't even have something similar to avoid displaying the virtual keyboard. If we really want to add the support for `inputMode="none"` one interesting approach we could take is to do something similar to what WebKit has done (https://github.com/WebKit/WebKit/commit/3b5f0c8ecf9de23f79524ed02e290837ab8334cd). In order to achieve this behavior, they had to return a `UIView` with a bounds of `CGRectZero` as the inputView of the `WKContentView` when inputmode=none is present. ~~I guess the real question here should be, do we really want to add this? Or perhaps should we just map `inputMode="none"` to `keyboardType="default"`~~ Android docs: https://developer.android.com/reference/android/widget/TextView#attr_android:inputType iOS docs: https://developer.apple.com/documentation/uikit/uikeyboardtype?language=objc #### `inputMode="search"` on Android **Currently mapped to `default` keyboard type.** Android `TextView` does not offers any options like `UIKeyboardTypeWebSearch` on iOS to be used as `search` with `android:inputType` and that's probably the reason why `keyboardType="web-search"` is iOS only. I checked how this is handled on the browser on my Android device and it seems that Chrome just uses the default keyboard, maybe we should do the same? ### Open questions - ~~What should be done about `inputMode="none"`?~~ Add it and map it to `default` keyboard type. - ~~Which keyboard should we show on Android when `inputMode="search"`?~~ Use the `default` keyboard the same way Chrome does ## Changelog [General] [Added] - Add inputMode prop to TextInput component ## Test Plan 1. Open the RNTester app and navigate to the TextInput page 2. Test the `TextInput` component through the `Input modes` section https://user-images.githubusercontent.com/11707729/185691224-3042e828-a008-4bd0-bb3d-010a6a18dfd5.mov Pull Request resolved: https://github.com/facebook/react-native/pull/34460 Reviewed By: necolas Differential Revision: D38900724 Pulled By: cipolleschi fbshipit-source-id: 60d405ccdbfad588b272fbb6b220b64ffdfc4b14 --- Libraries/Components/TextInput/TextInput.js | 43 +++++++++++++++++++ .../TextInput/TextInputSharedExamples.js | 24 +++++++++++ 2 files changed, 67 insertions(+) diff --git a/Libraries/Components/TextInput/TextInput.js b/Libraries/Components/TextInput/TextInput.js index 5e1384a77c57ee..59cb7f8e8bdc44 100644 --- a/Libraries/Components/TextInput/TextInput.js +++ b/Libraries/Components/TextInput/TextInput.js @@ -155,6 +155,16 @@ export type KeyboardType = // Android-only | 'visible-password'; +export type InputMode = + | 'none' + | 'text' + | 'decimal' + | 'numeric' + | 'tel' + | 'search' + | 'email' + | 'url'; + export type ReturnKeyType = // Cross Platform | 'done' @@ -567,6 +577,23 @@ export type Props = $ReadOnly<{| */ enterKeyHint?: ?enterKeyHintType, + /** + * `inputMode` works like the `inputmode` attribute in HTML, it determines which + * keyboard to open, e.g.`numeric` and has precedence over keyboardType + * + * Support the following values: + * + * - `none` + * - `text` + * - `decimal` + * - `numeric` + * - `tel` + * - `search` + * - `email` + * - `url` + */ + inputMode?: ?InputMode, + /** * Determines which keyboard to open, e.g.`numeric`. * @@ -1422,6 +1449,17 @@ const enterKeyHintToReturnTypeMap = { send: 'send', }; +const inputModeToKeyboardTypeMap = { + none: 'default', + text: 'default', + decimal: 'decimal-pad', + numeric: 'number-pad', + tel: 'phone-pad', + search: Platform.OS === 'ios' ? 'web-search' : 'default', + email: 'email-address', + url: 'url', +}; + const ExportedForwardRef: React.AbstractComponent< React.ElementConfig, React.ElementRef> & ImperativeMethods, @@ -1434,6 +1472,8 @@ const ExportedForwardRef: React.AbstractComponent< editable, enterKeyHint, returnKeyType, + inputMode, + keyboardType, ...restProps }, forwardedRef: ReactRefSetter< @@ -1449,6 +1489,9 @@ const ExportedForwardRef: React.AbstractComponent< returnKeyType={ enterKeyHint ? enterKeyHintToReturnTypeMap[enterKeyHint] : returnKeyType } + keyboardType={ + inputMode ? inputModeToKeyboardTypeMap[inputMode] : keyboardType + } {...restProps} forwardedRef={forwardedRef} /> diff --git a/packages/rn-tester/js/examples/TextInput/TextInputSharedExamples.js b/packages/rn-tester/js/examples/TextInput/TextInputSharedExamples.js index e88978486cb292..4a526310ff537d 100644 --- a/packages/rn-tester/js/examples/TextInput/TextInputSharedExamples.js +++ b/packages/rn-tester/js/examples/TextInput/TextInputSharedExamples.js @@ -721,6 +721,30 @@ module.exports = ([ return {examples}; }, }, + { + title: 'Input modes', + name: 'inputModes', + render: function (): React.Node { + const inputMode = [ + 'none', + 'text', + 'decimal', + 'numeric', + 'tel', + 'search', + 'email', + 'url', + ]; + const examples = inputMode.map(mode => { + return ( + + + + ); + }); + return {examples}; + }, + }, { title: 'Blur on submit', render: function (): React.Element {