Skip to content

Commit

Permalink
feat: Add inputMode prop to TextInput component (#34460)
Browse files Browse the repository at this point in the history
Summary:
This adds the `inputMode` prop to the TextInput component as requested on #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 (WebKit/WebKit@3b5f0c8). 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: #34460

Reviewed By: necolas

Differential Revision: D38900724

Pulled By: cipolleschi

fbshipit-source-id: 60d405ccdbfad588b272fbb6b220b64ffdfc4b14
  • Loading branch information
gabrieldonadel authored and facebook-github-bot committed Aug 30, 2022
1 parent 5451cd4 commit 9fac885
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 0 deletions.
43 changes: 43 additions & 0 deletions Libraries/Components/TextInput/TextInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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`.
*
Expand Down Expand Up @@ -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<typeof InternalTextInput>,
React.ElementRef<HostComponent<mixed>> & ImperativeMethods,
Expand All @@ -1434,6 +1472,8 @@ const ExportedForwardRef: React.AbstractComponent<
editable,
enterKeyHint,
returnKeyType,
inputMode,
keyboardType,
...restProps
},
forwardedRef: ReactRefSetter<
Expand All @@ -1449,6 +1489,9 @@ const ExportedForwardRef: React.AbstractComponent<
returnKeyType={
enterKeyHint ? enterKeyHintToReturnTypeMap[enterKeyHint] : returnKeyType
}
keyboardType={
inputMode ? inputModeToKeyboardTypeMap[inputMode] : keyboardType
}
{...restProps}
forwardedRef={forwardedRef}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,30 @@ module.exports = ([
return <View>{examples}</View>;
},
},
{
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 (
<WithLabel key={mode} label={mode}>
<TextInput inputMode={mode} style={styles.default} />
</WithLabel>
);
});
return <View>{examples}</View>;
},
},
{
title: 'Blur on submit',
render: function (): React.Element<any> {
Expand Down

0 comments on commit 9fac885

Please sign in to comment.