diff --git a/src/cdk/drag-drop/drop-list-ref.ts b/src/cdk/drag-drop/drop-list-ref.ts index e258bf44fff9..93e161f4b642 100644 --- a/src/cdk/drag-drop/drop-list-ref.ts +++ b/src/cdk/drag-drop/drop-list-ref.ts @@ -10,7 +10,7 @@ import {ElementRef, NgZone} from '@angular/core'; import {Direction} from '@angular/cdk/bidi'; import {coerceElement} from '@angular/cdk/coercion'; import {ViewportRuler} from '@angular/cdk/scrolling'; -import {_supportsShadowDom} from '@angular/cdk/platform'; +import {_getShadowRoot} from '@angular/cdk/platform'; import {Subject, Subscription, interval, animationFrameScheduler} from 'rxjs'; import {takeUntil} from 'rxjs/operators'; import {moveItemInArray} from './drag-utils'; @@ -907,7 +907,8 @@ export class DropListRef { */ private _getShadowRoot(): DocumentOrShadowRoot { if (!this._cachedShadowRoot) { - this._cachedShadowRoot = getShadowRoot(coerceElement(this.element)) || this._document; + const shadowRoot = _getShadowRoot(coerceElement(this.element)) as ShadowRoot | null; + this._cachedShadowRoot = shadowRoot || this._document; } return this._cachedShadowRoot; @@ -1102,16 +1103,3 @@ function getElementScrollDirections(element: HTMLElement, clientRect: ClientRect return [verticalScrollDirection, horizontalScrollDirection]; } - -/** Gets the shadow root of an element, if any. */ -function getShadowRoot(element: HTMLElement): DocumentOrShadowRoot | null { - if (_supportsShadowDom()) { - const rootNode = element.getRootNode ? element.getRootNode() : null; - - if (rootNode instanceof ShadowRoot) { - return rootNode; - } - } - - return null; -} diff --git a/src/cdk/platform/features/shadow-dom.ts b/src/cdk/platform/features/shadow-dom.ts index 12d3ed54f969..b387fc2f5730 100644 --- a/src/cdk/platform/features/shadow-dom.ts +++ b/src/cdk/platform/features/shadow-dom.ts @@ -17,3 +17,16 @@ export function _supportsShadowDom(): boolean { return shadowDomIsSupported; } + +/** Gets the shadow root of an element, if supported and the element is inside the Shadow DOM. */ +export function _getShadowRoot(element: HTMLElement): Node | null { + if (_supportsShadowDom()) { + const rootNode = element.getRootNode ? element.getRootNode() : null; + + if (rootNode instanceof ShadowRoot) { + return rootNode; + } + } + + return null; +} diff --git a/src/material/autocomplete/autocomplete-trigger.ts b/src/material/autocomplete/autocomplete-trigger.ts index 5818092a79ed..a7cb88cb2d13 100644 --- a/src/material/autocomplete/autocomplete-trigger.ts +++ b/src/material/autocomplete/autocomplete-trigger.ts @@ -17,7 +17,7 @@ import { ScrollStrategy, ConnectedPosition, } from '@angular/cdk/overlay'; -import {_supportsShadowDom} from '@angular/cdk/platform'; +import {_getShadowRoot} from '@angular/cdk/platform'; import {TemplatePortal} from '@angular/cdk/portal'; import {ViewportRuler} from '@angular/cdk/scrolling'; import {DOCUMENT} from '@angular/common'; @@ -230,14 +230,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, AfterViewIn window.addEventListener('blur', this._windowBlurHandler); }); - if (_supportsShadowDom()) { - const element = this._element.nativeElement; - const rootNode = element.getRootNode ? element.getRootNode() : null; - - // We need to take the `ShadowRoot` off of `window`, because the built-in types are - // incorrect. See https://github.com/Microsoft/TypeScript/issues/27929. - this._isInsideShadowRoot = rootNode instanceof (window as any).ShadowRoot; - } + this._isInsideShadowRoot = !!_getShadowRoot(this._element.nativeElement); } } diff --git a/src/material/progress-spinner/progress-spinner.spec.ts b/src/material/progress-spinner/progress-spinner.spec.ts index 687f4b098316..e8b6323bb792 100644 --- a/src/material/progress-spinner/progress-spinner.spec.ts +++ b/src/material/progress-spinner/progress-spinner.spec.ts @@ -1,9 +1,8 @@ import {TestBed, async, inject} from '@angular/core/testing'; import {Component, ViewEncapsulation, ViewChild, ElementRef} from '@angular/core'; import {By} from '@angular/platform-browser'; -import {Platform} from '@angular/cdk/platform'; +import {Platform, _getShadowRoot, _supportsShadowDom} from '@angular/cdk/platform'; import {CommonModule} from '@angular/common'; -import {_getShadowRoot} from './progress-spinner'; import { MatProgressSpinnerModule, MatProgressSpinner, @@ -11,8 +10,6 @@ import { } from './index'; describe('MatProgressSpinner', () => { - const supportsShadowDom = typeof document.createElement('div').attachShadow !== 'undefined'; - beforeEach(async(() => { TestBed.configureTestingModule({ imports: [MatProgressSpinnerModule, CommonModule], @@ -357,7 +354,7 @@ describe('MatProgressSpinner', () => { it('should add the indeterminate animation style tag to the Shadow root', () => { // The test is only relevant in browsers that support Shadow DOM. - if (!supportsShadowDom) { + if (!_supportsShadowDom()) { return; } @@ -366,7 +363,7 @@ describe('MatProgressSpinner', () => { fixture.detectChanges(); const spinner = fixture.debugElement.query(By.css('mat-progress-spinner'))!.nativeElement; - const shadowRoot = _getShadowRoot(spinner, document) as HTMLElement; + const shadowRoot = _getShadowRoot(spinner) as HTMLElement; expect(shadowRoot.querySelector('style[mat-spinner-animation="27"]')).toBeTruthy(); @@ -378,7 +375,7 @@ describe('MatProgressSpinner', () => { it('should not duplicate style tags inside the Shadow root', () => { // The test is only relevant in browsers that support Shadow DOM. - if (!supportsShadowDom) { + if (!_supportsShadowDom()) { return; } @@ -387,7 +384,7 @@ describe('MatProgressSpinner', () => { fixture.detectChanges(); const spinner = fixture.debugElement.query(By.css('mat-progress-spinner'))!.nativeElement; - const shadowRoot = _getShadowRoot(spinner, document) as HTMLElement; + const shadowRoot = _getShadowRoot(spinner) as HTMLElement; expect(shadowRoot.querySelectorAll('style[mat-spinner-animation="39"]').length).toBe(1); @@ -409,7 +406,7 @@ describe('MatProgressSpinner', () => { it('should add the indeterminate animation style tag to the Shadow root if the element is ' + 'inside an ngIf', () => { // The test is only relevant in browsers that support Shadow DOM. - if (!supportsShadowDom) { + if (!_supportsShadowDom()) { return; } @@ -418,7 +415,7 @@ describe('MatProgressSpinner', () => { fixture.detectChanges(); const spinner = fixture.componentInstance.spinner.nativeElement; - const shadowRoot = _getShadowRoot(spinner, document) as HTMLElement; + const shadowRoot = _getShadowRoot(spinner) as HTMLElement; expect(shadowRoot.querySelector('style[mat-spinner-animation="27"]')).toBeTruthy(); diff --git a/src/material/progress-spinner/progress-spinner.ts b/src/material/progress-spinner/progress-spinner.ts index 2bff6ed06786..61fe82c58299 100644 --- a/src/material/progress-spinner/progress-spinner.ts +++ b/src/material/progress-spinner/progress-spinner.ts @@ -7,7 +7,7 @@ */ import {coerceNumberProperty, NumberInput} from '@angular/cdk/coercion'; -import {Platform} from '@angular/cdk/platform'; +import {Platform, _getShadowRoot} from '@angular/cdk/platform'; import {DOCUMENT} from '@angular/common'; import { ChangeDetectionStrategy, @@ -218,7 +218,7 @@ export class MatProgressSpinner extends _MatProgressSpinnerMixinBase implements // Note that we need to look up the root node in ngOnInit, rather than the constructor, because // Angular seems to create the element outside the shadow root and then moves it inside, if the // node is inside an `ngIf` and a ShadowDom-encapsulated component. - this._styleRoot = _getShadowRoot(element, this._document) || this._document.head; + this._styleRoot = _getShadowRoot(element) || this._document.head; this._attachStyleNode(); // On IE and Edge, we can't animate the `stroke-dashoffset` @@ -333,26 +333,3 @@ export class MatSpinner extends MatProgressSpinner { this.mode = 'indeterminate'; } } - - -/** Gets the shadow root of an element, if supported and the element is inside the Shadow DOM. */ -export function _getShadowRoot(element: HTMLElement, _document: Document): Node | null { - // TODO(crisbeto): see whether we should move this into the CDK - // feature detection utilities once #15616 gets merged in. - if (typeof window !== 'undefined') { - const head = _document.head; - - // Check whether the browser supports Shadow DOM. - if (head && ((head as any).createShadowRoot || head.attachShadow)) { - const rootNode = element.getRootNode ? element.getRootNode() : null; - - // We need to take the `ShadowRoot` off of `window`, because the built-in types are - // incorrect. See https://github.com/Microsoft/TypeScript/issues/27929. - if (rootNode instanceof (window as any).ShadowRoot) { - return rootNode; - } - } - } - - return null; -} diff --git a/tools/public_api_guard/cdk/platform.d.ts b/tools/public_api_guard/cdk/platform.d.ts index 2b42a5ace81d..d5926f5f4345 100644 --- a/tools/public_api_guard/cdk/platform.d.ts +++ b/tools/public_api_guard/cdk/platform.d.ts @@ -1,3 +1,5 @@ +export declare function _getShadowRoot(element: HTMLElement): Node | null; + export declare function _supportsShadowDom(): boolean; export declare function getRtlScrollAxisType(): RtlScrollAxisType;