From 5d77d6e30f7027fbfcfd0fddb6a9bd6f7d0accc5 Mon Sep 17 00:00:00 2001 From: Nicolas Gallagher Date: Sat, 31 Mar 2018 13:57:31 -0700 Subject: [PATCH] [fix] TextInput focus management Defer to the browser's native handling of 'blur' and 'focus'; directly update the internal state of TextInputState. Fixes an issue with preserving focus when the tab is backgrounded. Also ensure the TextInputState is correctly set when a component mounts. When 'autoFocus' is true, 'onFocus' can be called before the internal ref is set. Fix #880 --- .../src/exports/TextInput/__tests__/index-test.js | 13 +++++++++++++ .../react-native-web/src/exports/TextInput/index.js | 7 +++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/packages/react-native-web/src/exports/TextInput/__tests__/index-test.js b/packages/react-native-web/src/exports/TextInput/__tests__/index-test.js index 66cb9733..498dc0cf 100644 --- a/packages/react-native-web/src/exports/TextInput/__tests__/index-test.js +++ b/packages/react-native-web/src/exports/TextInput/__tests__/index-test.js @@ -1,6 +1,7 @@ /* eslint-env jasmine, jest */ import React from 'react'; +import ReactDOM from 'react-dom'; import TextInput from '..'; import { mount, shallow } from 'enzyme'; @@ -112,8 +113,14 @@ describe('components/TextInput', () => { test('prop "onBlur"', () => { const onBlur = jest.fn(); const input = findNativeInput(mount()); + const node = ReactDOM.findDOMNode(input.instance()); + + // more accurate blur simulation input.simulate('blur'); + node.blur(); + expect(onBlur).toHaveBeenCalledTimes(1); + expect(TextInput.State.currentlyFocusedField()).toBe(null); }); test('prop "onChange"', () => { @@ -135,8 +142,14 @@ describe('components/TextInput', () => { test('prop "onFocus"', () => { const onFocus = jest.fn(); const input = findNativeInput(mount()); + const node = ReactDOM.findDOMNode(input.instance()); + + // more accurate focus simulation input.simulate('focus'); + node.focus(); + expect(onFocus).toHaveBeenCalledTimes(1); + expect(TextInput.State.currentlyFocusedField()).toBe(node); }); describe('prop "onKeyPress"', () => { diff --git a/packages/react-native-web/src/exports/TextInput/index.js b/packages/react-native-web/src/exports/TextInput/index.js index df155c41..74ed83ab 100644 --- a/packages/react-native-web/src/exports/TextInput/index.js +++ b/packages/react-native-web/src/exports/TextInput/index.js @@ -161,6 +161,9 @@ class TextInput extends Component<*> { componentDidMount() { setSelection(this._node, this.props.selection); + if (document.activeElement === this._node) { + TextInputState._currentlyFocusedNode = this._node; + } } componentDidUpdate() { @@ -263,7 +266,7 @@ class TextInput extends Component<*> { _handleBlur = e => { const { onBlur } = this.props; - TextInputState.blurTextInput(this._node); + TextInputState._currentlyFocusedNode = null; if (onBlur) { onBlur(e); } @@ -283,7 +286,7 @@ class TextInput extends Component<*> { _handleFocus = e => { const { clearTextOnFocus, onFocus, selectTextOnFocus } = this.props; const node = this._node; - TextInputState.focusTextInput(this._node); + TextInputState._currentlyFocusedNode = this._node; if (onFocus) { onFocus(e); }