Files
react-native/packages/rn-tester/js/examples/TextInput/TextInputSharedExamples.js
T
kunal.chavhan 18d6028ff9 fix: select text on auto focus for TextInput (#45004)
Summary:
Fixes: https://github.com/facebook/react-native/issues/43413

This pull request addresses an issue on Android where the text selection was not working when both `selectTextOnFocus` and `autoFocus` were set to true on TextInput.
`ReactTextInputManager` was calling `setSelectAllOnFocus` on `ReactEditText` before its onLayout is called causing text selection to not work on auto focus.

Changes Made

- Added logic to wait for the ReactEditText view's layout to be drawn before attempting to select the text.
On the first layout pass, the code now explicitly calls selectAll() to select the text.
- Implemented a check to ensure selectAll() is only called during the first layout pass, avoiding unnecessary calls on subsequent layout passes.

Impact
This change ensures that text selection is properly triggered when selectTextOnFocus and autoFocus are both enabled, improving the user experience and making text input behavior consistent and reliable.

## Changelog:

[ANDROID] [FIXED]: fixed select text on auto focus for TextInput

<!-- Help reviewers and the release process by writing your own changelog entry.

Pick one each for the category and type tags:

[ANDROID|GENERAL|IOS|INTERNAL] [BREAKING|ADDED|CHANGED|DEPRECATED|REMOVED|FIXED|SECURITY] - Message

For more details, see:
https://reactnative.dev/contributing/changelogs-in-pull-requests

Pull Request resolved: https://github.com/facebook/react-native/pull/45004

Test Plan:
| Before | After |
|--------|--------|
| ![before](https://github.com/facebook/react-native/assets/61144478/02da975a-2c80-41b9-86c1-196d0324f437) | ![after](https://github.com/facebook/react-native/assets/61144478/fbe78758-04ab-460f-8ecb-b267ed07751a) |

Reviewed By: javache

Differential Revision: D59448600

Pulled By: cortinico

fbshipit-source-id: 8a594d3193f227ba2d64b808d905bab8b3d24e9b
2024-07-12 10:49:54 -07:00

1153 lines
30 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @flow
*/
'use strict';
import type {RNTesterModuleExample} from '../../types/RNTesterTypes';
import type {TextStyle} from 'react-native/Libraries/StyleSheet/StyleSheet';
import RNTesterButton from '../../components/RNTesterButton';
import {RNTesterThemeContext} from '../../components/RNTesterTheme';
import ExampleTextInput from './ExampleTextInput';
import * as React from 'react';
import {useContext, useState} from 'react';
import {
Button,
Platform,
StyleSheet,
Text,
TextInput,
View,
} from 'react-native';
const styles = StyleSheet.create({
multiline: {
height: 50,
marginBottom: 4,
},
singleLine: {
fontSize: 16,
},
labelContainer: {
flexDirection: 'row',
marginVertical: 2,
},
label: {
width: 115,
textAlign: 'right',
marginRight: 10,
paddingTop: 2,
fontSize: 12,
},
inputContainer: {
flex: 1,
},
rewriteContainer: {
flexDirection: 'row',
alignItems: 'center',
},
remainder: {
textAlign: 'right',
width: 24,
},
hashtag: {
color: 'blue',
fontWeight: 'bold',
},
eventLabel: {
margin: 3,
fontSize: 12,
},
focusedUncontrolled: {
margin: -2,
},
screenshotArea: {
position: 'absolute',
top: -5,
left: 120,
right: -5,
bottom: -5,
},
});
class AutoFocusWithSelectOnFocusTextExample extends React.Component<
$FlowFixMeProps,
any,
> {
constructor(props: any | void) {
super(props);
this.state = {
autoFocusFalse: 'autoFocus: false - selectTextOnFocus: true',
autoFocusTrue: 'autoFocus: true - selectTextOnFocus: true',
};
}
render(): React.Node {
return (
<View>
<ExampleTextInput
autoFocus={false}
selectTextOnFocus={true}
value={this.state.autoFocusFalse}
onChangeText={text => this.setState({autoFocusFalse: text})}
accessibilityLabel="I am the accessibility label for text input"
/>
<ExampleTextInput
autoFocus={true}
selectTextOnFocus={true}
value={this.state.autoFocusTrue}
onChangeText={text => this.setState({autoFocusTrue: text})}
accessibilityLabel="I am the accessibility label for text input"
/>
</View>
);
}
}
class WithLabel extends React.Component<$FlowFixMeProps> {
render(): React.Node {
return (
<View style={styles.labelContainer}>
<Text style={styles.label}>{this.props.label}</Text>
<View style={styles.inputContainer}>{this.props.children}</View>
</View>
);
}
}
class RewriteExample extends React.Component<$FlowFixMeProps, any> {
constructor(props: any | void) {
super(props);
this.state = {text: ''};
}
render(): React.Node {
const limit = 20;
const remainder = limit - this.state.text.length;
const remainderColor = remainder > 5 ? 'blue' : 'red';
return (
<View style={styles.rewriteContainer}>
<ExampleTextInput
testID="rewrite_sp_underscore_input"
autoCorrect={false}
multiline={false}
maxLength={limit}
onChangeText={text => {
text = text.replace(/ /g, '_');
this.setState({text});
}}
value={this.state.text}
/>
<Text style={[styles.remainder, {color: remainderColor}]}>
{remainder}
</Text>
</View>
);
}
}
class RewriteExampleInvalidCharacters extends React.Component<
$FlowFixMeProps,
any,
> {
constructor(props: any | void) {
super(props);
this.state = {text: ''};
}
render(): React.Node {
return (
<View style={styles.rewriteContainer}>
<ExampleTextInput
testID="rewrite_no_sp_input"
autoCorrect={false}
multiline={false}
onChangeText={text => {
this.setState({text: text.replace(/\s/g, '')});
}}
value={this.state.text}
/>
</View>
);
}
}
class RewriteInvalidCharactersAndClearExample extends React.Component<
$FlowFixMeProps,
any,
> {
inputRef: ?React.ElementRef<typeof TextInput> = null;
constructor(props: any | void) {
super(props);
this.state = {text: ''};
}
render(): React.Node {
return (
<View style={styles.rewriteContainer}>
<ExampleTextInput
testID="rewrite_clear_input"
autoCorrect={false}
ref={ref => {
this.inputRef = ref;
}}
multiline={true}
onChangeText={text => {
this.setState({text: text.replace(/ /g, '')});
}}
value={this.state.text}
/>
<Button
testID="rewrite_clear_button"
onPress={() => {
if (this.inputRef != null) {
this.inputRef.clear();
}
}}
title="Clear"
/>
</View>
);
}
}
type ExampleRef = {current: null | {focus(): void, ...}};
class BlurOnSubmitExample extends React.Component<{...}> {
ref1: ExampleRef = React.createRef();
ref2: ExampleRef = React.createRef();
ref3: ExampleRef = React.createRef();
ref4: ExampleRef = React.createRef();
ref5: ExampleRef = React.createRef();
render(): React.Node {
return (
<View>
<ExampleTextInput
ref={this.ref1}
style={styles.singleLine}
placeholder="blurOnSubmit = false"
returnKeyType="next"
blurOnSubmit={false}
onSubmitEditing={() => this.ref2.current?.focus()}
/>
<ExampleTextInput
ref={this.ref2}
style={styles.singleLine}
keyboardType="email-address"
placeholder="blurOnSubmit = false"
returnKeyType="next"
blurOnSubmit={false}
onSubmitEditing={() => this.ref3.current?.focus()}
/>
<ExampleTextInput
ref={this.ref3}
style={styles.singleLine}
keyboardType="url"
placeholder="blurOnSubmit = false"
returnKeyType="next"
blurOnSubmit={false}
onSubmitEditing={() => this.ref4.current?.focus()}
/>
<ExampleTextInput
ref={this.ref4}
style={styles.singleLine}
keyboardType="numeric"
placeholder="blurOnSubmit = false"
blurOnSubmit={false}
onSubmitEditing={() => this.ref5.current?.focus()}
/>
<ExampleTextInput
ref={this.ref5}
style={styles.singleLine}
keyboardType="numbers-and-punctuation"
placeholder="blurOnSubmit = true"
returnKeyType="done"
/>
</View>
);
}
}
class SubmitBehaviorExample extends React.Component<{...}> {
ref1: ExampleRef = React.createRef();
ref2: ExampleRef = React.createRef();
ref3: ExampleRef = React.createRef();
ref4: ExampleRef = React.createRef();
ref5: ExampleRef = React.createRef();
ref6: ExampleRef = React.createRef();
ref7: ExampleRef = React.createRef();
ref8: ExampleRef = React.createRef();
ref9: ExampleRef = React.createRef();
ref10: ExampleRef = React.createRef();
ref11: ExampleRef = React.createRef();
render(): React.Node {
return (
<View>
<ExampleTextInput
ref={this.ref1}
placeholder="single line submit"
submitBehavior="submit"
onSubmitEditing={() => this.ref2.current?.focus()}
/>
<ExampleTextInput
ref={this.ref2}
placeholder="single line blurAndSubmit"
submitBehavior="blurAndSubmit"
onSubmitEditing={() => this.ref3.current?.focus()}
/>
<ExampleTextInput
ref={this.ref3}
placeholder="single line default"
onSubmitEditing={() => this.ref4.current?.focus()}
/>
<ExampleTextInput
ref={this.ref4}
blurOnSubmit
placeholder="single line blurOnSubmit true"
onSubmitEditing={() => this.ref5.current?.focus()}
/>
<ExampleTextInput
ref={this.ref5}
blurOnSubmit={false}
placeholder="single line blurOnSubmit false"
onSubmitEditing={() => this.ref6.current?.focus()}
/>
<ExampleTextInput
ref={this.ref6}
multiline
placeholder="multiline submit"
submitBehavior="submit"
onSubmitEditing={() => this.ref7.current?.focus()}
/>
<ExampleTextInput
ref={this.ref7}
multiline
placeholder="multiline blurAndSubmit"
submitBehavior="blurAndSubmit"
onSubmitEditing={() => this.ref8.current?.focus()}
/>
<ExampleTextInput
ref={this.ref8}
multiline
blurOnSubmit
placeholder="multiline blurOnSubmit true"
onSubmitEditing={() => this.ref9.current?.focus()}
/>
<ExampleTextInput
ref={this.ref9}
multiline
blurOnSubmit={false}
placeholder="multiline blurOnSubmit false"
/>
<ExampleTextInput
ref={this.ref10}
multiline
placeholder="multiline newline"
submitBehavior="newline"
/>
<ExampleTextInput
ref={this.ref11}
multiline
placeholder="multiline default"
/>
</View>
);
}
}
class TextEventsExample extends React.Component<{...}, $FlowFixMeState> {
state:
| any
| {
curText: string,
prev2Text: string,
prev3Text: string,
prevText: string,
} = {
curText: '<No Event>',
prevText: '<No Event>',
prev2Text: '<No Event>',
prev3Text: '<No Event>',
};
updateText = (text: string) => {
this.setState(state => {
return {
curText: text,
prevText: state.curText,
prev2Text: state.prevText,
prev3Text: state.prev2Text,
};
});
};
render(): React.Node {
return (
<View>
<ExampleTextInput
autoCapitalize="none"
placeholder="Enter text to see events"
autoCorrect={false}
multiline
onFocus={() => this.updateText('onFocus')}
onBlur={() => this.updateText('onBlur')}
onChange={event =>
this.updateText('onChange text: ' + event.nativeEvent.text)
}
onContentSizeChange={event =>
this.updateText(
'onContentSizeChange size: ' +
JSON.stringify(event.nativeEvent.contentSize),
)
}
onEndEditing={event =>
this.updateText('onEndEditing text: ' + event.nativeEvent.text)
}
onSubmitEditing={event =>
this.updateText('onSubmitEditing text: ' + event.nativeEvent.text)
}
onKeyPress={event =>
this.updateText('onKeyPress key: ' + event.nativeEvent.key)
}
style={styles.singleLine}
/>
<Text style={styles.eventLabel}>
{this.state.curText}
{'\n'}
(prev: {this.state.prevText}){'\n'}
(prev2: {this.state.prev2Text}){'\n'}
(prev3: {this.state.prev3Text})
</Text>
</View>
);
}
}
class TokenizedTextExample extends React.Component<
$FlowFixMeProps,
$FlowFixMeState,
> {
constructor(props: any | void) {
super(props);
this.state = {text: 'Hello #World'};
}
render(): React.Node {
//define delimiter
let delimiter = /\s+/;
//split string
let _text = this.state.text;
let token,
index,
parts = [];
while (_text) {
delimiter.lastIndex = 0;
token = delimiter.exec(_text);
if (token === null) {
break;
}
index = token.index;
if (token[0].length === 0) {
index = 1;
}
parts.push(_text.slice(0, index));
parts.push(token[0]);
index = index + token[0].length;
_text = _text.slice(index);
}
parts.push(_text);
//highlight hashtags
parts = parts.map(text => {
if (/^#/.test(text)) {
return (
<Text testID="hashtag" key={text} style={styles.hashtag}>
{text}
</Text>
);
} else {
return text;
}
});
return (
<View style={{flexDirection: 'row'}}>
<ExampleTextInput
testID="text-input"
multiline={true}
style={styles.multiline}
onChangeText={text => {
this.setState({text});
}}>
<Text>{parts}</Text>
</ExampleTextInput>
</View>
);
}
}
type SelectionExampleState = {
selection: $ReadOnly<{|
start: number,
end: number,
|}>,
value: string,
...
};
class SelectionExample extends React.Component<
$FlowFixMeProps,
SelectionExampleState,
> {
_textInput: React.ElementRef<typeof TextInput> | null = null;
/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's
* LTI update could not be added via codemod */
constructor(props) {
super(props);
this.state = {
selection: {start: 0, end: 0},
value: props.value,
};
}
/* $FlowFixMe[missing-local-annot] The type annotation(s) required by Flow's
* LTI update could not be added via codemod */
onSelectionChange({nativeEvent: {selection}}) {
this.setState({selection});
}
getRandomPosition(): number {
const length = this.state.value.length;
return Math.round(Math.random() * length);
}
select(start: number, end: number) {
this._textInput?.focus();
this.setState({selection: {start, end}});
if (this.props.imperative) {
this._textInput?.setSelection(start, end);
}
}
selectRandom() {
const positions = [
this.getRandomPosition(),
this.getRandomPosition(),
].sort();
this.select(...positions);
}
placeAt(position: number) {
this.select(position, position);
}
placeAtRandom() {
this.placeAt(this.getRandomPosition());
}
render(): React.Node {
const length = this.state.value.length;
return (
<View>
<View style={{flexDirection: 'row'}}>
<ExampleTextInput
testID={`${this.props.testID}-text-input`}
multiline={this.props.multiline}
onChangeText={value => this.setState({value})}
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
onSelectionChange={this.onSelectionChange.bind(this)}
ref={textInput => (this._textInput = textInput)}
selection={this.props.imperative ? undefined : this.state.selection}
style={this.props.style}
value={this.state.value}
/>
</View>
<View>
<Text testID={`${this.props.testID}-selection`}>
selection ={' '}
{`{start:${this.state.selection.start},end:${this.state.selection.end}}`}
</Text>
<Text
testID={`${this.props.testID}-cursor-start`}
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
onPress={this.placeAt.bind(this, 0)}>
Place at Start (0, 0)
</Text>
<Text
testID={`${this.props.testID}-cursor-end`}
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
onPress={this.placeAt.bind(this, length)}>
Place at End ({length}, {length})
</Text>
{/* $FlowFixMe[method-unbinding] added when improving typing for this
* parameters */}
<Text onPress={this.placeAtRandom.bind(this)}>Place at Random</Text>
<Text
testID={`${this.props.testID}-select-all`}
// $FlowFixMe[method-unbinding] added when improving typing for this parameters
onPress={this.select.bind(this, 0, length)}>
Select All
</Text>
{/* $FlowFixMe[method-unbinding] added when improving typing for this
* parameters */}
<Text onPress={this.selectRandom.bind(this)}>Select Random</Text>
</View>
</View>
);
}
}
function UncontrolledExample() {
const [isFocused, setIsFocused] = React.useState(false);
return (
<ExampleTextInput
defaultValue="Hello World!"
testID="uncontrolled-textinput"
style={isFocused ? styles.focusedUncontrolled : undefined}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
/>
);
}
const TextStylesExample = React.memo(() => {
const theme = useContext(RNTesterThemeContext);
return (
<TextStylesContainer
examples={[
{
name: 'backgroundColor',
textStyles: [
{backgroundColor: theme.SystemBackgroundColor},
{backgroundColor: 'red'},
{backgroundColor: 'orange'},
{backgroundColor: 'yellow'},
{backgroundColor: 'green'},
{backgroundColor: 'blue'},
],
},
{
name: 'color',
textStyles: [
{color: theme.LabelColor},
{color: 'red'},
{color: 'orange'},
{color: 'yellow'},
{color: 'green'},
{color: 'blue'},
],
},
{
name: 'fontFamily',
textStyles: [
{fontFamily: 'sans-serif'},
{fontFamily: 'serif'},
{fontFamily: 'monospace'},
],
},
{
name: 'fontSize',
textStyles: [
{fontSize: 8},
{fontSize: 10},
{fontSize: 12},
{fontSize: 14},
{fontSize: 16},
{fontSize: 18},
],
},
{
name: 'fontStyle',
textStyles: [{fontStyle: 'normal'}, {fontStyle: 'italic'}],
},
{
name: 'fontWeight',
textStyles: [
{fontWeight: 'normal'},
{fontWeight: 'bold'},
{fontWeight: '200'},
{fontWeight: '400'},
{fontWeight: '600'},
{fontWeight: '800'},
],
},
{
name: 'letterSpacing',
textStyles: [
{letterSpacing: 0},
{letterSpacing: 1},
{letterSpacing: 2},
{letterSpacing: 3},
{letterSpacing: 4},
{letterSpacing: 5},
],
},
{
name: 'lineHeight',
multiline: true,
textStyles: [
{lineHeight: 4},
{lineHeight: 8},
{lineHeight: 16},
{lineHeight: 24},
],
},
{
name: 'textDecorationLine',
textStyles: [
{textDecorationLine: 'none'},
{textDecorationLine: 'underline'},
{textDecorationLine: 'line-through'},
{textDecorationLine: 'underline line-through'},
],
},
{
name: 'textShadow',
textStyles: [
{
textShadowColor: 'black',
textShadowOffset: {width: 0, height: 0},
textShadowRadius: 0,
},
{
textShadowColor: 'black',
textShadowOffset: {width: 0, height: 0},
textShadowRadius: 5,
},
{
textShadowColor: 'red',
textShadowOffset: {width: 0, height: 0},
textShadowRadius: 5,
},
{
textShadowColor: 'blue',
textShadowOffset: {width: 0, height: -5},
textShadowRadius: 5,
},
{
textShadowColor: 'green',
textShadowOffset: {width: 0, height: 5},
textShadowRadius: 5,
},
{
textShadowColor: 'orange',
textShadowOffset: {width: 10, height: 0},
textShadowRadius: 5,
},
],
},
]}
/>
);
});
type TextStylesContainerProps = {
examples: $ReadOnlyArray<{
name: string,
textStyles: $ReadOnlyArray<TextStyle>,
multiline?: boolean,
}>,
};
function TextStylesContainer({examples}: TextStylesContainerProps) {
const [offset, setOffset] = useState(0);
const MAX_CYCLES = 6;
return (
<View>
<RNTesterButton
testID="cycle-styles"
textTestID="cycle-styles-label"
onPress={() => setOffset((offset + 1) % MAX_CYCLES)}>
Cycle {offset + 1}/{MAX_CYCLES}
</RNTesterButton>
<View>
<View testID="styles-screenshot-area" style={styles.screenshotArea} />
{examples.map(({name, multiline, textStyles}) => (
<WithLabel label={name} key={name}>
{multiline ? (
<MultilineStyledTextInput
name={name}
textStyles={textStyles}
styleOffset={offset}
/>
) : (
<StyledTextInput
name={name}
textStyles={textStyles}
styleOffset={offset}
/>
)}
</WithLabel>
))}
</View>
</View>
);
}
type StyledTextInputProps = {
name: string,
textStyles: $ReadOnlyArray<TextStyle>,
styleOffset: number,
};
function StyledTextInput({
name,
textStyles,
styleOffset,
}: StyledTextInputProps) {
return (
<ExampleTextInput
style={textStyles[(0 + styleOffset) % textStyles.length]}
testID={`style-${name}`}>
<Text>He</Text>
<Text style={textStyles[(1 + styleOffset) % textStyles.length]}>ll</Text>
<Text style={textStyles[(2 + styleOffset) % textStyles.length]}>
o,
<Text style={textStyles[(0 + styleOffset) % textStyles.length]}> </Text>
</Text>
<Text style={textStyles[(3 + styleOffset) % textStyles.length]}>Wo</Text>
<Text style={textStyles[(4 + styleOffset) % textStyles.length]}>rl</Text>
<Text style={textStyles[(5 + styleOffset) % textStyles.length]}>d!</Text>
</ExampleTextInput>
);
}
function MultilineStyledTextInput({
name,
textStyles,
styleOffset,
}: StyledTextInputProps) {
return (
<ExampleTextInput
multiline={true}
style={textStyles[(0 + styleOffset) % textStyles.length]}
testID={`style-${name}`}>
<Text>Hel{'\n'}</Text>
<Text style={textStyles[(1 + styleOffset) % textStyles.length]}>
lo {'\n'}
</Text>
<Text style={textStyles[(2 + styleOffset) % textStyles.length]}>
Wor{'\n'}
</Text>
<Text style={textStyles[(3 + styleOffset) % textStyles.length]}>ld!</Text>
</ExampleTextInput>
);
}
module.exports = ([
{
title: 'Auto-focus & select text on focus',
render: function (): React.Node {
return <AutoFocusWithSelectOnFocusTextExample />;
},
},
{
name: 'maxLength',
title: "Live Re-Write (<sp> -> '_') + maxLength",
render: function (): React.Node {
return <RewriteExample />;
},
},
{
title: 'Live Re-Write (no spaces allowed)',
render: function (): React.Node {
return <RewriteExampleInvalidCharacters />;
},
},
{
name: 'clearButton',
title: 'Live Re-Write (no spaces allowed) and clear',
render: function (): React.Node {
return <RewriteInvalidCharactersAndClearExample />;
},
},
{
title: 'Auto-capitalize',
name: 'autoCapitalize',
render: function (): React.Node {
return (
<View>
<WithLabel label="none">
<ExampleTextInput testID="capitalize-none" autoCapitalize="none" />
</WithLabel>
<WithLabel label="sentences">
<ExampleTextInput
testID="capitalize-sentences"
autoCapitalize="sentences"
/>
</WithLabel>
<WithLabel label="words">
<ExampleTextInput
testID="capitalize-words"
autoCapitalize="words"
/>
</WithLabel>
<WithLabel label="characters">
<ExampleTextInput
testID="capitalize-characters"
autoCapitalize="characters"
/>
</WithLabel>
</View>
);
},
},
{
title: 'Auto-correct',
render: function (): React.Node {
return (
<View>
<WithLabel label="true">
<ExampleTextInput autoCorrect={true} />
</WithLabel>
<WithLabel label="false">
<ExampleTextInput autoCorrect={false} />
</WithLabel>
</View>
);
},
},
{
title: 'Keyboard types',
name: 'keyboardTypes',
render: function (): React.Node {
const keyboardTypes = [
'default',
'ascii-capable',
'numbers-and-punctuation',
'url',
'number-pad',
'phone-pad',
'name-phone-pad',
'email-address',
'decimal-pad',
'twitter',
'web-search',
'ascii-capable-number-pad',
'numeric',
];
const examples = keyboardTypes.map(type => {
return (
<WithLabel key={type} label={type}>
<ExampleTextInput keyboardType={type} />
</WithLabel>
);
});
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}>
<ExampleTextInput inputMode={mode} />
</WithLabel>
);
});
return <View>{examples}</View>;
},
},
{
title: 'Blur on submit',
render: function (): React.Element<any> {
return <BlurOnSubmitExample />;
},
},
{
title: 'enterKeyHint modes',
name: 'enterKeyHintTypes',
render: function (): React.Node {
const enterKeyHintTypesHints = [
'enter',
'done',
'go',
'next',
'previous',
'search',
'send',
];
const examples = enterKeyHintTypesHints.map(hint => {
return (
<WithLabel key={hint} label={hint}>
<ExampleTextInput enterKeyHint={hint} />
</WithLabel>
);
});
return <View>{examples}</View>;
},
},
{
title: 'Submit behavior',
render: function (): React.Element<any> {
return <SubmitBehaviorExample />;
},
},
{
title: 'Event handling',
render: function (): React.Element<any> {
return <TextEventsExample />;
},
},
{
title: 'fontFamily, fontWeight and fontStyle',
render: function (): React.Node {
const fontFamilyA = Platform.OS === 'ios' ? 'Cochin' : 'sans-serif';
const fontFamilyB = Platform.OS === 'ios' ? 'Courier' : 'serif';
return (
<View>
<ExampleTextInput
style={[styles.singleLine, {fontFamily: fontFamilyA}]}
placeholder={`Custom fonts like ${fontFamilyA} are supported`}
/>
<ExampleTextInput
style={[
styles.singleLine,
{fontFamily: fontFamilyA, fontWeight: 'bold'},
]}
placeholder={`${fontFamilyA} bold`}
/>
<ExampleTextInput
style={[
styles.singleLine,
{fontFamily: fontFamilyA, fontWeight: '500'},
]}
placeholder={`${fontFamilyA} 500`}
/>
<ExampleTextInput
style={[
styles.singleLine,
{fontFamily: fontFamilyA, fontStyle: 'italic'},
]}
placeholder={`${fontFamilyA} italic`}
/>
<ExampleTextInput
style={[styles.singleLine, {fontFamily: fontFamilyB}]}
placeholder={fontFamilyB}
/>
</View>
);
},
},
{
title: 'Attributed text',
name: 'attributedText',
render: function (): React.Node {
return <TokenizedTextExample />;
},
},
{
title: 'Text selection & cursor placement',
name: 'cursorPlacement',
render: function (): React.Node {
return (
<View>
<SelectionExample
testID="singleline"
value="text selection can be changed"
/>
<SelectionExample
testID="multiline"
multiline
style={styles.multiline}
value={'multiline text selection\ncan also be changed'}
/>
</View>
);
},
},
{
title: 'Text selection & cursor placement (imperative)',
name: 'cursorPlacementImperative',
render: function (): React.Node {
return (
<View>
<SelectionExample
testID="singlelineImperative"
value="text selection can be changed imperatively"
imperative={true}
/>
<SelectionExample
testID="multilineImperative"
multiline
style={styles.multiline}
value={'multiline text selection\ncan also be changed imperatively'}
imperative={true}
/>
</View>
);
},
},
{
title: 'Uncontrolled component with layout changes',
name: 'uncontrolledComponent',
render: () => <UncontrolledExample />,
},
{
title: 'Text styles',
name: 'textStyles',
render: () => <TextStylesExample />,
},
{
title: 'showSoftInputOnFocus',
render: function (): React.Node {
return (
<View>
<WithLabel label="showSoftInputOnFocus: false">
<ExampleTextInput showSoftInputOnFocus={false} />
</WithLabel>
</View>
);
},
},
{
title: 'Clipping',
name: 'clipping',
render: function (): React.Node {
return (
<View>
<ExampleTextInput
multiline={true}
testID="textinput-clipping"
style={{
borderRadius: 50,
padding: 0,
borderColor: 'red',
borderWidth: 5,
overflow: 'hidden',
fontSize: 16,
}}>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut labore et dolore magna aliqua.
</ExampleTextInput>
</View>
);
},
},
]: Array<RNTesterModuleExample>);