|
1 | 1 | import {Platform, _supportsShadowDom} from '../../platform'; |
2 | 2 | import {CdkPortalOutlet, PortalModule, TemplatePortal} from '../../portal'; |
3 | 3 | import { |
| 4 | + AfterViewInit, |
4 | 5 | Component, |
5 | 6 | TemplateRef, |
6 | 7 | ViewChild, |
@@ -185,6 +186,24 @@ describe('FocusTrap', () => { |
185 | 186 | expect(() => focusTrapInstance.focusFirstTabbableElement()).not.toThrow(); |
186 | 187 | expect(() => focusTrapInstance.focusLastTabbableElement()).not.toThrow(); |
187 | 188 | }); |
| 189 | + |
| 190 | + it('should find tabbable elements in shadow DOM', () => { |
| 191 | + if (!_supportsShadowDom()) { |
| 192 | + return; |
| 193 | + } |
| 194 | + |
| 195 | + const fixture = TestBed.createComponent(FocusTrapWithShadowDom); |
| 196 | + fixture.detectChanges(); |
| 197 | + const focusTrapInstance = fixture.componentInstance.focusTrapDirective.focusTrap; |
| 198 | + |
| 199 | + // The shadow button should be found as the first tabbable element |
| 200 | + expect(focusTrapInstance.focusFirstTabbableElement()).toBe(true); |
| 201 | + expect(getActiveElement().textContent?.trim()).toBe('Shadow Button'); |
| 202 | + |
| 203 | + // The shadow button should also be found as the last tabbable element |
| 204 | + expect(focusTrapInstance.focusLastTabbableElement()).toBe(true); |
| 205 | + expect(getActiveElement().textContent?.trim()).toBe('Shadow Button'); |
| 206 | + }); |
188 | 207 | }); |
189 | 208 |
|
190 | 209 | describe('with autoCapture', () => { |
@@ -448,3 +467,25 @@ class FocusTrapInsidePortal { |
448 | 467 | @ViewChild('template') template: TemplateRef<any>; |
449 | 468 | @ViewChild(CdkPortalOutlet) portalOutlet: CdkPortalOutlet; |
450 | 469 | } |
| 470 | + |
| 471 | +@Component({ |
| 472 | + template: ` |
| 473 | + <div cdkTrapFocus> |
| 474 | + <div #shadowHost></div> |
| 475 | + </div> |
| 476 | + `, |
| 477 | + imports: [A11yModule], |
| 478 | +}) |
| 479 | +class FocusTrapWithShadowDom implements AfterViewInit { |
| 480 | + @ViewChild(CdkTrapFocus) focusTrapDirective: CdkTrapFocus; |
| 481 | + @ViewChild('shadowHost', {static: true}) shadowHost: any; |
| 482 | + |
| 483 | + ngAfterViewInit() { |
| 484 | + if (_supportsShadowDom()) { |
| 485 | + const shadowRoot = this.shadowHost.nativeElement.attachShadow({mode: 'open'}); |
| 486 | + const shadowButton = document.createElement('button'); |
| 487 | + shadowButton.textContent = 'Shadow Button'; |
| 488 | + shadowRoot.appendChild(shadowButton); |
| 489 | + } |
| 490 | + } |
| 491 | +} |
0 commit comments