@@ -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 navFocusRegion : 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 . navFocusRegion ?. deactivate ( )
239253 }
240254
241255 trackPage ( page : string ) {
@@ -359,6 +373,40 @@ class App extends Component<AppProps, AppState> {
359373 }
360374 }
361375
376+ getFocusRegionConfig ( ) {
377+ return {
378+ shouldContainFocus : true ,
379+ shouldCloseOnEscape : true ,
380+ shouldReturnFocus : false ,
381+ onDismiss : this . handleMenuClose
382+ }
383+ }
384+
385+ handleNavigationFocusRegion ( ) {
386+ if ( this . state . showMenu ) {
387+ if ( this . navFocusRegion ) {
388+ this . navFocusRegion . activate ( )
389+ } else if ( this . navRef . current ) {
390+ this . navFocusRegion = new FocusRegion (
391+ this . navRef . current ,
392+ this . getFocusRegionConfig ( )
393+ )
394+ this . navFocusRegion . activate ( )
395+ }
396+ }
397+ }
398+
399+ handleFocusChange = ( e : FocusEvent ) => {
400+ const isFocusInsideNav =
401+ e . target && this . navRef . current ?. contains ( e . target as Node )
402+ if ( ! isFocusInsideNav && this . navFocusRegion ) {
403+ this . navFocusRegion . deactivate ( )
404+ this . navFocusRegion = null
405+ } else if ( isFocusInsideNav && ! this . navFocusRegion ) {
406+ this . handleNavigationFocusRegion ( )
407+ }
408+ }
409+
362410 renderThemeSelect ( ) {
363411 const themeKeys = Object . keys ( this . state . docsData ! . themes )
364412 const smallScreen = this . state . layout === 'small'
@@ -712,7 +760,9 @@ class App extends Component<AppProps, AppState> {
712760 )
713761
714762 return layout !== 'small' ? (
715- < nav css = { this . props . styles ?. inlineNavigation } > { navContent } </ nav >
763+ < nav css = { this . props . styles ?. inlineNavigation } ref = { this . navRef } >
764+ { navContent }
765+ </ nav >
716766 ) : (
717767 < Tray label = "Navigation" open = { showMenu } onDismiss = { this . handleMenuClose } >
718768 { navContent }
0 commit comments