Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 39 additions & 12 deletions Libraries/Components/TextInput/TextInput.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,6 +19,7 @@ import type {TextInputType} from './TextInput.flow';

import usePressability from '../../Pressability/usePressability';
import flattenStyle from '../../StyleSheet/flattenStyle';
import processStyles from '../../StyleSheet/processStyles';
import StyleSheet, {
type ColorValue,
type TextStyleProp,
Expand DownExpand Up@@ -1072,6 +1073,15 @@ const emptyFunctionThatReturnsTrue = () => true;
*
*/
function InternalTextInput(props: Props): React.Node {
const {
'aria-busy': ariaBusy,
'aria-checked': ariaChecked,
'aria-disabled': ariaDisabled,
'aria-expanded': ariaExpanded,
'aria-selected': ariaSelected,
accessibilityState,
} = props;

const inputRef = useRef<null | React.ElementRef<HostComponent<mixed>>>(null);

// Android sends a "onTextChanged" event followed by a "onSelectionChanged" event, for
Expand DownExpand Up@@ -1392,25 +1402,38 @@ function InternalTextInput(props: Props): React.Node {
// so omitting onBlur and onFocus pressability handlers here.
const {onBlur, onFocus, ...eventHandlers} = usePressability(config) || {};

const _accessibilityState = {
busy: props['aria-busy'] ?? props.accessibilityState?.busy,
checked: props['aria-checked'] ?? props.accessibilityState?.checked,
disabled: props['aria-disabled'] ?? props.accessibilityState?.disabled,
expanded: props['aria-expanded'] ?? props.accessibilityState?.expanded,
selected: props['aria-selected'] ?? props.accessibilityState?.selected,
};
let _accessibilityState;
if (
accessibilityState != null ||
ariaBusy != null ||
ariaChecked != null ||
ariaDisabled != null ||
ariaExpanded != null ||
ariaSelected != null
) {
_accessibilityState = {
busy: ariaBusy ?? accessibilityState?.busy,
checked: ariaChecked ?? accessibilityState?.checked,
disabled: ariaDisabled ?? accessibilityState?.disabled,
expanded: ariaExpanded ?? accessibilityState?.expanded,
selected: ariaSelected ?? accessibilityState?.selected,
};
}

if (Platform.OS === 'ios') {
const RCTTextInputView =
props.multiline === true
? RCTMultilineTextInputView
: RCTSinglelineTextInputView;

const style =
let style =
props.multiline === true
? StyleSheet.flatten([styles.multilineInput, props.style])
? [styles.multilineInput, props.style]
: props.style;

style = flattenStyle(style);
Comment thread
mayank-96 marked this conversation as resolved.
style = processStyles(style);

const useOnChangeSync =
(props.unstable_onChangeSync || props.unstable_onChangeTextSync) &&
!(props.onChange || props.onChangeText);
Expand All@@ -1420,8 +1443,8 @@ function InternalTextInput(props: Props): React.Node {
ref={_setNativeRef}
{...props}
{...eventHandlers}
accessible={accessible}
accessibilityState={_accessibilityState}
accessible={accessible}
submitBehavior={submitBehavior}
caretHidden={caretHidden}
dataDetectorTypes={props.dataDetectorTypes}
Expand All@@ -1442,7 +1465,9 @@ function InternalTextInput(props: Props): React.Node {
/>
);
} else if (Platform.OS === 'android') {
const style = [props.style];
let style = flattenStyle(props.style);
style = processStyles(style);

const autoCapitalize = props.autoCapitalize || 'sentences';
const _accessibilityLabelledBy =
props?.['aria-labelledby'] ?? props?.accessibilityLabelledBy;
Expand DownExpand Up@@ -1470,9 +1495,9 @@ function InternalTextInput(props: Props): React.Node {
ref={_setNativeRef}
{...props}
{...eventHandlers}
accessible={accessible}
accessibilityState={_accessibilityState}
accessibilityLabelledBy={_accessibilityLabelledBy}
accessible={accessible}
autoCapitalize={autoCapitalize}
submitBehavior={submitBehavior}
caretHidden={caretHidden}
Expand DownExpand Up@@ -1650,6 +1675,8 @@ const ExportedForwardRef: React.AbstractComponent<
);
});

ExportedForwardRef.displayName = 'TextInput';

/**
* Switch to `deprecated-react-native-prop-types` for compatibility with future
* releases. This is deprecated and will be removed in the future.
Expand Down
275 changes: 275 additions & 0 deletions Libraries/Components/TextInput/__tests__/TextInput-test.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -179,3 +179,278 @@ describe('TextInput tests', () => {
);
});
});

describe('TextInput', () => {
it('default render', () => {
const instance = ReactTestRenderer.create(<TextInput />);

expect(instance.toJSON()).toMatchInlineSnapshot(`
<RCTSinglelineTextInputView
accessible={true}
allowFontScaling={true}
focusable={true}
forwardedRef={null}
mostRecentEventCount={0}
onBlur={[Function]}
onChange={[Function]}
onChangeSync={null}
onClick={[Function]}
onFocus={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onScroll={[Function]}
onSelectionChange={[Function]}
onSelectionChangeShouldSetResponder={[Function]}
onStartShouldSetResponder={[Function]}
rejectResponderTermination={true}
selection={null}
style={Object {}}
submitBehavior="blurAndSubmit"
text=""
underlineColorAndroid="transparent"
/>
`);
});

it('has displayName', () => {
expect(TextInput.displayName).toEqual('TextInput');
});
});

describe('TextInput compat with web', () => {
it('renders core props', () => {
const props = {
id: 'id',
tabIndex: 0,
testID: 'testID',
};

const instance = ReactTestRenderer.create(<TextInput {...props} />);

expect(instance.toJSON()).toMatchInlineSnapshot(`
<RCTSinglelineTextInputView
accessible={true}
allowFontScaling={true}
focusable={true}
forwardedRef={null}
id="id"
mostRecentEventCount={0}
onBlur={[Function]}
onChange={[Function]}
onChangeSync={null}
onClick={[Function]}
onFocus={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onScroll={[Function]}
onSelectionChange={[Function]}
onSelectionChangeShouldSetResponder={[Function]}
onStartShouldSetResponder={[Function]}
rejectResponderTermination={true}
selection={null}
style={Object {}}
submitBehavior="blurAndSubmit"
tabIndex={0}
testID="testID"
text=""
underlineColorAndroid="transparent"
/>
`);
});

it('renders "aria-*" props', () => {
const props = {
'aria-activedescendant': 'activedescendant',
'aria-atomic': true,
'aria-autocomplete': 'list',
'aria-busy': true,
'aria-checked': true,
'aria-columncount': 5,
'aria-columnindex': 3,
'aria-columnspan': 2,
'aria-controls': 'controls',
'aria-current': 'current',
'aria-describedby': 'describedby',
'aria-details': 'details',
'aria-disabled': true,
'aria-errormessage': 'errormessage',
'aria-expanded': true,
'aria-flowto': 'flowto',
'aria-haspopup': true,
'aria-hidden': true,
'aria-invalid': true,
'aria-keyshortcuts': 'Cmd+S',
'aria-label': 'label',
'aria-labelledby': 'labelledby',
'aria-level': 3,
'aria-live': 'polite',
'aria-modal': true,
'aria-multiline': true,
'aria-multiselectable': true,
'aria-orientation': 'portrait',
'aria-owns': 'owns',
'aria-placeholder': 'placeholder',
'aria-posinset': 5,
'aria-pressed': true,
'aria-readonly': true,
'aria-required': true,
role: 'main',
'aria-roledescription': 'roledescription',
'aria-rowcount': 5,
'aria-rowindex': 3,
'aria-rowspan': 3,
'aria-selected': true,
'aria-setsize': 5,
'aria-sort': 'ascending',
'aria-valuemax': 5,
'aria-valuemin': 0,
'aria-valuenow': 3,
'aria-valuetext': '3',
};

const instance = ReactTestRenderer.create(<TextInput {...props} />);

expect(instance.toJSON()).toMatchInlineSnapshot(`
<RCTSinglelineTextInputView
accessibilityState={
Object {
"busy": true,
"checked": true,
"disabled": true,
"expanded": true,
"selected": true,
}
}
accessible={true}
allowFontScaling={true}
aria-activedescendant="activedescendant"
aria-atomic={true}
aria-autocomplete="list"
aria-busy={true}
aria-checked={true}
aria-columncount={5}
aria-columnindex={3}
aria-columnspan={2}
aria-controls="controls"
aria-current="current"
aria-describedby="describedby"
aria-details="details"
aria-disabled={true}
aria-errormessage="errormessage"
aria-expanded={true}
aria-flowto="flowto"
aria-haspopup={true}
aria-hidden={true}
aria-invalid={true}
aria-keyshortcuts="Cmd+S"
aria-label="label"
aria-labelledby="labelledby"
aria-level={3}
aria-live="polite"
aria-modal={true}
aria-multiline={true}
aria-multiselectable={true}
aria-orientation="portrait"
aria-owns="owns"
aria-placeholder="placeholder"
aria-posinset={5}
aria-pressed={true}
aria-readonly={true}
aria-required={true}
aria-roledescription="roledescription"
aria-rowcount={5}
aria-rowindex={3}
aria-rowspan={3}
aria-selected={true}
aria-setsize={5}
aria-sort="ascending"
aria-valuemax={5}
aria-valuemin={0}
aria-valuenow={3}
aria-valuetext="3"
focusable={true}
forwardedRef={null}
mostRecentEventCount={0}
onBlur={[Function]}
onChange={[Function]}
onChangeSync={null}
onClick={[Function]}
onFocus={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onScroll={[Function]}
onSelectionChange={[Function]}
onSelectionChangeShouldSetResponder={[Function]}
onStartShouldSetResponder={[Function]}
rejectResponderTermination={true}
role="main"
selection={null}
style={Object {}}
submitBehavior="blurAndSubmit"
text=""
underlineColorAndroid="transparent"
/>
`);
});

it('renders styles', () => {
const style = {
display: 'flex',
flex: 1,
backgroundColor: 'white',
marginInlineStart: 10,
userSelect: 'none',
verticalAlign: 'middle',
};

const instance = ReactTestRenderer.create(<TextInput style={style} />);

expect(instance.toJSON()).toMatchInlineSnapshot(`
<RCTSinglelineTextInputView
accessible={true}
allowFontScaling={true}
focusable={true}
forwardedRef={null}
mostRecentEventCount={0}
onBlur={[Function]}
onChange={[Function]}
onChangeSync={null}
onClick={[Function]}
onFocus={[Function]}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onScroll={[Function]}
onSelectionChange={[Function]}
onSelectionChangeShouldSetResponder={[Function]}
onStartShouldSetResponder={[Function]}
rejectResponderTermination={true}
selection={null}
style={
Object {
"backgroundColor": "white",
"display": "flex",
"flex": 1,
"marginStart": 10,
"textAlignVertical": "center",
"userSelect": "none",
}
}
submitBehavior="blurAndSubmit"
text=""
underlineColorAndroid="transparent"
/>
`);
});
});
Loading