From ed03b00d93546d8984307527528a79ce1784b2e5 Mon Sep 17 00:00:00 2001 From: Nicolas Gallagher Date: Fri, 26 Apr 2019 10:30:46 -0700 Subject: [PATCH 1/2] Adjust FocusScope props and make el.focus() calls safe --- packages/react-events/src/FocusScope.js | 37 ++++++++++++------- .../src/__tests__/FocusScope-test.internal.js | 8 ++-- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/packages/react-events/src/FocusScope.js b/packages/react-events/src/FocusScope.js index ba1747cfdcfaa..c31e11d8f8e52 100644 --- a/packages/react-events/src/FocusScope.js +++ b/packages/react-events/src/FocusScope.js @@ -15,8 +15,8 @@ import {REACT_EVENT_COMPONENT_TYPE} from 'shared/ReactSymbols'; type FocusScopeProps = { autoFocus: Boolean, + contain: Boolean, restoreFocus: Boolean, - trap: Boolean, }; type FocusScopeState = { @@ -27,14 +27,21 @@ type FocusScopeState = { const targetEventTypes = [{name: 'keydown', passive: false}]; const rootEventTypes = [{name: 'focus', passive: true, capture: true}]; -function focusFirstChildEventTarget( +function focusElement(element: ?HTMLElement) { + if (element != null) { + try { + element.focus(); + } catch (err) {} + } +} + +function getFirstFocusableElement( context: ReactResponderContext, state: FocusScopeState, -): void { +): ?HTMLElement { const elements = context.getFocusableElementsInScope(); if (elements.length > 0) { - const firstElement = elements[0]; - firstElement.focus(); + return elements[0]; } } @@ -78,7 +85,7 @@ const FocusScopeResponder = { if (shiftKey) { if (position === 0) { - if (props.trap) { + if (props.contain) { nextElement = elements[lastPosition]; } else { // Out of bounds @@ -90,7 +97,7 @@ const FocusScopeResponder = { } } else { if (position === lastPosition) { - if (props.trap) { + if (props.contain) { nextElement = elements[0]; } else { // Out of bounds @@ -107,7 +114,7 @@ const FocusScopeResponder = { if (!context.isTargetWithinEventResponderScope(nextElement)) { context.releaseOwnership(); } - nextElement.focus(); + focusElement(nextElement); state.currentFocusedNode = nextElement; ((nativeEvent: any): KeyboardEvent).preventDefault(); } @@ -122,14 +129,15 @@ const FocusScopeResponder = { ) { const {target} = event; - // Handle global trapping - if (props.trap) { + // Handle global focus containment + if (props.contain) { if (!context.isTargetWithinEventComponent(target)) { const currentFocusedNode = state.currentFocusedNode; if (currentFocusedNode !== null) { - currentFocusedNode.focus(); + focusElement(currentFocusedNode); } else if (props.autoFocus) { - focusFirstChildEventTarget(context, state); + const firstElement = getFirstFocusableElement(context, state); + focusElement(firstElement); } } } @@ -143,7 +151,8 @@ const FocusScopeResponder = { state.nodeToRestore = context.getActiveDocument().activeElement; } if (props.autoFocus) { - focusFirstChildEventTarget(context, state); + const firstElement = getFirstFocusableElement(context, state); + focusElement(firstElement); } }, onUnmount( @@ -156,7 +165,7 @@ const FocusScopeResponder = { state.nodeToRestore !== null && context.hasOwnership() ) { - state.nodeToRestore.focus(); + focusElement(state.nodeToRestore); } }, onOwnershipChange( diff --git a/packages/react-events/src/__tests__/FocusScope-test.internal.js b/packages/react-events/src/__tests__/FocusScope-test.internal.js index 673246269ab07..2194422c3b0e7 100644 --- a/packages/react-events/src/__tests__/FocusScope-test.internal.js +++ b/packages/react-events/src/__tests__/FocusScope-test.internal.js @@ -85,7 +85,7 @@ describe('FocusScope event responder', () => { expect(document.activeElement).toBe(divRef.current); }); - it('should work as expected with autofocus and trapping', () => { + it('should work as expected with autoFocus and contain', () => { const inputRef = React.createRef(); const input2Ref = React.createRef(); const buttonRef = React.createRef(); @@ -93,7 +93,7 @@ describe('FocusScope event responder', () => { const SimpleFocusScope = () => (
- +