@@ -15,8 +15,8 @@ import {REACT_EVENT_COMPONENT_TYPE} from 'shared/ReactSymbols';
1515
1616type FocusScopeProps = {
1717 autoFocus : Boolean ,
18+ contain : Boolean ,
1819 restoreFocus : Boolean ,
19- trap : Boolean ,
2020} ;
2121
2222type FocusScopeState = {
@@ -27,14 +27,21 @@ type FocusScopeState = {
2727const targetEventTypes = [ { name : 'keydown' , passive : false } ] ;
2828const rootEventTypes = [ { name : 'focus' , passive : true , capture : true } ] ;
2929
30- function focusFirstChildEventTarget (
30+ function focusElement ( element : ?HTMLElement ) {
31+ if ( element != null ) {
32+ try {
33+ element . focus ( ) ;
34+ } catch ( err ) { }
35+ }
36+ }
37+
38+ function getFirstFocusableElement (
3139 context : ReactResponderContext ,
3240 state : FocusScopeState ,
33- ) : void {
41+ ) : ? HTMLElement {
3442 const elements = context . getFocusableElementsInScope ( ) ;
3543 if ( elements . length > 0 ) {
36- const firstElement = elements [ 0 ] ;
37- firstElement . focus ( ) ;
44+ return elements [ 0 ] ;
3845 }
3946}
4047
@@ -78,7 +85,7 @@ const FocusScopeResponder = {
7885
7986 if ( shiftKey ) {
8087 if ( position === 0 ) {
81- if ( props . trap ) {
88+ if ( props . contain ) {
8289 nextElement = elements [ lastPosition ] ;
8390 } else {
8491 // Out of bounds
@@ -90,7 +97,7 @@ const FocusScopeResponder = {
9097 }
9198 } else {
9299 if ( position === lastPosition ) {
93- if ( props . trap ) {
100+ if ( props . contain ) {
94101 nextElement = elements [ 0 ] ;
95102 } else {
96103 // Out of bounds
@@ -107,7 +114,7 @@ const FocusScopeResponder = {
107114 if ( ! context . isTargetWithinEventResponderScope ( nextElement ) ) {
108115 context . releaseOwnership ( ) ;
109116 }
110- nextElement . focus ( ) ;
117+ focusElement ( nextElement ) ;
111118 state . currentFocusedNode = nextElement ;
112119 ( ( nativeEvent : any ) : KeyboardEvent ) . preventDefault ( ) ;
113120 }
@@ -122,14 +129,15 @@ const FocusScopeResponder = {
122129 ) {
123130 const { target} = event ;
124131
125- // Handle global trapping
126- if ( props . trap ) {
132+ // Handle global focus containment
133+ if ( props . contain ) {
127134 if ( ! context . isTargetWithinEventComponent ( target ) ) {
128135 const currentFocusedNode = state . currentFocusedNode ;
129136 if ( currentFocusedNode !== null ) {
130- currentFocusedNode . focus ( ) ;
137+ focusElement ( currentFocusedNode ) ;
131138 } else if ( props . autoFocus ) {
132- focusFirstChildEventTarget ( context , state ) ;
139+ const firstElement = getFirstFocusableElement ( context , state ) ;
140+ focusElement ( firstElement ) ;
133141 }
134142 }
135143 }
@@ -143,7 +151,8 @@ const FocusScopeResponder = {
143151 state . nodeToRestore = context . getActiveDocument ( ) . activeElement ;
144152 }
145153 if ( props . autoFocus ) {
146- focusFirstChildEventTarget ( context , state ) ;
154+ const firstElement = getFirstFocusableElement ( context , state ) ;
155+ focusElement ( firstElement ) ;
147156 }
148157 } ,
149158 onUnmount (
@@ -156,7 +165,7 @@ const FocusScopeResponder = {
156165 state . nodeToRestore !== null &&
157166 context . hasOwnership ( )
158167 ) {
159- state . nodeToRestore . focus ( ) ;
168+ focusElement ( state . nodeToRestore ) ;
160169 }
161170 } ,
162171 onOwnershipChange (
0 commit comments