From 27347b58b47e57b264dacc70901cf34266043f1a Mon Sep 17 00:00:00 2001 From: Kristiyan Kostadinov Date: Wed, 7 Nov 2018 18:23:35 +0000 Subject: [PATCH] fix(focus-trap): warn if initial element is not focusable (#13960) Currently if the consumer sets the `cdkFocusInitial` group on a non-focusable element, nothing will happen. These changes add a warning if the app is running in dev mode. Relates to #13953. --- src/cdk/a11y/focus-trap/focus-trap.spec.ts | 23 ++++++++++++++++++++++ src/cdk/a11y/focus-trap/focus-trap.ts | 7 +++++++ 2 files changed, 30 insertions(+) diff --git a/src/cdk/a11y/focus-trap/focus-trap.spec.ts b/src/cdk/a11y/focus-trap/focus-trap.spec.ts index c82dca07b9b..5d91fbc4a6f 100644 --- a/src/cdk/a11y/focus-trap/focus-trap.spec.ts +++ b/src/cdk/a11y/focus-trap/focus-trap.spec.ts @@ -16,6 +16,7 @@ describe('FocusTrap', () => { FocusTrapWithSvg, FocusTrapWithoutFocusableElements, FocusTrapWithAutoCapture, + FocusTrapUnfocusableTarget, ], }); @@ -138,6 +139,18 @@ describe('FocusTrap', () => { focusTrapInstance.focusLastTabbableElement(); expect(document.activeElement!.id).toBe('last'); }); + + it('should warn if the initial focus target is not focusable', () => { + const alternateFixture = TestBed.createComponent(FocusTrapUnfocusableTarget); + alternateFixture.detectChanges(); + focusTrapInstance = fixture.componentInstance.focusTrapDirective.focusTrap; + + spyOn(console, 'warn'); + focusTrapInstance.focusInitialElement(); + + expect(console.warn).toHaveBeenCalled(); + }); + }); describe('special cases', () => { @@ -235,6 +248,16 @@ class FocusTrapTargets { @ViewChild(CdkTrapFocus) focusTrapDirective: CdkTrapFocus; } +@Component({ + template: ` +
+
+
+ ` +}) +class FocusTrapUnfocusableTarget { + @ViewChild(CdkTrapFocus) focusTrapDirective: CdkTrapFocus; +} @Component({ template: ` diff --git a/src/cdk/a11y/focus-trap/focus-trap.ts b/src/cdk/a11y/focus-trap/focus-trap.ts index 08d068928e5..faed08f6413 100644 --- a/src/cdk/a11y/focus-trap/focus-trap.ts +++ b/src/cdk/a11y/focus-trap/focus-trap.ts @@ -18,6 +18,7 @@ import { NgZone, OnDestroy, DoCheck, + isDevMode, } from '@angular/core'; import {take} from 'rxjs/operators'; import {InteractivityChecker} from '../interactivity-checker/interactivity-checker'; @@ -189,6 +190,12 @@ export class FocusTrap { `will be removed in 8.0.0`, redirectToElement); } + // Warn the consumer if the element they've pointed to + // isn't focusable, when not in production mode. + if (isDevMode() && !this._checker.isFocusable(redirectToElement)) { + console.warn(`Element matching '[cdkFocusInitial]' is not focusable.`, redirectToElement); + } + redirectToElement.focus(); return true; }