Skip to content

Commit f6cf22f

Browse files
crisbetommalerba
authored andcommitted
build: focus trap demo not working on IE/Edge (#18106)
The focus trap demo throws an error in IE and Edge, because it uses a component with Shadom DOM encapsulation. Adding an `ngIf` around it will break its functionality, because of the way the focus traps are initialized inside `ngAfterViewInit`. These changes switch the demo to use the focus trap directive and make it so we can test the demo against IE and Edge.
1 parent d1de6fa commit f6cf22f

File tree

3 files changed

+65
-83
lines changed

3 files changed

+65
-83
lines changed

src/dev-app/focus-trap/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ ng_module(
1313
],
1414
deps = [
1515
"//src/cdk/a11y",
16+
"//src/cdk/platform",
1617
"//src/material/button",
1718
"//src/material/card",
1819
"//src/material/dialog",

src/dev-app/focus-trap/focus-trap-demo.html

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
<button mat-raised-button (click)="toggleFocus(basicFocusTrap)">
66
{{basicFocusTrap && basicFocusTrap.enabled ? "Disable" : "Enable"}} FocusTrap
77
</button>
8-
<div class="demo-focus-trap-region" #basicDemoRegion
9-
[class.demo-focus-trap-enabled]="(basicFocusTrap && basicFocusTrap.enabled) || false">
8+
<div
9+
class="demo-focus-trap-region"
10+
cdkTrapFocus
11+
#basicFocusTrap="cdkTrapFocus"
12+
[class.demo-focus-trap-enabled]="basicFocusTrap && basicFocusTrap.enabled">
1013
<textarea class="demo-focus-trap-element" placeholder="One"></textarea>
1114
<textarea class="demo-focus-trap-element" placeholder="Two"></textarea>
1215
</div>
@@ -19,16 +22,20 @@
1922
<button mat-raised-button (click)="toggleFocus(nestedOuterFocusTrap)">
2023
{{nestedOuterFocusTrap && nestedOuterFocusTrap.enabled ? "Disable" : "Enable"}} outer FocusTrap
2124
</button>
22-
<div class="demo-focus-trap-region" #nestedOuterDemoRegion
23-
[class.demo-focus-trap-enabled]="(nestedOuterFocusTrap && nestedOuterFocusTrap.enabled) || false">
25+
<div class="demo-focus-trap-region"
26+
cdkTrapFocus
27+
#nestedOuterFocusTrap="cdkTrapFocus"
28+
[class.demo-focus-trap-enabled]="nestedOuterFocusTrap && nestedOuterFocusTrap.enabled">
2429
<textarea class="demo-focus-trap-element" placeholder="One"></textarea>
2530
<textarea class="demo-focus-trap-element" placeholder="Two"></textarea>
2631
<button mat-raised-button class="demo-focus-trap-element"
2732
(click)="toggleFocus(nestedInnerFocusTrap)">
2833
{{nestedInnerFocusTrap && nestedInnerFocusTrap.enabled ? "Disable" : "Enable"}} inner FocusTrap
2934
</button>
30-
<div class="demo-focus-trap-region" #nestedInnerDemoRegion
31-
[class.demo-focus-trap-enabled]="(nestedInnerFocusTrap && nestedInnerFocusTrap.enabled) || false">
35+
<div class="demo-focus-trap-region"
36+
cdkTrapFocus
37+
#nestedInnerFocusTrap="cdkTrapFocus"
38+
[class.demo-focus-trap-enabled]="nestedInnerFocusTrap && nestedInnerFocusTrap.enabled">
3239
<textarea class="demo-focus-trap-element" placeholder="Three"></textarea>
3340
<textarea class="demo-focus-trap-element" placeholder="Four"></textarea>
3441
</div>
@@ -42,8 +49,10 @@
4249
<button mat-raised-button (click)="toggleFocus(tabIndexFocusTrap)">
4350
{{tabIndexFocusTrap && tabIndexFocusTrap.enabled ? "Disable" : "Enable"}} FocusTrap
4451
</button>
45-
<div class="demo-focus-trap-region" #tabIndexDemoRegion
46-
[class.demo-focus-trap-enabled]="(tabIndexFocusTrap && tabIndexFocusTrap.enabled) || false">
52+
<div class="demo-focus-trap-region"
53+
cdkTrapFocus
54+
#tabIndexFocusTrap="cdkTrapFocus"
55+
[class.demo-focus-trap-enabled]="tabIndexFocusTrap && tabIndexFocusTrap.enabled">
4756
<textarea class="demo-focus-trap-element" tabindex="1"
4857
placeholder="I have tabindex 1"></textarea>
4958
<textarea class="demo-focus-trap-element" placeholder="One"></textarea>
@@ -56,21 +65,26 @@
5665

5766
<mat-card class="demo-mat-card">
5867
<mat-toolbar color="primary">Shadow DOMs</mat-toolbar>
59-
<mat-card-content class="demo-mat-card-content">
60-
<button mat-raised-button (click)="toggleFocus(shadowDomFocusTrap)">
61-
{{shadowDomFocusTrap && shadowDomFocusTrap.enabled ? "Disable" : "Enable"}} FocusTrap
62-
</button>
63-
<div class="demo-focus-trap-region" #shadowDomDemoRegion
64-
[class.demo-focus-trap-enabled]="(shadowDomFocusTrap && shadowDomFocusTrap.enabled) || false">
68+
<mat-card-content class="demo-mat-card-content" [ngSwitch]="_supportsShadowDom">
69+
<ng-container *ngSwitchCase="true">
70+
<button mat-raised-button (click)="toggleFocus(shadowDomFocusTrap)">
71+
{{shadowDomFocusTrap && shadowDomFocusTrap.enabled ? "Disable" : "Enable"}} FocusTrap
72+
</button>
73+
<div class="demo-focus-trap-region"
74+
cdkTrapFocus
75+
#shadowDomFocusTrap="cdkTrapFocus"
76+
[class.demo-focus-trap-enabled]="shadowDomFocusTrap && shadowDomFocusTrap.enabled">
77+
<shadow-dom-demo>
78+
<textarea placeholder="I am in a shadow DOM"></textarea>
79+
</shadow-dom-demo>
80+
<textarea class="demo-focus-trap-element" placeholder="One"></textarea>
81+
<textarea class="demo-focus-trap-element" placeholder="Two"></textarea>
82+
</div>
6583
<shadow-dom-demo>
66-
<textarea placeholder="I am in a shadow DOM"></textarea>
84+
<textarea class="demo-focus-trap-element" placeholder="I am in a shadow DOM"></textarea>
6785
</shadow-dom-demo>
68-
<textarea class="demo-focus-trap-element" placeholder="One"></textarea>
69-
<textarea class="demo-focus-trap-element" placeholder="Two"></textarea>
70-
</div>
71-
<shadow-dom-demo>
72-
<textarea class="demo-focus-trap-element" placeholder="I am in a shadow DOM"></textarea>
73-
</shadow-dom-demo>
86+
</ng-container>
87+
<ng-container *ngSwitchCase="false">Shadow DOM not supported</ng-container>
7488
</mat-card-content>
7589
</mat-card>
7690

@@ -80,8 +94,10 @@
8094
<button mat-raised-button (click)="toggleFocus(iframeFocusTrap)">
8195
{{iframeFocusTrap && iframeFocusTrap.enabled ? "Disable" : "Enable"}} FocusTrap
8296
</button>
83-
<div class="demo-focus-trap-region" #iframeDemoRegion
84-
[class.demo-focus-trap-enabled]="(iframeFocusTrap && iframeFocusTrap.enabled) || false">
97+
<div class="demo-focus-trap-region"
98+
cdkTrapFocus
99+
#iframeFocusTrap="cdkTrapFocus"
100+
[class.demo-focus-trap-enabled]="iframeFocusTrap && iframeFocusTrap.enabled">
85101
<iframe class="demo-focus-trap-element"
86102
srcdoc="<textarea placeholder='I am in an iframe'></textarea>">
87103
</iframe>
@@ -98,8 +114,10 @@
98114
<button mat-raised-button (click)="toggleFocus(dynamicFocusTrap)">
99115
{{dynamicFocusTrap && dynamicFocusTrap.enabled ? "Disable" : "Enable"}} FocusTrap
100116
</button>
101-
<div class="demo-focus-trap-region" #dynamicDemoRegion
102-
[class.demo-focus-trap-enabled]="(dynamicFocusTrap && dynamicFocusTrap.enabled) || false">
117+
<div class="demo-focus-trap-region"
118+
cdkTrapFocus
119+
#dynamicFocusTrap="cdkTrapFocus"
120+
[class.demo-focus-trap-enabled]="dynamicFocusTrap && dynamicFocusTrap.enabled">
103121
<textarea class="demo-focus-trap-element" placeholder="One"></textarea>
104122
<textarea class="demo-focus-trap-element" placeholder="Two"></textarea>
105123
<button mat-raised-button class="demo-focus-trap-element" (click)="addNewElement()">

src/dev-app/focus-trap/focus-trap-demo.ts

Lines changed: 21 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,18 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {FocusTrap, FocusTrapFactory} from '@angular/cdk/a11y';
9+
import {CdkTrapFocus} from '@angular/cdk/a11y';
1010
import {
1111
AfterViewInit,
1212
Component,
1313
ElementRef,
1414
ViewChild,
15-
ViewEncapsulation} from '@angular/core';
15+
ViewEncapsulation,
16+
ViewChildren,
17+
QueryList,
18+
} from '@angular/core';
1619
import {MatDialog} from '@angular/material/dialog';
20+
import {_supportsShadowDom} from '@angular/cdk/platform';
1721

1822
@Component({
1923
selector: 'shadow-dom-demo',
@@ -29,69 +33,28 @@ export class FocusTrapShadowDomDemo {}
2933
styleUrls: ['focus-trap-demo.css'],
3034
})
3135
export class FocusTrapDemo implements AfterViewInit {
36+
@ViewChild('newElements')
37+
private _newElements: ElementRef<HTMLElement>;
3238

33-
basicFocusTrap: FocusTrap;
34-
@ViewChild('basicDemoRegion', {static: false}) private readonly _basicDemoRegion!: ElementRef;
39+
@ViewChildren(CdkTrapFocus)
40+
private _focusTraps: QueryList<CdkTrapFocus>;
3541

36-
nestedOuterFocusTrap: FocusTrap;
37-
@ViewChild('nestedOuterDemoRegion', {static: false})
38-
private readonly _nestedOuterDemoRegion!: ElementRef;
39-
nestedInnerFocusTrap: FocusTrap;
40-
@ViewChild('nestedInnerDemoRegion', {static: false})
41-
private readonly _nestedInnerDemoRegion!: ElementRef;
42+
_supportsShadowDom = _supportsShadowDom();
4243

43-
tabIndexFocusTrap: FocusTrap;
44-
@ViewChild('tabIndexDemoRegion', {static: false})
45-
private readonly _tabIndexDemoRegion!: ElementRef;
46-
47-
shadowDomFocusTrap: FocusTrap;
48-
@ViewChild('shadowDomDemoRegion', {static: false})
49-
private readonly _shadowDomDemoRegion!: ElementRef;
50-
51-
iframeFocusTrap: FocusTrap;
52-
@ViewChild('iframeDemoRegion', {static: false})
53-
private readonly _iframeDemoRegion!: ElementRef;
54-
55-
dynamicFocusTrap: FocusTrap;
56-
@ViewChild('dynamicDemoRegion', {static: false})
57-
private readonly _dynamicDemoRegion!: ElementRef;
58-
@ViewChild('newElements', {static: false}) private readonly _newElements!: ElementRef;
59-
60-
constructor(
61-
public dialog: MatDialog,
62-
private _focusTrapFactory: FocusTrapFactory) {}
44+
constructor(public dialog: MatDialog) {}
6345

6446
ngAfterViewInit() {
65-
this.basicFocusTrap = this._focusTrapFactory.create(this._basicDemoRegion.nativeElement);
66-
this.basicFocusTrap.enabled = false;
67-
68-
this.nestedOuterFocusTrap = this._focusTrapFactory.create(
69-
this._nestedOuterDemoRegion.nativeElement);
70-
this.nestedOuterFocusTrap.enabled = false;
71-
72-
this.nestedInnerFocusTrap = this._focusTrapFactory.create(
73-
this._nestedInnerDemoRegion.nativeElement);
74-
this.nestedInnerFocusTrap.enabled = false;
75-
76-
this.tabIndexFocusTrap = this._focusTrapFactory.create(
77-
this._tabIndexDemoRegion.nativeElement);
78-
this.tabIndexFocusTrap.enabled = false;
79-
80-
this.shadowDomFocusTrap = this._focusTrapFactory.create(
81-
this._shadowDomDemoRegion.nativeElement);
82-
this.shadowDomFocusTrap.enabled = false;
83-
84-
this.iframeFocusTrap = this._focusTrapFactory.create(this._iframeDemoRegion.nativeElement);
85-
this.iframeFocusTrap.enabled = false;
86-
87-
this.dynamicFocusTrap = this._focusTrapFactory.create(this._dynamicDemoRegion.nativeElement);
88-
this.dynamicFocusTrap.enabled = false;
47+
// We want all the traps to be disabled by default, but doing so while using the value in
48+
// the view will result in "changed after checked" errors so we defer it to the next tick.
49+
setTimeout(() => {
50+
this._focusTraps.forEach(trap => trap.enabled = false);
51+
});
8952
}
9053

91-
toggleFocus(focusTrap: FocusTrap) {
92-
focusTrap.enabled = !focusTrap.enabled;
93-
if (focusTrap.enabled) {
94-
focusTrap.focusInitialElementWhenReady();
54+
toggleFocus(instance: CdkTrapFocus) {
55+
instance.enabled = !instance.enabled;
56+
if (instance.enabled) {
57+
instance.focusTrap.focusInitialElementWhenReady();
9558
}
9659
}
9760

0 commit comments

Comments
 (0)