From 8a5460ce80e69c11a007121d4278d55642f6b10e Mon Sep 17 00:00:00 2001 From: Vojtech Novak Date: Tue, 22 Mar 2022 11:43:57 -0700 Subject: [PATCH] fix attempting to focus disabled textinputs (#30695) Summary: when we call `focus()` upon a TextInput ref which has prop `editable=false` it marks the textinput as focused in `TextInputState` even though the focus is rejected by textinput itself because it is not editable. then, when you change `editable` prop to `true` and call `focus` again, [this condition](https://github.com/facebook/react-native/blob/e912c462eb0b7166ca5947bb5a3ee20761d910b6/Libraries/Components/TextInput/TextInputState.js#L46) or rather [this one](https://github.com/facebook/react-native/blob/1b2b2198e1b2383523b4655dc8c220d251b057d6/Libraries/Components/TextInput/TextInputState.js#L89) will evaluate to `false` and focus will not happen even though it can and should happen. see also https://github.com/facebook/react-native/blob/0.64-stable/Libraries/Renderer/implementations/ReactNativeRenderer-dev.js#L3895 ## Changelog [General] [Fixed] - `focus()` on TextInput to respect its `editable` state Pull Request resolved: https://github.com/facebook/react-native/pull/30695 Test Plan: Create a `TextInput` with prop `editable=false` and call `ref.current.focus()` upon its ref. TextInput should not be marked as focused in `TextInputState`. Reviewed By: yungsters Differential Revision: D34357913 Pulled By: lunaleaps fbshipit-source-id: 9a2fb819bbb05ef213c9b5d739dec583ae0a3e6f --- .../Components/TextInput/TextInputState.js | 12 +++++++++-- .../TextInput/__tests__/TextInput-test.js | 21 +++++++++++++++++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/Libraries/Components/TextInput/TextInputState.js b/Libraries/Components/TextInput/TextInputState.js index fe9a1c89da..87cb3ccf78 100644 --- a/Libraries/Components/TextInput/TextInputState.js +++ b/Libraries/Components/TextInput/TextInputState.js @@ -73,7 +73,7 @@ function blurField(textFieldID: ?number) { /** * @param {number} TextInputID id of the text field to focus * Focuses the specified text field - * noop if the text field was already focused + * noop if the text field was already focused or if the field is not editable */ function focusTextInput(textField: ?ComponentRef) { if (typeof textField === 'number') { @@ -86,7 +86,15 @@ function focusTextInput(textField: ?ComponentRef) { return; } - if (currentlyFocusedInputRef !== textField && textField != null) { + if (textField != null) { + const fieldCanBeFocused = + currentlyFocusedInputRef !== textField && + // $FlowFixMe - `currentProps` is missing in `NativeMethods` + textField.currentProps?.editable !== false; + + if (!fieldCanBeFocused) { + return; + } focusInput(textField); if (Platform.OS === 'ios') { // This isn't necessarily a single line text input diff --git a/Libraries/Components/TextInput/__tests__/TextInput-test.js b/Libraries/Components/TextInput/__tests__/TextInput-test.js index 9704a1a2c5..f63c82bebe 100644 --- a/Libraries/Components/TextInput/__tests__/TextInput-test.js +++ b/Libraries/Components/TextInput/__tests__/TextInput-test.js @@ -79,9 +79,26 @@ describe('TextInput tests', () => { }); }); - it('should have support being focused and unfocused', () => { + function createTextInput(extraProps) { const textInputRef = React.createRef(null); - ReactTestRenderer.create(); + ReactTestRenderer.create( + , + ); + return textInputRef; + } + + it('focus() should not do anything if the TextInput is not editable', () => { + const textInputRef = createTextInput({editable: false}); + // currentProps is the property actually containing props at runtime + textInputRef.current.currentProps = textInputRef.current.props; + expect(textInputRef.current.isFocused()).toBe(false); + + TextInput.State.focusTextInput(textInputRef.current); + expect(textInputRef.current.isFocused()).toBe(false); + }); + + it('should have support for being focused and blurred', () => { + const textInputRef = createTextInput(); expect(textInputRef.current.isFocused()).toBe(false); ReactNative.findNodeHandle = jest.fn().mockImplementation(ref => {