Files
react-native/packages/rn-tester/js/examples/TextInput/TextInputSharedExamples.js
T
Nick GerlemanandFacebook GitHub Bot 5e7c0d2096 Add Jest E2E Tests for S462299 (#50000)
Summary:
Pull Request resolved: https://github.com/facebook/react-native/pull/50000

This adds some tests that verify that controlled textinput, via text prop, or children creating attributedstring, does not jumble text, so long as JS side does not mutate underlying text content.

The rewriting/max length example does still run into problems here.

Changelog: [Internal]

Reviewed By: joevilches

Differential Revision: D71076703

fbshipit-source-id: 85995e8cc087533b1115f158ba5dad67ed008289
2025-03-12 19:45:29 -07:00

1265 lines
33 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 RNTesterText from '../../components/RNTesterText';
import {RNTesterThemeContext} from '../../components/RNTesterTheme';
import ExampleTextInput from './ExampleTextInput';
import * as React from 'react';
import {useContext, useState} from 'react';
import {
Button,
Platform,
StyleSheet,
Switch,
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,
},
wrappedText: {
maxWidth: 300,
},
});
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}>
<RNTesterText style={styles.label}>{this.props.label}</RNTesterText>
<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 | React.ElementRef<typeof ExampleTextInput>};
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}
/>
<RNTesterText style={styles.eventLabel}>
{this.state.curText}
{'\n'}
(prev: {this.state.prevText}){'\n'}
(prev2: {this.state.prev2Text}){'\n'}
(prev3: {this.state.prev3Text})
</RNTesterText>
</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>
<RNTesterText testID={`${this.props.testID}-selection`}>
selection ={' '}
{`{start:${this.state.selection.start},end:${this.state.selection.end}}`}
</RNTesterText>
<RNTesterText
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)
</RNTesterText>
<RNTesterText
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})
</RNTesterText>
{/* $FlowFixMe[method-unbinding] added when improving typing for this
* parameters */}
<RNTesterText onPress={this.placeAtRandom.bind(this)}>
Place at Random
</RNTesterText>
<RNTesterText
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
</RNTesterText>
{/* $FlowFixMe[method-unbinding] added when improving typing for this
* parameters */}
<RNTesterText onPress={this.selectRandom.bind(this)}>
Select Random
</RNTesterText>
</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>
);
}
function DynamicContentWidth() {
const [text, setText] = useState('');
const update = () => {
const randomNumber = Math.floor(Math.random() * 10);
setText(text + randomNumber);
};
return (
<View>
<RNTesterText>Uncontrolled:</RNTesterText>
<TextInput
testID="dynamic-width-uncontrolled-textinput"
placeholder="Type..."
style={{
fontSize: 16,
alignSelf: 'center',
backgroundColor: 'orange',
}}
/>
<RNTesterText>Controlled:</RNTesterText>
<TextInput
testID="dynamic-width-controlled-textinput"
placeholder="..."
value={text}
onChangeText={setText}
style={{
fontSize: 16,
alignSelf: 'center',
backgroundColor: 'orange',
}}
/>
<Button title="Update controlled Input" onPress={update} />
</View>
);
}
function AutogrowingTextInputExample({
style,
...props
}: React.ElementConfig<typeof TextInput>) {
const [multiline, setMultiline] = React.useState(true);
const [fullWidth, setFullWidth] = React.useState(true);
const [text, setText] = React.useState('');
const [contentSize, setContentSize] = React.useState({width: 0, height: 0});
return (
<View>
<RNTesterText>Full width:</RNTesterText>
<Switch value={fullWidth} onValueChange={setFullWidth} />
<RNTesterText>Multiline:</RNTesterText>
<Switch value={multiline} onValueChange={setMultiline} />
<RNTesterText>TextInput:</RNTesterText>
<ExampleTextInput
multiline={multiline}
style={[style, {width: fullWidth ? '100%' : '50%'}]}
onChangeText={setText}
onContentSizeChange={({nativeEvent}) => {
setContentSize({
width: nativeEvent.contentSize.width,
height: nativeEvent.contentSize.height,
});
}}
{...props}
/>
<RNTesterText>Plain text value representation:</RNTesterText>
<RNTesterText>{text}</RNTesterText>
<RNTesterText>Content Size: {JSON.stringify(contentSize)}</RNTesterText>
</View>
);
}
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.MixedElement {
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.MixedElement {
return <SubmitBehaviorExample />;
},
},
{
title: 'Event handling',
render: function (): React.MixedElement {
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>
);
},
},
{
title: 'Dynamic content width',
name: 'dynamicWidth',
render: function (): React.Node {
return <DynamicContentWidth />;
},
},
{
title: 'Auto-expanding',
render: function (): React.Node {
return (
<View style={styles.wrappedText}>
<AutogrowingTextInputExample
enablesReturnKeyAutomatically={true}
returnKeyType="done"
style={{maxHeight: 400, minHeight: 20, backgroundColor: '#eeeeee'}}>
generic generic generic
<Text style={{fontSize: 6, color: 'red'}}>
small small small small small small
</Text>
<Text>regular regular</Text>
<Text style={{fontSize: 30, color: 'green'}}>
huge huge huge huge huge
</Text>
generic generic generic
</AutogrowingTextInputExample>
</View>
);
},
},
]: Array<RNTesterModuleExample>);