diff --git a/packages/components/src/higher-order/with-focus-outside/index.js b/packages/components/src/higher-order/with-focus-outside/index.js index 960f5abe12aa6a..5f178b4a34df28 100644 --- a/packages/components/src/higher-order/with-focus-outside/index.js +++ b/packages/components/src/higher-order/with-focus-outside/index.js @@ -79,6 +79,14 @@ export default createHigherOrderComponent( } this.blurCheckTimeout = setTimeout( () => { + // If document is not focused then focus should remain + // inside the wrapped component and therefore we cancel + // this blur event thereby leaving focus in place. + // https://developer.mozilla.org/en-US/docs/Web/API/Document/hasFocus. + if ( ! document.hasFocus() ) { + event.preventDefault(); + return; + } if ( 'function' === typeof this.node.handleFocusOutside ) { this.node.handleFocusOutside( event ); } diff --git a/packages/components/src/higher-order/with-focus-outside/test/index.js b/packages/components/src/higher-order/with-focus-outside/test/index.js index dd39f910981c7e..ac47e3a57290a2 100644 --- a/packages/components/src/higher-order/with-focus-outside/test/index.js +++ b/packages/components/src/higher-order/with-focus-outside/test/index.js @@ -17,6 +17,8 @@ import ReactDOM from 'react-dom'; let wrapper, onFocusOutside; describe( 'withFocusOutside', () => { + let origHasFocus; + const EnhancedComponent = withFocusOutside( class extends Component { handleFocusOutside() { @@ -51,12 +53,21 @@ describe( 'withFocusOutside', () => { }; beforeEach( () => { + // Mock document.hasFocus() to always be true for testing + // note: we overide this for some tests. + origHasFocus = document.hasFocus; + document.hasFocus = () => true; + onFocusOutside = jest.fn(); wrapper = TestUtils.renderIntoDocument( getTestComponent( EnhancedComponent, { onFocusOutside } ) ); } ); + afterEach( () => { + document.hasFocus = origHasFocus; + } ); + it( 'should not call handler if focus shifts to element within component', () => { simulateEvent( 'focus' ); simulateEvent( 'blur' ); @@ -91,6 +102,19 @@ describe( 'withFocusOutside', () => { expect( onFocusOutside ).toHaveBeenCalled(); } ); + it( 'should not call handler if focus shifts outside the component when the document does not have focus', () => { + // Force document.hasFocus() to return false to simulate the window/document losing focus + // See https://developer.mozilla.org/en-US/docs/Web/API/Document/hasFocus. + document.hasFocus = () => false; + + simulateEvent( 'focus' ); + simulateEvent( 'blur' ); + + jest.runAllTimers(); + + expect( onFocusOutside ).not.toHaveBeenCalled(); + } ); + it( 'should cancel check when unmounting while queued', () => { simulateEvent( 'focus' ); simulateEvent( 'input' );