diff --git a/src/cdk/a11y/focus-monitor/focus-monitor.ts b/src/cdk/a11y/focus-monitor/focus-monitor.ts index fcd35ee9813..58f207e2a47 100644 --- a/src/cdk/a11y/focus-monitor/focus-monitor.ts +++ b/src/cdk/a11y/focus-monitor/focus-monitor.ts @@ -19,6 +19,7 @@ import { SkipSelf, } from '@angular/core'; import {Observable, of as observableOf, Subject, Subscription} from 'rxjs'; +import {coerceElement} from '@angular/cdk/coercion'; // This is the value used by AngularJS Material. Through trial and error (on iPhone 6S) they found @@ -158,7 +159,7 @@ export class FocusMonitor implements OnDestroy { return observableOf(null); } - const nativeElement = this._getNativeElement(element); + const nativeElement = coerceElement(element); // Check if we're already monitoring this element. if (this._elementInfo.has(nativeElement)) { @@ -206,7 +207,7 @@ export class FocusMonitor implements OnDestroy { stopMonitoring(element: ElementRef): void; stopMonitoring(element: HTMLElement | ElementRef): void { - const nativeElement = this._getNativeElement(element); + const nativeElement = coerceElement(element); const elementInfo = this._elementInfo.get(nativeElement); if (elementInfo) { @@ -239,7 +240,7 @@ export class FocusMonitor implements OnDestroy { origin: FocusOrigin, options?: FocusOptions): void { - const nativeElement = this._getNativeElement(element); + const nativeElement = coerceElement(element); this._setOriginForCurrentEventQueue(origin); @@ -418,10 +419,6 @@ export class FocusMonitor implements OnDestroy { clearTimeout(this._originTimeoutId); } } - - private _getNativeElement(element: HTMLElement | ElementRef): HTMLElement { - return element instanceof ElementRef ? element.nativeElement : element; - } } diff --git a/src/cdk/coercion/BUILD.bazel b/src/cdk/coercion/BUILD.bazel index 1aad554a66e..505db4de784 100644 --- a/src/cdk/coercion/BUILD.bazel +++ b/src/cdk/coercion/BUILD.bazel @@ -1,24 +1,28 @@ package(default_visibility=["//visibility:public"]) -load("//tools:defaults.bzl", "ts_library", "jasmine_node_test", "markdown_to_html") +load("//tools:defaults.bzl", "ts_library", "ts_web_test_suite", "markdown_to_html") ts_library( name = "coercion", srcs = glob(["**/*.ts"], exclude=["**/*.spec.ts"]), module_name = "@angular/cdk/coercion", + deps = [ + "@angular//packages/core", + ] ) ts_library( name = "coercion_test_sources", srcs = glob(["**/*.spec.ts"]), deps = [ + "@angular//packages/core", "@matdeps//@types/jasmine", ":coercion" ], testonly = 1, ) -jasmine_node_test( +ts_web_test_suite( name = "unit_tests", deps = [":coercion_test_sources"], ) diff --git a/src/cdk/coercion/element.spec.ts b/src/cdk/coercion/element.spec.ts new file mode 100644 index 00000000000..4e10de4b673 --- /dev/null +++ b/src/cdk/coercion/element.spec.ts @@ -0,0 +1,13 @@ +import {ElementRef} from '@angular/core'; +import {coerceElement} from './element'; + +describe('coerceElement', () => { + it('should coerce an ElementRef into an element', () => { + const ref = new ElementRef(document.body); + expect(coerceElement(ref)).toBe(document.body); + }); + + it('should return the element, if a native element is passed in', () => { + expect(coerceElement(document.body)).toBe(document.body); + }); +}); diff --git a/src/cdk/coercion/element.ts b/src/cdk/coercion/element.ts new file mode 100644 index 00000000000..92b1307bb06 --- /dev/null +++ b/src/cdk/coercion/element.ts @@ -0,0 +1,17 @@ +/** + * @license + * Copyright Google LLC All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {ElementRef} from '@angular/core'; + +/** + * Coerces an ElementRef or an Element into an element. + * Useful for APIs that can accept either a ref or the native element itself. + */ +export function coerceElement(elementOrRef: ElementRef | T): T { + return elementOrRef instanceof ElementRef ? elementOrRef.nativeElement : elementOrRef; +} diff --git a/src/cdk/coercion/public-api.ts b/src/cdk/coercion/public-api.ts index 6563d2bfab0..a33a133e56b 100644 --- a/src/cdk/coercion/public-api.ts +++ b/src/cdk/coercion/public-api.ts @@ -10,3 +10,4 @@ export * from './boolean-property'; export * from './number-property'; export * from './array'; export * from './css-pixel-value'; +export * from './element'; diff --git a/src/cdk/observers/observe-content.ts b/src/cdk/observers/observe-content.ts index f9d91c44fe2..7312eb6c556 100644 --- a/src/cdk/observers/observe-content.ts +++ b/src/cdk/observers/observe-content.ts @@ -6,7 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ -import {coerceBooleanProperty, coerceNumberProperty} from '@angular/cdk/coercion'; +import {coerceBooleanProperty, coerceNumberProperty, coerceElement} from '@angular/cdk/coercion'; import { AfterContentInit, Directive, @@ -63,7 +63,7 @@ export class ContentObserver implements OnDestroy { observe(element: ElementRef): Observable; observe(elementOrRef: Element | ElementRef): Observable { - const element = elementOrRef instanceof ElementRef ? elementOrRef.nativeElement : elementOrRef; + const element = coerceElement(elementOrRef); return Observable.create((observer: Observer) => { const stream = this._observeElement(element); diff --git a/src/cdk/overlay/position/flexible-connected-position-strategy.ts b/src/cdk/overlay/position/flexible-connected-position-strategy.ts index fb22a2f5ab1..14500c566a5 100644 --- a/src/cdk/overlay/position/flexible-connected-position-strategy.ts +++ b/src/cdk/overlay/position/flexible-connected-position-strategy.ts @@ -19,7 +19,7 @@ import { import {Observable, Subscription, Subject, Observer} from 'rxjs'; import {OverlayReference} from '../overlay-reference'; import {isElementScrolledOutsideView, isElementClippedByScrolling} from './scroll-clip'; -import {coerceCssPixelValue, coerceArray} from '@angular/cdk/coercion'; +import {coerceCssPixelValue, coerceArray, coerceElement} from '@angular/cdk/coercion'; import {Platform} from '@angular/cdk/platform'; import {OverlayContainer} from '../overlay-container'; @@ -431,7 +431,7 @@ export class FlexibleConnectedPositionStrategy implements PositionStrategy { * @param origin Reference to the new origin element. */ setOrigin(origin: ElementRef | HTMLElement): this { - this._origin = origin instanceof ElementRef ? origin.nativeElement : origin; + this._origin = coerceElement(origin); return this; } diff --git a/src/cdk/text-field/autofill.ts b/src/cdk/text-field/autofill.ts index 609f17d584a..4707d7a1678 100644 --- a/src/cdk/text-field/autofill.ts +++ b/src/cdk/text-field/autofill.ts @@ -17,6 +17,7 @@ import { OnInit, Output, } from '@angular/core'; +import {coerceElement} from '@angular/cdk/coercion'; import {EMPTY, Observable, Subject} from 'rxjs'; @@ -70,7 +71,7 @@ export class AutofillMonitor implements OnDestroy { return EMPTY; } - const element = elementOrRef instanceof ElementRef ? elementOrRef.nativeElement : elementOrRef; + const element = coerceElement(elementOrRef); const info = this._monitoredElements.get(element); if (info) { @@ -122,7 +123,7 @@ export class AutofillMonitor implements OnDestroy { stopMonitoring(element: ElementRef): void; stopMonitoring(elementOrRef: Element | ElementRef): void { - const element = elementOrRef instanceof ElementRef ? elementOrRef.nativeElement : elementOrRef; + const element = coerceElement(elementOrRef); const info = this._monitoredElements.get(element); if (info) { diff --git a/tools/public_api_guard/cdk/a11y.d.ts b/tools/public_api_guard/cdk/a11y.d.ts index 3792bf20d75..ea6afa083f6 100644 --- a/tools/public_api_guard/cdk/a11y.d.ts +++ b/tools/public_api_guard/cdk/a11y.d.ts @@ -70,10 +70,10 @@ export declare class FocusKeyManager extends ListKeyManager, origin: FocusOrigin, options?: FocusOptions): void; focusVia(element: HTMLElement, origin: FocusOrigin, options?: FocusOptions): void; - monitor(element: HTMLElement, checkChildren?: boolean): Observable; + focusVia(element: ElementRef, origin: FocusOrigin, options?: FocusOptions): void; monitor(element: ElementRef, checkChildren?: boolean): Observable; + monitor(element: HTMLElement, checkChildren?: boolean): Observable; ngOnDestroy(): void; stopMonitoring(element: ElementRef): void; stopMonitoring(element: HTMLElement): void; diff --git a/tools/public_api_guard/cdk/coercion.d.ts b/tools/public_api_guard/cdk/coercion.d.ts index 44725d0a4e5..31ce7b44c2b 100644 --- a/tools/public_api_guard/cdk/coercion.d.ts +++ b/tools/public_api_guard/cdk/coercion.d.ts @@ -6,5 +6,7 @@ export declare function coerceBooleanProperty(value: any): boolean; export declare function coerceCssPixelValue(value: any): string; +export declare function coerceElement(elementOrRef: ElementRef | T): T; + export declare function coerceNumberProperty(value: any): number; export declare function coerceNumberProperty(value: any, fallback: D): number | D;