@@ -76,6 +76,7 @@ import type {
7676} from '../../buildScripts/DataTypes.mjs'
7777import { logError } from '@instructure/console'
7878import type { Spacing } from '@instructure/emotion'
79+ import { FocusRegion } from '@instructure/ui-a11y-utils'
7980
8081type AppContextType = {
8182 themeKey : keyof MainDocsData [ 'themes' ]
@@ -94,6 +95,9 @@ class App extends Component<AppProps, AppState> {
9495 static propTypes = propTypes
9596 static allowedProps = allowedProps
9697
98+ private navRef = createRef < HTMLElement > ( )
99+ private focusRegion : FocusRegion | null = null
100+
97101 static defaultProps = {
98102 trayWidth : 300
99103 }
@@ -222,10 +226,18 @@ class App extends Component<AppProps, AppState> {
222226 } )
223227 } )
224228 . catch ( errorHandler )
229+ document . addEventListener ( 'focusin' , this . handleFocusChange )
225230 }
226231
227- componentDidUpdate ( ) {
232+ componentDidUpdate ( _prevProps : AppProps , prevState : AppState ) {
228233 this . props . makeStyles ?.( )
234+
235+ if (
236+ prevState . showMenu !== this . state . showMenu ||
237+ prevState . layout !== this . state . layout
238+ ) {
239+ this . handleNavigationFocusRegion ( )
240+ }
229241 }
230242
231243 componentWillUnmount ( ) {
@@ -236,6 +248,8 @@ class App extends Component<AppProps, AppState> {
236248 if ( this . _mediaQueryListener ) {
237249 this . _mediaQueryListener . remove ( )
238250 }
251+ document . removeEventListener ( 'focusin' , this . handleFocusChange )
252+ this . focusRegion ?. deactivate ( )
239253 }
240254
241255 trackPage ( page : string ) {
@@ -359,6 +373,32 @@ class App extends Component<AppProps, AppState> {
359373 }
360374 }
361375
376+ handleNavigationFocusRegion ( ) {
377+ if ( this . focusRegion ) {
378+ this . focusRegion . deactivate ( )
379+ }
380+
381+ if ( this . navRef . current ) {
382+ this . focusRegion = new FocusRegion ( this . navRef . current , {
383+ shouldContainFocus : true ,
384+ shouldCloseOnEscape : true ,
385+ shouldReturnFocus : false ,
386+ onDismiss : this . handleMenuClose
387+ } )
388+ this . focusRegion . activate ( )
389+ }
390+ }
391+
392+ handleFocusChange = ( e : FocusEvent ) => {
393+ const isFocusInsideNav = this . navRef . current ?. contains ( e . target as Node )
394+ if ( isFocusInsideNav ) {
395+ this . handleNavigationFocusRegion ( )
396+ } else {
397+ this . focusRegion ?. deactivate ( )
398+ this . focusRegion = null
399+ }
400+ }
401+
362402 renderThemeSelect ( ) {
363403 const themeKeys = Object . keys ( this . state . docsData ! . themes )
364404 const smallScreen = this . state . layout === 'small'
@@ -712,7 +752,9 @@ class App extends Component<AppProps, AppState> {
712752 )
713753
714754 return layout !== 'small' ? (
715- < nav css = { this . props . styles ?. inlineNavigation } > { navContent } </ nav >
755+ < nav css = { this . props . styles ?. inlineNavigation } ref = { this . navRef } >
756+ { navContent }
757+ </ nav >
716758 ) : (
717759 < Tray label = "Navigation" open = { showMenu } onDismiss = { this . handleMenuClose } >
718760 { navContent }
0 commit comments