* Copyright (c) 2024 Huawei Technologies Co., Ltd.
*
* This source code is licensed under the MIT license found in the
* LICENSE-MIT file in the root directory of this source tree.
*/
import {
EnterKeyHintType,
EnterKeyHintTypeAndroid,
KeyboardTypeOptions,
Platform,
ReturnKeyType,
ReturnKeyTypeAndroid,
StyleSheet,
Text,
TextInput,
TextInputProps,
View,
ScrollView
} from 'react-native';
import {TestSuite} from '@rnoh/testerino';
import {useState, useRef, useEffect} from 'react';
import {Button, Effect, StateKeeper, TestCase} from '../components';
const KEYBOARD_TYPES: KeyboardTypeOptions[] = [
'default',
'number-pad',
'decimal-pad',
'numeric',
'email-address',
'phone-pad',
'url',
];
export function TextInputTest() {
return (
<TestSuite name="TextInput">
<TestCase.Example itShould="render textinput and change the text component based on the values inputted">
<TextInputWithText style={styles.textInput} />
</TestCase.Example>
<TestCase.Example
modal
itShould="not crash when invalid keyboardType is supplied">
<TextInputWithText
style={styles.textInput}
keyboardType={'invalid_type' as KeyboardTypeOptions}
/>
</TestCase.Example>
<TestCase.Example itShould="render textinput with set content">
<TextInputWithTextContent style={styles.textInput} />
</TestCase.Example>
<TestCase.Example itShould="render non-editable textInput">
<TextInputWithText
defaultValue="test"
style={styles.textInput}
editable={false}
/>
</TestCase.Example>
<TestCase.Example itShould="render textInput with Pacifico Regular font">
<TextInputWithText
style={[styles.textInput, {fontFamily: 'Pacifico-Regular'}]}
/>
</TestCase.Example>
<TestCase.Example itShould="render textInput with caret hidden">
<TextInputWithText style={styles.textInput} caretHidden />
</TestCase.Example>
<TestCase.Example itShould="render textInput with red caret">
<TextInputWithText style={styles.textInput} cursorColor={'red'} />
</TestCase.Example>
<TestCase.Manual
itShould="report content size changes (onContentSizeChange)"
initialState={false}
skip={{android: false, harmony: true}}
arrange={({setState}) => {
return (
<TextInputWithText
style={styles.textInput}
multiline
onContentSizeChange={() => setState(true)}
/>
);
}}
assert={({expect, state}) => {
expect(state).to.be.true;
}}
/>
<TestCase.Example
modal
tags={['C_API']}
itShould="report content size changes with parameter(onContentSizeChange)">
<OnContentSizeChangeView />
</TestCase.Example>
<TestSuite name="focus/blur">
<TestCase.Manual
itShould="blur text on submit (singleline)"
initialState={false}
arrange={({setState}) => {
return (
<>
<TextInputWithText
style={styles.textInput}
blurOnSubmit
onBlur={() => setState(true)}
/>
</>
);
}}
assert={({expect, state}) => {
expect(state).to.be.true;
}}
/>
<TestCase.Manual
itShould="blur text after switching to another textinput"
initialState={false}
arrange={({setState}) => {
return (
<>
<TextInputWithText
style={styles.textInput}
onBlur={() => setState(true)}
/>
<TextInputWithText
style={styles.textInput}
onBlur={() => setState(true)}
/>
</>
);
}}
assert={({expect, state}) => {
expect(state).to.be.true;
}}
/>
<TestCase.Example
modal
itShould="not blur text on submit"
skip={{android: false, harmony: true}}
//https://gl.swmansion.com/rnoh/react-native-harmony/-/issues/403
>
<TextInputWithText style={styles.textInput} blurOnSubmit={false} />
<TextInputWithText
style={styles.textInput}
blurOnSubmit={false}
multiline
/>
</TestCase.Example>
<TestCase.Manual
modal
itShould="automatically focus textInput when displayed"
initialState={false}
arrange={({setState}) => (
<TextInputWithText
style={styles.textInput}
autoFocus
onFocus={() => setState(true)}
/>
)}
assert={({expect, state}) => {
expect(state).to.be.true;
}}
/>
<TestCase.Manual
itShould="focus textInput on click"
initialState={false}
arrange={({setState}) => (
<TextInput
style={styles.textInput}
onFocus={() => setState(true)}
/>
)}
assert={({expect, state}) => {
expect(state).to.be.true;
}}
/>
<TestCase.Manual
modal
itShould="focus textInput when pressing the button"
initialState={false}
arrange={({setState}) => <FocusTextInputTest setState={setState} />}
assert={({state, expect}) => {
expect(state).to.be.true;
}}
/>
<TestCase.Manual
modal
itShould="When the TextInput is unmounted, other components will not gain focus passively."
initialState={'init'}
arrange={({setState}) => <DisableFocusPassivelyTest setState={setState} />}
assert={({state, expect}) => {
expect(state).to.be.eq('pass');
}}
/>
</TestSuite>
<TestCase.Example
modal
itShould="render textInput with blue underline"
skip={{android: false, harmony: true}}
//https://gl.swmansion.com/rnoh/react-native-harmony/-/issues/404
>
<TextInputWithText
style={styles.textInput}
underlineColorAndroid={'blue'}
/>
</TestCase.Example>
<TestCase.Example itShould="render textinput with red placeholder">
<TextInputWithText
style={styles.textInput}
placeholder="Placeholder"
placeholderTextColor={'red'}
/>
</TestCase.Example>
<TestCase.Example itShould="render textinput with default selection color">
<TextInputWithText style={styles.textInput} />
</TestCase.Example>
<TestCase.Example itShould="render textinput with green selection color">
<TextInputWithText style={styles.textInput} selectionColor="green" />
</TestCase.Example>
<TestCase.Example itShould="render multiline text input">
<TextInputWithText style={styles.textInputBigger} multiline={true} />
</TestCase.Example>
<TestCase.Example itShould="render multiline text input with common border">
<TextInputWithText style={styles.multilineInput} multiline={true} />
</TestCase.Example>
<TestCase.Example itShould="render multiline text input with Pacifico Regular font">
<TextInputWithText
style={[styles.textInputBigger, {fontFamily: 'Pacifico-Regular'}]}
multiline
/>
</TestCase.Example>
<TestCase.Example itShould="render text input with maximally 10 characters">
<TextInputWithText style={styles.textInput} maxLength={10} />
</TestCase.Example>
<TestCase.Example modal itShould="toggle between maxLength 5 and 0">
<SwitchBetweenDiffMaxLength />
</TestCase.Example>
<TestCase.Example
modal
itShould="toggle between rendering 10 and 5 characters">
<StateKeeper
initialValue={10}
renderContent={(maxLength, setMaxLength) => {
return (
<Effect
onMount={() => {
const interval = setInterval(() => {
setMaxLength(prev => (prev === 10 ? 5 : 10));
}, 1000);
return () => {
clearInterval(interval);
};
}}>
<TextInputWithText
style={styles.textInput}
maxLength={maxLength}
value="1234567890"
/>
</Effect>
);
}}
/>
</TestCase.Example>
<TestCase.Example
modal
itShould="toggle between different capitalization modes"
skip={{android: false, harmony: true}}
//https://gl.swmansion.com/rnoh/react-native-harmony/-/issues/408
>
<AutoCapitalize />
</TestCase.Example>
<TestCase.Manual
modal
itShould="trigger onSubmitEditing event after submiting"
initialState={false}
arrange={({setState}) => (
<TextInputWithText
style={styles.textInput}
onSubmitEditing={() => setState(true)}
/>
)}
assert={({expect, state}) => {
expect(state).to.be.true;
}}
/>
<TestCase.Example modal itShould="toggle between different return keys">
<ReturnKeyTypeView />
</TestCase.Example>
<TestCase.Example modal itShould="toggle between different enter keys">
<EnterKeyHintExample />
</TestCase.Example>
<TestCase.Example itShould="render secure text input (text obscured)">
<TextInputWithText style={styles.textInput} secureTextEntry />
</TestCase.Example>
<TestCase.Manual
modal
itShould="trigger onKeyPress event after pressing delete key (press 'Backspace' to pass)"
initialState={''}
arrange={({setState}) => (
<TextInputWithText
style={styles.textInput}
autoFocus
onKeyPress={event => {
setState(event.nativeEvent.key);
}}
/>
)}
assert={({expect, state}) => {
expect(state).to.be.eq('Backspace');
}}
/>
<TestCase.Manual
modal
itShould="trigger onKeyPress event after pressing key (press 'A' to pass)"
initialState={''}
arrange={({setState}) => (
<TextInputWithText
style={styles.textInput}
autoFocus
onKeyPress={event => {
setState(event.nativeEvent.key);
}}
/>
)}
assert={({expect, state}) => {
expect(state).to.be.eq('A');
}}
/>
<TestCase.Manual
modal
skip={{android: false, harmony: {arkTS: true, cAPI: false}}}
//https://gl.swmansion.com/rnoh/react-native-harmony/-/issues/736
itShould="trigger onKeyPress event after pressing backspace"
initialState={''}
arrange={({setState}) => (
<>
<TextInputWithText
style={styles.textInput}
autoFocus
defaultValue="a"
onKeyPress={event => setState(event.nativeEvent.key)}
/>
<TextInputWithText
style={styles.textInputBigger}
autoFocus
multiline
defaultValue="a"
onKeyPress={event => setState(event.nativeEvent.key)}
/>
</>
)}
assert={({expect, state}) => {
expect(state).to.be.eq('Backspace');
}}
/>
<TestCase.Example
modal
itShould="show text input with default value (defaultProps)">
<DefaultProps />
</TestCase.Example>
<TestCase.Example itShould="show text input with default value (defaultValue)">
<TextInput style={styles.textInput} defaultValue="defaultText" />
</TestCase.Example>
<TestCase.Manual
modal
itShould="trigger onLayout event on mount"
initialState={{}}
arrange={({setState, state}) => {
return (
<>
<Text>{JSON.stringify(state)}</Text>
<TextInput
style={styles.textInput}
onLayout={event => {
setState(event.nativeEvent.layout);
}}
/>
</>
);
}}
assert={({expect, state}) => {
expect(state).to.include.all.keys('width', 'height', 'x', 'y');
}}
/>
<TestCase.Example
modal
itShould="render textinputs with different keyboard types">
<View>
{KEYBOARD_TYPES.map(type => (
<TextInputKeyboardType
key={`singleline_${type}`}
keyboardType={type}
/>
))}
</View>
</TestCase.Example>
<TestCase.Example
modal
itShould="render multiline textinputs with different keyboard types">
<View>
{KEYBOARD_TYPES.map(type => (
<TextInputKeyboardType
key={`multiline_${type}`}
keyboardType={type}
multiline
/>
))}
</View>
</TestCase.Example>
<TestCase.Example modal itShould="render textinput with allowFontScaling">
<TextInputWithText
style={styles.textInput}
allowFontScaling
defaultValue="Scaled"
/>
<TextInputWithText
style={styles.textInput}
allowFontScaling={false}
defaultValue="Not scaled"
/>
<TextInputWithText
style={[styles.textInput, {fontSize: 40}]}
allowFontScaling={true}
defaultValue="Scaled big"
/>
<TextInputWithText
style={[styles.textInput, {fontSize: 40}]}
allowFontScaling={false}
defaultValue="Not scaled big"
/>
</TestCase.Example>
<TestCase.Example itShould="show textInput with padding" modal>
<View style={{width: 300, height: 200}}>
<TextInputWithText
style={{
paddingLeft: 10,
paddingTop: 20,
paddingRight: 30,
paddingBottom: 40,
backgroundColor: 'red',
}}
/>
</View>
</TestCase.Example>
<TestCase.Example itShould="show textInput multiline with padding" modal>
<View style={{width: 300, height: 200}}>
<TextInputWithText
style={{
paddingLeft: 10,
paddingTop: 20,
paddingRight: 30,
paddingBottom: 40,
backgroundColor: 'red',
height: 100,
}}
multiline
/>
</View>
</TestCase.Example>
<TestCase.Example modal itShould="render textinput with readonly">
<TextInputWithText
style={styles.textInput}
defaultValue="readOnly"
// @ts-ignore
readOnly
/>
</TestCase.Example>
<TestCase.Example
modal
itShould="display bold, italic, large placeholder with a custom font">
<TextInput
style={{
fontFamily: 'StintUltraCondensed-Regular',
fontWeight: 'bold',
fontSize: 24,
fontStyle: 'italic',
}}
placeholder="placeholder"
/>
</TestCase.Example>
<TestCase.Example modal itShould="render textinput with red text color">
<TextInputWithText style={[styles.textInput, {color: 'red'}]} />
</TestCase.Example>
<TestCase.Example modal itShould="clear text on focus">
{/* iOS only */}
<TextInputWithText
style={styles.textInput}
defaultValue="Hello, World!"
clearTextOnFocus
/>
</TestCase.Example>
<TestCase.Example
// Only "unless-editing" mode doesn't work on C_API
modal
itShould="use different clearButtonMode values">
<TextInputWithText style={styles.textInput} clearButtonMode="always" />
<Text>clearButtonMode="always"</Text>
{
/* Except Android */
Platform.OS !== 'android' && (
<>
<TextInputWithText
style={styles.textInput}
clearButtonMode="while-editing"
/>
<Text>clearButtonMode="while-editing"</Text>
<TextInputWithText
style={styles.textInput}
clearButtonMode="unless-editing"
/>
<Text>clearButtonMode="unless-editing"</Text>
</>
)
}
<TextInputWithText style={styles.textInput} clearButtonMode="never" />
{}
<Text style={{marginBottom: 200}}>clearButtonMode="never"</Text>
</TestCase.Example>
<TestCase.Example modal itShould="show selection start and end values">
<TextSelectionChangeTest />
</TestCase.Example>
<TestCase.Example
modal
itShould="show textinput with 'Selection' substring selected">
<SelectionTest />
</TestCase.Example>
<TestCase.Example
modal
itShould="show controlled textinput with 'Selection' substring selected">
<ControlledSelectionTest />
</TestCase.Example>
<TestCase.Example
modal
itShould="not show text context menu when long press">
<ContextMenuHiddenTest />
</TestCase.Example>
<TestCase.Example modal itShould="select text on focus">
<TextInput
value="selectTextOnFocus"
selectTextOnFocus
style={styles.textInput}
/>
<TextInput
value={'selectTextOnFocus \n but multiline'}
selectTextOnFocus
multiline
style={[styles.textInputBigger, {marginTop: 10}]}
/>
</TestCase.Example>
<TestCase.Example
modal
itShould="allow to change the text input value via props">
<TextInputValueSetProgrammatically />
</TestCase.Example>
<TestCase.Example
modal
itShould="should show transparent TextInput on a red background">
<View style={{backgroundColor: 'red'}}>
<TextInput
value="parent is red but I am transparent"
style={{height: 40}}
/>
</View>
</TestCase.Example>
<TestCase.Manual
modal
itShould="trigger onEndEditing event after editing ends"
initialState={false}
arrange={({setState}) => (
<TextInputWithText
style={styles.textInput}
autoFocus
onEndEditing={() => setState(true)}
/>
)}
assert={({expect, state}) => {
expect(state).to.be.true;
}}
/>
<TestCase.Manual
modal
itShould="trigger onChange event after changing value"
initialState={false}
arrange={({setState}) => (
<TextInputWithText onChange={() => setState(true)} />
)}
assert={({expect, state}) => {
expect(state).to.be.true;
}}
/>
<TestCase.Example
modal
itShould="set cursorColor to red after pressing button (setNativeProps)">
<SetNativePropsTest />
</TestCase.Example>
<TestCase.Example
modal
itShould="select only Selection word (selectTextOnFocus && selection)">
<TextInput
style={styles.textInput}
selection={{start: 4, end: 13}}
value="TextSelectionTest"
selectTextOnFocus
/>
</TestCase.Example>
<TestCase.Manual
modal
itShould="not trigger onChange event after changing value through state"
initialState={{text: '', onChangeFired: false}}
arrange={({setState, state}) => {
return (
<View>
<TextInput
style={styles.textInput}
value={state.text}
onChange={() => {
setState(prev => ({...prev, onChangeFired: true}));
}}
/>
<Button
label="change text"
onPress={() =>
setState(prev => ({...prev, text: prev.text + 'a'}))
}
/>
</View>
);
}}
assert={({expect, state}) => {
expect(state.onChangeFired).to.be.false;
expect(state.text).to.not.be.equal('');
}}
/>
<TestCase.Example
modal
itShould="change the text input value programmatically on blur">
<TextInputValueSetProgrammaticallyOnBlur />
</TestCase.Example>
<TestCase.Example
modal
itShould="change the text input value programmatically when submitted">
<TextInputValueSetProgrammaticallyWhenSubmitted />
</TestCase.Example>
<TestCase.Example
modal
itShould="extended textContentType test (based on SDK API 16)">
<TextInputTextContentType />
</TestCase.Example>
<TestCase.Example
modal
itShould="When entering a bank card number, the cursor should
automatically move past the space after encountering one.">
<TextInputBankCardNumber />
</TestCase.Example>
<TestCase.Example
modal
itShould="cursor should be at the end when focusing a textinput with value"
>
<FocusWithCursorTest />
</TestCase.Example>
<TestCase.Example
modal
itShould="show textinput with predefined selection">
<SelectionTestComponent />
</TestCase.Example>
<TestCase.Example
modal
itShould="display onKeyPress event data">
<OnKeyPressTest />
</TestCase.Example>
<TestCase.Example
modal
itShould="display onChange event data">
<OnChangeTest />
</TestCase.Example>
<TestCase.Example
modal
itShould="clear textinput by calling clear method">
<ClearButtonTest />
</TestCase.Example>
<TestCase.Example
modal
itShould="format zipcode input (add dash after 5 digits)">
<ZipCodeTextInputMask />
</TestCase.Example>
</TestSuite>
);
}
const SetNativePropsTest = () => {
const ref = useRef<TextInput>(null);
return (
<View>
<TextInput cursorColor={'blue'} ref={ref} />
<Button
label="setCursorColor"
onPress={() => ref.current?.setNativeProps({cursorColor: 'red'})}
/>
</View>
);
};
const SelectionTest = () => {
return (
<View>
<TextInput
style={styles.textInput}
value="TextSelectionTest"
selection={{start: 4, end: 13}}
/>
<TextInput
style={[styles.textInputBigger, {marginTop: 10}]}
value={'TextSelect\nionTest'}
selection={{start: 4, end: 13}}
multiline
/>
</View>
);
};
const ContextMenuHiddenTest = () => {
return (
<View>
<TextInput
style={styles.textInput}
value="ContextMenuHiddenTest"
contextMenuHidden={true}
/>
<TextInput
style={[styles.textInput, {marginTop: 5}]}
value="ContextMenuHiddenTest_multiline"
multiline={true}
contextMenuHidden={true}
/>
</View>
);
};
const TextSelectionChangeTest = () => {
const [eventData, setEventData] = useState('');
return (
<View>
<TextInput
style={styles.textInput}
onSelectionChange={event =>
setEventData(JSON.stringify(event.nativeEvent.selection))
}
/>
<Text style={{marginVertical: 8}}>Selection {eventData}</Text>
</View>
);
};
const FocusTextInputTest = (props: {
setState: React.Dispatch<React.SetStateAction<boolean>>;
}) => {
const ref = useRef<TextInput>(null);
return (
<View>
<Button label="focus text input" onPress={() => ref.current?.focus()} />
<TextInput onFocus={() => props.setState(true)} ref={ref} />
</View>
);
};
const DisableFocusPassivelyTest = (props: {
setState: React.Dispatch<React.SetStateAction<string>>;
}) => {
const ref = useRef<TextInput>(null);
const [show, setShow] = useState(true);
const [firstFocus, setFirstFocus] = useState(false);
const [secondFocus, setSecondFocus] = useState(false);
const [done, setDone] = useState(false);
useEffect(() => {
setTimeout(() => {
setShow(false);
}, 300);
setTimeout(() => {
setShow(true);
}, 400);
setTimeout(() => {
setShow(false);
ref.current?.focus();
}, 500);
setTimeout(() => {
setDone(true)
}, 600);
}, [])
useEffect(() => {
if (done) {
props.setState((firstFocus && !secondFocus) ? 'pass' : 'fail');
}
}, [done])
return (
<View collapsable={false} style={{gap: 10}}>
<TextInput
ref={ref}
placeholder="TextInput1"
style={{backgroundColor: 'pink', height: 40}}
onFocus={() => {
setFirstFocus(true);
}}
/>
<TextInput
placeholder="TextInput2"
style={{backgroundColor: 'orange', height: 40}}
onFocus={() => {
setSecondFocus(true);
}}
/>
{show ? (
<TextInput
placeholder="TextInput3"
autoFocus={true}
style={{backgroundColor: 'blue', height: 40}}
/>
) : null}
</View>
);
};
const TextInputKeyboardType = (props: TextInputProps) => {
return (
<>
<Text>{props.keyboardType}</Text>
<TextInput
style={{...styles.textInputSmall, marginBottom: 10}}
keyboardType={props.keyboardType}
multiline={props.multiline}
/>
</>
);
};
const TextInputWithTextContent = (props: TextInputProps) => {
const texts = [
'Text content',
'Hello, World!',
'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
];
const [index, setIndex] = useState(0);
return (
<>
<TextInput {...props}>{texts[index]}</TextInput>
<Button
label="Toggle text"
onPress={() => setIndex(i => (i + 1) % texts.length)}
/>
</>
);
};
const TextInputWithText = (props: TextInputProps) => {
const [text, onChangeText] = useState(props.defaultValue ?? '');
return (
<>
<Text style={styles.text}>{text}</Text>
<TextInput {...props} onChangeText={onChangeText} value={text} />
</>
);
};
type CapitalizationType = 'none' | 'sentences' | 'words' | 'characters';
const AutoCapitalize = () => {
const [state, setState] = useState<CapitalizationType>('none');
const capitalizations: Array<CapitalizationType> = [
'none',
'sentences',
'words',
'characters',
];
const toggleCapitalization = () => {
const index = capitalizations.indexOf(state);
setState(capitalizations[(index + 1) % capitalizations.length]);
};
return (
<>
<TextInputWithText style={styles.textInput} autoCapitalize={state} />
<Button label="toggle capitalize mode" onPress={toggleCapitalization} />
<Text>Capitalize mode: {state}</Text>
</>
);
};
const SwitchBetweenDiffMaxLength = () => {
const [state, setState] = useState(5)
return (
<>
<TextInputWithText style={styles.textInput} maxLength={state} />
<Button label="toggle return key type" onPress={() => {setState(state == 0 ? 5 : 0)}} />
<Text>maxLength: {state}</Text>
</>
);
}
const ReturnKeyTypeView = () => {
const [state, setState] = useState<ReturnKeyType | ReturnKeyTypeAndroid>(
'none',
);
const returnKey: Array<ReturnKeyType | ReturnKeyTypeAndroid> = [
'none',
'done',
'go',
'next',
'search',
'send',
'none',
'previous',
];
const toggleReturnKey = () => {
const index = returnKey.indexOf(state);
setState(returnKey[(index + 1) % returnKey.length]);
};
return (
<>
<TextInputWithText style={styles.textInput} returnKeyType={state} />
<Button label="toggle return key type" onPress={toggleReturnKey} />
<Text>Return key: {state}</Text>
</>
);
};
const OnContentSizeChangeView = () => {
const [width, setWidth] = useState<number>(0);
const [height, setHeight] = useState<number>(0);
const [inputWidth, setInputWidth] = useState<number>(0);
const [inputHeight, setInputHeight] = useState<number>(0);
const renderSize = (event: any) => {
setWidth(event.nativeEvent.contentSize.width);
setHeight(event.nativeEvent.contentSize.height);
}
const renderInputSize = (event: any) => {
setInputWidth(event.nativeEvent.contentSize.width);
setInputHeight(event.nativeEvent.contentSize.height);
}
return (
<View style={{backgroundColor: 'black'}}>
<Text style={styles.text}>Textinput content size:</Text>
<TextInputWithText
style={styles.textInput}
onContentSizeChange={(event) => renderInputSize(event)}
/>
<Text style={styles.text}>input content size width: {inputWidth}</Text>
<Text style={styles.text}>input content size height: {inputHeight}</Text>
<Text style={styles.text}>Multiline textinput content size:</Text>
<TextInputWithText
style={styles.textInputBigger}
multiline
onContentSizeChange={(event) => renderSize(event)}
/>
<Text style={styles.text}>content size width: {width}</Text>
<Text style={styles.text}>content size height: {height}</Text>
</View>
);
}
const EnterKeyHintExample = () => {
const [index, setIndex] = useState(0);
const enterKey: Array<EnterKeyHintType | EnterKeyHintTypeAndroid> = [
'done',
'go',
'next',
'previous',
'search',
'send',
];
const toggleEnterKey = () => {
setIndex(i => (i + 1) % enterKey.length);
};
return (
<>
<TextInputWithText
style={styles.textInput}
enterKeyHint={enterKey[index]}
/>
<Button label="toggle enter key type" onPress={toggleEnterKey} />
<Text>Enter key: {enterKey[index]}</Text>
</>
);
};
const DefaultProps = () => {
TextInput.defaultProps = {
value: 'defaultText',
};
return <TextInput style={styles.textInput} />;
};
const TextInputValueSetProgrammatically = () => {
const [value, setValue] = useState('Default Text Input Value');
return (
<View style={{height: 400}}>
<View style={{marginBottom: 16}}>
<Button
label="Set 'Hello, World!' as text input value "
onPress={() => setValue('Hello, World!')}
/>
<Button
label="Set 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.' as text input value "
onPress={() =>
setValue('Lorem ipsum dolor sit amet, consectetur adipiscing elit.')
}
/>
<Button label="Clear text input value" onPress={() => setValue('')} />
</View>
<TextInput
style={styles.textInput}
defaultValue={value}
value={value}
onChangeText={setValue}
/>
</View>
);
};
const ControlledSelectionTest = () => {
const [value, setValue] = useState('TextSelectionTest');
const [valueM, setValueM] = useState('TextSelect\nionTest');
return (
<View>
<TextInput
style={styles.textInput}
value={value}
onChange={event => setValue(event.nativeEvent.text)}
selection={{start: 4, end: 13}}
/>
<TextInput
style={[styles.textInputBigger, {marginTop: 10}]}
value={valueM}
onChange={event => setValueM(event.nativeEvent.text)}
selection={{start: 4, end: 13}}
multiline
/>
</View>
);
};
const TextInputValueSetProgrammaticallyOnBlur = () => {
const [value, setValue] = useState("I'm the first text input.");
return (
<>
<TextInput
value={value}
autoFocus
onChangeText={text => setValue(text)}
style={styles.textInput}
/>
<TextInput
onFocus={() => setValue('It works!')}
multiline
defaultValue="Click me and see if the value of the first text input has been changed."
style={styles.textInputBigger}
/>
</>
);
};
const TextInputValueSetProgrammaticallyWhenSubmitted = () => {
const [value, setValue] = useState('(1) Submit the text input.');
return (
<>
<TextInput
value={value}
autoFocus
onChangeText={text => setValue(text)}
style={styles.textInput}
/>
<Button
label="(2) Press to change the text of the text input"
onPress={() => setValue('(3) It works!')}
/>
</>
);
};
const TextInputTextContentType = () => {
return (
<ScrollView>
<Text>PassportNumber Input:</Text>
<TextInput
style={styles.textInput}
textContentType="passPortNumber"
/>
<Text>validity Input:</Text>
<TextInput
style={styles.textInput}
textContentType="validity"
/>
<Text>issueAt Input:</Text>
<TextInput
style={styles.textInput}
textContentType="issueAt"
/>
<Text>organization Input:</Text>
<TextInput
style={styles.textInput}
textContentType="organization"
/>
<Text>taxId Input:</Text>
<TextInput
style={styles.textInput}
textContentType="taxId"
/>
<Text>addressCityAndState Input:</Text>
<TextInput
style={styles.textInput}
textContentType="addressCityAndState"
/>
<Text>flightNumber Input:</Text>
<TextInput
style={styles.textInput}
textContentType="flightNumber"
/>
<Text>licenseNumber Input:</Text>
<TextInput
style={styles.textInput}
textContentType="licenseNumber"
/>
<Text>licenseFileNumber Input:</Text>
<TextInput
style={styles.textInput}
textContentType="licenseFileNumber"
/>
<Text>engineNumber Input:</Text>
<TextInput
style={styles.textInput}
textContentType="engineNumber"
/>
<Text>licenseChassisNumber Input:</Text>
<TextInput
style={styles.textInput}
textContentType="licenseChassisNumber"
/>
</ScrollView>
)
}
const TextInputBankCardNumber = () => {
const [value, setValue] = useState('');
const handleChangeText = (text) => {
let ruleArr = [3, 7]
let newText = ""
let ruleIndex = 0
if (text) {
text = text.replace(/\D/g, '')
text = text.split(" ").join("")
text = text.replace(/\s+/g, "")
for (let i = 0; i < text.length; i++) {
if (i % ruleArr[ruleIndex] == 0 && i != 0) {
ruleIndex++
newText += " " + text[i]
} else {
newText += text[i]
}
}
}
setValue(newText);
}
return (
<View>
<TextInput
maxLength={21}
style={styles.textInput}
onChangeText={handleChangeText}
value={value}
/>
</View>
)
}
const styles = StyleSheet.create({
container: {
width: 80,
height: 80,
backgroundColor: 'red',
},
text: {
width: '100%',
height: 40,
},
textInputSmall: {
height: 20,
fontSize: 8,
color: 'black',
backgroundColor: 'rgb(245, 240, 211)',
borderRadius: 20,
},
textInput: {
height: 40,
fontSize: 16,
color: 'black',
backgroundColor: 'rgb(245, 240, 211)',
borderRadius: 40,
},
textInputBigger: {
height: 80,
fontSize: 16,
color: 'black',
backgroundColor: 'rgb(245, 240, 211)',
borderRadius: 20,
},
multilineInput: {
height: 80,
borderWidth: 1,
paddingVertical: 10,
paddingHorizontal: 15,
borderRadius: 5,
marginBottom: 20,
},
input: {
height: 40,
borderColor: 'gray',
borderWidth: 1,
marginBottom: 16,
paddingHorizontal: 8,
},
});
const FocusWithCursorTest = () => {
const ref = useRef<TextInput>(null);
const [value, setValue] = useState('12345');
const handleFocus = () => {
ref.current?.focus();
};
return (
<View>
<TextInput
ref={ref}
style={styles.textInput}
value={value}
onChangeText={setValue}
/>
<Button label="点击后focus" onPress={handleFocus} />
</View>
);
};
const SelectionTestComponent = () => {
return (
<View>
<Text>1. start less than end (选中文本): start=2, end=5</Text>
<TextInput
style={{
height: 40,
borderColor: 'gray',
borderWidth: 1,
marginBottom: 16,
paddingHorizontal: 8,
}}
defaultValue="点击输入框可以选择文字"
selection={{start: 2, end: 5}}
/>
<Text>2. start equals end (光标位置): start=5, end=5</Text>
<TextInput
style={{
height: 40,
borderColor: 'gray',
borderWidth: 1,
marginBottom: 16,
paddingHorizontal: 8,
}}
defaultValue="点击输入框可以选择文字"
selection={{start: 5, end: 5}}
/>
<Text>3. start greater than end (反向选择): start=8, end=3</Text>
<TextInput
style={{
height: 40,
borderColor: 'gray',
borderWidth: 1,
marginBottom: 16,
paddingHorizontal: 8,
}}
defaultValue="点击输入框可以选择文字"
selection={{start: 8, end: 3}}
/>
</View>
);
};
const OnKeyPressTest = () => {
const [value, setValue] = useState('');
const [data, setData] = useState('');
const handleKeyPress = (event: any) => {
setData(event.nativeEvent.key);
};
return (
<View>
<Text>onKeyPress 参数</Text>
<TextInput
style={{
height: 40,
borderColor: 'gray',
borderWidth: 1,
marginBottom: 16,
paddingHorizontal: 8,
}}
value={value}
onChangeText={setValue}
onKeyPress={handleKeyPress}
/>
<Text>keyPress内容为:{data}</Text>
</View>
);
};
const OnChangeTest = () => {
const [value, setValue] = useState('');
const [onChangeData, setOnChangeData] = useState('');
const handleChange = (text: string) => {
setValue(text);
};
const handleChangeEvent = (event: any) => {
setOnChangeData(event.nativeEvent.text);
};
return (
<View>
<Text>onChange 参数</Text>
<TextInput
style={{
height: 40,
borderColor: 'gray',
borderWidth: 1,
marginBottom: 16,
paddingHorizontal: 8,
}}
value={value}
onChangeText={handleChange}
onChange={handleChangeEvent}
/>
<Text>当前输入值: {value}</Text>
<Text>onChange内容为:{onChangeData}</Text>
</View>
);
};
const ClearButtonTest = () => {
const ref = useRef<TextInput>(null);
const [value, setValue] = useState('');
return (
<View>
<Text>点击按钮调用clear清空</Text>
<TextInput
style={styles.input}
placeholder="输入输入"
value={value}
onChangeText={setValue}
ref={ref}
/>
<Button label="清空" onPress={() => ref.current?.clear()} />
</View>
);
};
const ZipCodeTextInputMask = () => {
const [code, setCode] = useState('');
const [rawCode, setRawCode] = useState('');
const handleCodeChange = (text: string) => {
const raw = text.replace(/-/g, '');
setRawCode(raw);
let formatted = '';
for (let i = 0; i < raw.length; i++) {
if (i === 5) {
formatted += '-' + raw[i];
} else {
formatted += raw[i];
}
}
setCode(formatted);
};
return (
<View>
<TextInput
value={code}
onChangeText={handleCodeChange}
style={styles.input}
placeholder="输入邮政编码"
keyboardType="number-pad"
/>
<Text>格式化值: {code}</Text>
<Text>原始值: {rawCode}</Text>
<View style={{marginTop: 10, backgroundColor: '#f0f0f0', padding: 10}}>
<Text style={{fontWeight: 'bold'}}>测试用例:</Text>
<Text>输入 "123" → 期望: "123"</Text>
<Text>输入 "12345" → 期望: "12345"</Text>
<Text>输入 "123456" → 期望: "12345-6"</Text>
<Text>输入 "12345678" → 期望: "12345-678"</Text>
</View>
</View>
);
};