Skip to content

Commit 430d397

Browse files
committed
refactor(platform): consolidate and expose shadow dom detection utilities
Over time we had accumulated the same utility for finding the shadow root of an element in multiple places. These changes move it into a common place under `cdk/platform`. I also decided to remove the underscore from `_supportsShadowDom` so it's officially a public API. It was already exposed under `cdk/platform` so there was nothing stopping people from using it, but the underscore looked weird. The API should be stable enough, since we've had it for more than a year in multiple components and we haven't had issues with it.
1 parent 3d35180 commit 430d397

File tree

6 files changed

+28
-55
lines changed

6 files changed

+28
-55
lines changed

src/cdk/drag-drop/directives/drag.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {
2424
import {TestBed, ComponentFixture, fakeAsync, flush, tick} from '@angular/core/testing';
2525
import {DOCUMENT} from '@angular/common';
2626
import {ViewportRuler, ScrollingModule} from '@angular/cdk/scrolling';
27-
import {_supportsShadowDom} from '@angular/cdk/platform';
27+
import {supportsShadowDom} from '@angular/cdk/platform';
2828
import {of as observableOf} from 'rxjs';
2929

3030
import {DragDropModule} from '../drag-drop-module';
@@ -4578,7 +4578,7 @@ describe('CdkDrag', () => {
45784578

45794579
it('should be able to drop into a new container inside the Shadow DOM', fakeAsync(() => {
45804580
// This test is only relevant for Shadow DOM-supporting browsers.
4581-
if (!_supportsShadowDom()) {
4581+
if (!supportsShadowDom()) {
45824582
return;
45834583
}
45844584

@@ -4612,7 +4612,7 @@ describe('CdkDrag', () => {
46124612
it('should be able to drop into a new container inside the Shadow DOM and ngIf',
46134613
fakeAsync(() => {
46144614
// This test is only relevant for Shadow DOM-supporting browsers.
4615-
if (!_supportsShadowDom()) {
4615+
if (!supportsShadowDom()) {
46164616
return;
46174617
}
46184618

src/cdk/drag-drop/drop-list-ref.ts

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {ElementRef, NgZone} from '@angular/core';
1010
import {Direction} from '@angular/cdk/bidi';
1111
import {coerceElement} from '@angular/cdk/coercion';
1212
import {ViewportRuler} from '@angular/cdk/scrolling';
13-
import {_supportsShadowDom} from '@angular/cdk/platform';
13+
import {supportsShadowDom, getShadowRoot} from '@angular/cdk/platform';
1414
import {Subject, Subscription, interval, animationFrameScheduler} from 'rxjs';
1515
import {takeUntil} from 'rxjs/operators';
1616
import {moveItemInArray} from './drag-utils';
@@ -907,7 +907,8 @@ export class DropListRef<T = any> {
907907
*/
908908
private _getShadowRoot(): DocumentOrShadowRoot {
909909
if (!this._cachedShadowRoot) {
910-
this._cachedShadowRoot = getShadowRoot(coerceElement(this.element)) || this._document;
910+
const shadowRoot = getShadowRoot(coerceElement(this.element)) as ShadowRoot | null;
911+
this._cachedShadowRoot = shadowRoot || this._document;
911912
}
912913

913914
return this._cachedShadowRoot;
@@ -1102,16 +1103,3 @@ function getElementScrollDirections(element: HTMLElement, clientRect: ClientRect
11021103

11031104
return [verticalScrollDirection, horizontalScrollDirection];
11041105
}
1105-
1106-
/** Gets the shadow root of an element, if any. */
1107-
function getShadowRoot(element: HTMLElement): DocumentOrShadowRoot | null {
1108-
if (_supportsShadowDom()) {
1109-
const rootNode = element.getRootNode ? element.getRootNode() : null;
1110-
1111-
if (rootNode instanceof ShadowRoot) {
1112-
return rootNode;
1113-
}
1114-
}
1115-
1116-
return null;
1117-
}

src/cdk/platform/features/shadow-dom.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,24 @@
99
let shadowDomIsSupported: boolean;
1010

1111
/** Checks whether the user's browser support Shadow DOM. */
12-
export function _supportsShadowDom(): boolean {
12+
export function supportsShadowDom(): boolean {
1313
if (shadowDomIsSupported == null) {
1414
const head = typeof document !== 'undefined' ? document.head : null;
1515
shadowDomIsSupported = !!(head && ((head as any).createShadowRoot || head.attachShadow));
1616
}
1717

1818
return shadowDomIsSupported;
1919
}
20+
21+
/** Gets the shadow root of an element, if supported and the element is inside the Shadow DOM. */
22+
export function getShadowRoot(element: HTMLElement): Node | null {
23+
if (supportsShadowDom()) {
24+
const rootNode = element.getRootNode ? element.getRootNode() : null;
25+
26+
if (rootNode instanceof ShadowRoot) {
27+
return rootNode;
28+
}
29+
}
30+
31+
return null;
32+
}

src/material/autocomplete/autocomplete-trigger.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
ScrollStrategy,
1818
ConnectedPosition,
1919
} from '@angular/cdk/overlay';
20-
import {_supportsShadowDom} from '@angular/cdk/platform';
20+
import {getShadowRoot} from '@angular/cdk/platform';
2121
import {TemplatePortal} from '@angular/cdk/portal';
2222
import {ViewportRuler} from '@angular/cdk/scrolling';
2323
import {DOCUMENT} from '@angular/common';
@@ -230,14 +230,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, AfterViewIn
230230
window.addEventListener('blur', this._windowBlurHandler);
231231
});
232232

233-
if (_supportsShadowDom()) {
234-
const element = this._element.nativeElement;
235-
const rootNode = element.getRootNode ? element.getRootNode() : null;
236-
237-
// We need to take the `ShadowRoot` off of `window`, because the built-in types are
238-
// incorrect. See https://github.com/Microsoft/TypeScript/issues/27929.
239-
this._isInsideShadowRoot = rootNode instanceof (window as any).ShadowRoot;
240-
}
233+
this._isInsideShadowRoot = !!getShadowRoot(this._element.nativeElement);
241234
}
242235
}
243236

src/material/progress-spinner/progress-spinner.ts

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
*/
88

99
import {coerceNumberProperty, NumberInput} from '@angular/cdk/coercion';
10-
import {Platform} from '@angular/cdk/platform';
10+
import {Platform, getShadowRoot} from '@angular/cdk/platform';
1111
import {DOCUMENT} from '@angular/common';
1212
import {
1313
ChangeDetectionStrategy,
@@ -218,7 +218,7 @@ export class MatProgressSpinner extends _MatProgressSpinnerMixinBase implements
218218
// Note that we need to look up the root node in ngOnInit, rather than the constructor, because
219219
// Angular seems to create the element outside the shadow root and then moves it inside, if the
220220
// node is inside an `ngIf` and a ShadowDom-encapsulated component.
221-
this._styleRoot = _getShadowRoot(element, this._document) || this._document.head;
221+
this._styleRoot = getShadowRoot(element) || this._document.head;
222222
this._attachStyleNode();
223223

224224
// On IE and Edge, we can't animate the `stroke-dashoffset`
@@ -333,26 +333,3 @@ export class MatSpinner extends MatProgressSpinner {
333333
this.mode = 'indeterminate';
334334
}
335335
}
336-
337-
338-
/** Gets the shadow root of an element, if supported and the element is inside the Shadow DOM. */
339-
export function _getShadowRoot(element: HTMLElement, _document: Document): Node | null {
340-
// TODO(crisbeto): see whether we should move this into the CDK
341-
// feature detection utilities once #15616 gets merged in.
342-
if (typeof window !== 'undefined') {
343-
const head = _document.head;
344-
345-
// Check whether the browser supports Shadow DOM.
346-
if (head && ((head as any).createShadowRoot || head.attachShadow)) {
347-
const rootNode = element.getRootNode ? element.getRootNode() : null;
348-
349-
// We need to take the `ShadowRoot` off of `window`, because the built-in types are
350-
// incorrect. See https://github.com/Microsoft/TypeScript/issues/27929.
351-
if (rootNode instanceof (window as any).ShadowRoot) {
352-
return rootNode;
353-
}
354-
}
355-
}
356-
357-
return null;
358-
}

tools/public_api_guard/cdk/platform.d.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
export declare function _supportsShadowDom(): boolean;
2-
31
export declare function getRtlScrollAxisType(): RtlScrollAxisType;
42

3+
export declare function getShadowRoot(element: HTMLElement): Node | null;
4+
55
export declare function getSupportedInputTypes(): Set<string>;
66

77
export declare function normalizePassiveListenerOptions(options: AddEventListenerOptions): AddEventListenerOptions | boolean;
@@ -35,3 +35,5 @@ export declare const enum RtlScrollAxisType {
3535
export declare function supportsPassiveEventListeners(): boolean;
3636

3737
export declare function supportsScrollBehavior(): boolean;
38+
39+
export declare function supportsShadowDom(): boolean;

0 commit comments

Comments
 (0)