Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: set text position should not reset component text #49450

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -1026,7 +1026,7 @@ function useTextInputStateSynchronization_STATE({
mostRecentEventCount: number,
selection: ?Selection,
inputRef: React.RefObject<null | HostInstance>,
text: string,
text?: string,
viewCommands: ViewCommands,
}): {
setLastNativeText: string => void,
Expand Down Expand Up @@ -1107,7 +1107,7 @@ function useTextInputStateSynchronization_REFS({
mostRecentEventCount: number,
selection: ?Selection,
inputRef: React.RefObject<null | HostInstance>,
text: string,
text?: string,
viewCommands: ViewCommands,
}): {
setLastNativeText: string => void,
Expand Down Expand Up @@ -1321,7 +1321,7 @@ function InternalTextInput(props: TextInputProps): React.Node {
? props.value
: typeof props.defaultValue === 'string'
? props.defaultValue
: '';
: undefined;

const viewCommands =
AndroidTextInputCommands ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* @format
*/

const {create} = require('../../../../jest/renderer');
const {create, update} = require('../../../../jest/renderer');
const ReactNativeFeatureFlags = require('../../../../src/private/featureflags/ReactNativeFeatureFlags');
const ReactNative = require('../../../ReactNative/RendererProxy');
const {
Expand All @@ -20,8 +20,14 @@ const ReactTestRenderer = require('react-test-renderer');

jest.unmock('../TextInput');

[true, false].forEach(useRefsForTextInputState => {
describe(`TextInput tests (useRefsForTextInputState = ${useRefsForTextInputState}`, () => {
[
{useRefsForTextInputState: true, useTextChildren:true},
{useRefsForTextInputState: false, useTextChildren: true},
{useRefsForTextInputState: true, useTextChildren: false},
{useRefsForTextInputState: false, useTextChildren: false},
].forEach((testCase) => {
const {useRefsForTextInputState, useTextChildren} = testCase;
describe(`TextInput tests (useRefsForTextInputState = ${useRefsForTextInputState}) useTextChildren = ${useTextChildren}`, () => {
let input;
let inputRef;
let onChangeListener;
Expand All @@ -43,15 +49,15 @@ jest.unmock('../TextInput');
return (
<TextInput
ref={inputRef}
value={state.text}
value={useTextChildren ? undefined : state.text}
onChangeText={text => {
onChangeTextListener(text);
setState({text});
}}
onChange={event => {
onChangeListener(event);
}}
/>
>{useTextChildren ? state.text : undefined}</TextInput>
);
}
const renderTree = await create(<TextInputWrapper />);
Expand All @@ -75,12 +81,20 @@ jest.unmock('../TextInput');
);
});
it('calls onChange callbacks', () => {
expect(input.props.value).toBe(initialValue);
if (!useTextChildren) {
expect(input.props.value).toBe(initialValue);
} else {
expect(input.props.children).toBe(initialValue);
}
const message = 'This is a test message';
ReactTestRenderer.act(() => {
enter(input, message);
});
expect(input.props.value).toBe(message);
if (!useTextChildren) {
expect(input.props.value).toBe(message);
} else {
expect(input.props.children).toBe(message);
}
expect(onChangeTextListener).toHaveBeenCalledWith(message);
expect(onChangeListener).toHaveBeenCalledWith({
nativeEvent: {text: message},
Expand All @@ -90,7 +104,9 @@ jest.unmock('../TextInput');
async function createTextInput(extraProps) {
const textInputRef = React.createRef(null);
await create(
<TextInput ref={textInputRef} value="value1" {...extraProps} />,
<TextInput ref={textInputRef} value={useTextChildren ? undefined : 'value1'} {...extraProps}>
{useTextChildren ? 'value1' : undefined}
</TextInput>,
);
return textInputRef;
}
Expand Down Expand Up @@ -134,14 +150,30 @@ jest.unmock('../TextInput');
expect(TextInput.State.currentlyFocusedInput()).toBe(null);
});

it('change selection keeps content', async () => {
const defaultValue = 'value1';
// create content
let renderTree = await create(<TextInput value={useTextChildren ? undefined : defaultValue} position={{start: 1, end: 1}}>{useTextChildren ? defaultValue : undefined }</TextInput>);
input = renderTree.root.findByType(TextInput);
expect(useTextChildren ? input.children[0].props.children : input.props.value).toBe(defaultValue);
expect(input.props.position.start).toBe(1);
expect(input.props.position.end).toBe(1);

// update position
renderTree = await update(renderTree, <TextInput value={useTextChildren ? undefined : defaultValue} position={{start: 2, end: 2}}>{useTextChildren ? defaultValue : undefined }</TextInput>);
expect(useTextChildren ? input.children[0].props.children : input.props.value).toBe(defaultValue);
expect(input.props.position.start).toBe(2);
expect(input.props.position.end).toBe(2);
});

it('should unfocus when other TextInput is focused', async () => {
const textInputRe1 = React.createRef(null);
const textInputRe2 = React.createRef(null);

await create(
<>
<TextInput ref={textInputRe1} value="value1" />
<TextInput ref={textInputRe2} value="value2" />
<TextInput ref={textInputRe1} value={useTextChildren ? undefined : 'value1'} >{useTextChildren ? 'value1' : undefined}</TextInput>
<TextInput ref={textInputRe2} value={useTextChildren ? undefined : 'value2'} >{useTextChildren ? 'value2' : undefined}</TextInput>
</>,
);
ReactNative.findNodeHandle = jest.fn().mockImplementation(ref => {
Expand Down Expand Up @@ -210,7 +242,6 @@ jest.unmock('../TextInput');
rejectResponderTermination={true}
selection={null}
submitBehavior="blurAndSubmit"
text=""
textContentType="emailAddress"
underlineColorAndroid="transparent"
/>
Expand Down Expand Up @@ -255,7 +286,6 @@ jest.unmock('../TextInput');
rejectResponderTermination={true}
selection={null}
submitBehavior="blurAndSubmit"
text=""
underlineColorAndroid="transparent"
/>
`);
Expand Down Expand Up @@ -301,7 +331,6 @@ jest.unmock('../TextInput');
selection={null}
submitBehavior="blurAndSubmit"
testID="testID"
text=""
underlineColorAndroid="transparent"
/>
`);
Expand Down Expand Up @@ -432,7 +461,6 @@ jest.unmock('../TextInput');
role="main"
selection={null}
submitBehavior="blurAndSubmit"
text=""
underlineColorAndroid="transparent"
/>
`);
Expand Down Expand Up @@ -489,7 +517,6 @@ jest.unmock('../TextInput');
]
}
submitBehavior="blurAndSubmit"
text=""
underlineColorAndroid="transparent"
/>
`);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`TextInput tests (useRefsForTextInputState = false should render as expected: should deep render when mocked (please verify output manually) 1`] = `
exports[`TextInput tests (useRefsForTextInputState = false) useTextChildren = false should render as expected: should deep render when mocked (please verify output manually) 1`] = `
<RCTSinglelineTextInputView
accessible={true}
allowFontScaling={true}
Expand All @@ -23,12 +23,11 @@ exports[`TextInput tests (useRefsForTextInputState = false should render as expe
rejectResponderTermination={true}
selection={null}
submitBehavior="blurAndSubmit"
text=""
underlineColorAndroid="transparent"
/>
`;

exports[`TextInput tests (useRefsForTextInputState = false should render as expected: should deep render when not mocked (please verify output manually) 1`] = `
exports[`TextInput tests (useRefsForTextInputState = false) useTextChildren = false should render as expected: should deep render when not mocked (please verify output manually) 1`] = `
<RCTSinglelineTextInputView
accessible={true}
allowFontScaling={true}
Expand All @@ -51,12 +50,11 @@ exports[`TextInput tests (useRefsForTextInputState = false should render as expe
rejectResponderTermination={true}
selection={null}
submitBehavior="blurAndSubmit"
text=""
underlineColorAndroid="transparent"
/>
`;

exports[`TextInput tests (useRefsForTextInputState = true should render as expected: should deep render when mocked (please verify output manually) 1`] = `
exports[`TextInput tests (useRefsForTextInputState = false) useTextChildren = true should render as expected: should deep render when mocked (please verify output manually) 1`] = `
<RCTSinglelineTextInputView
accessible={true}
allowFontScaling={true}
Expand All @@ -79,12 +77,119 @@ exports[`TextInput tests (useRefsForTextInputState = true should render as expec
rejectResponderTermination={true}
selection={null}
submitBehavior="blurAndSubmit"
text=""
underlineColorAndroid="transparent"
/>
`;

exports[`TextInput tests (useRefsForTextInputState = true should render as expected: should deep render when not mocked (please verify output manually) 1`] = `
exports[`TextInput tests (useRefsForTextInputState = false) useTextChildren = true should render as expected: should deep render when not mocked (please verify output manually) 1`] = `
<RCTSinglelineTextInputView
accessible={true}
allowFontScaling={true}
focusable={true}
forwardedRef={null}
mostRecentEventCount={0}
onBlur={[Function]}
onChange={[Function]}
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}
submitBehavior="blurAndSubmit"
underlineColorAndroid="transparent"
/>
`;

exports[`TextInput tests (useRefsForTextInputState = true) useTextChildren = false should render as expected: should deep render when mocked (please verify output manually) 1`] = `
<RCTSinglelineTextInputView
accessible={true}
allowFontScaling={true}
focusable={true}
forwardedRef={null}
mostRecentEventCount={0}
onBlur={[Function]}
onChange={[Function]}
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}
submitBehavior="blurAndSubmit"
underlineColorAndroid="transparent"
/>
`;

exports[`TextInput tests (useRefsForTextInputState = true) useTextChildren = false should render as expected: should deep render when not mocked (please verify output manually) 1`] = `
<RCTSinglelineTextInputView
accessible={true}
allowFontScaling={true}
focusable={true}
forwardedRef={null}
mostRecentEventCount={0}
onBlur={[Function]}
onChange={[Function]}
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}
submitBehavior="blurAndSubmit"
underlineColorAndroid="transparent"
/>
`;

exports[`TextInput tests (useRefsForTextInputState = true) useTextChildren = true should render as expected: should deep render when mocked (please verify output manually) 1`] = `
<RCTSinglelineTextInputView
accessible={true}
allowFontScaling={true}
focusable={true}
forwardedRef={null}
mostRecentEventCount={0}
onBlur={[Function]}
onChange={[Function]}
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}
submitBehavior="blurAndSubmit"
underlineColorAndroid="transparent"
/>
`;

exports[`TextInput tests (useRefsForTextInputState = true) useTextChildren = true should render as expected: should deep render when not mocked (please verify output manually) 1`] = `
<RCTSinglelineTextInputView
accessible={true}
allowFontScaling={true}
Expand All @@ -107,7 +212,6 @@ exports[`TextInput tests (useRefsForTextInputState = true should render as expec
rejectResponderTermination={true}
selection={null}
submitBehavior="blurAndSubmit"
text=""
underlineColorAndroid="transparent"
/>
`;