diff --git a/src/cdk/scrolling/scroll-dispatcher.spec.ts b/src/cdk/scrolling/scroll-dispatcher.spec.ts index 9e783304930e..462e809aafd9 100644 --- a/src/cdk/scrolling/scroll-dispatcher.spec.ts +++ b/src/cdk/scrolling/scroll-dispatcher.spec.ts @@ -159,6 +159,14 @@ describe('ScrollDispatcher', () => { expect(scrollableElementIds).toEqual(['scrollable-1', 'scrollable-1a']); }); + it('allows a raw HTMLElement', () => { + const scrollContainers = scroll.getAncestorScrollContainers(element.nativeElement); + const scrollableElementIds = + scrollContainers.map(scrollable => scrollable.getElementRef().nativeElement.id); + + expect(scrollableElementIds).toEqual(['scrollable-1', 'scrollable-1a']); + }); + it('should emit when one of the ancestor scrollable containers is scrolled', () => { const spy = jasmine.createSpy('scroll spy'); const subscription = scroll.ancestorScrolled(element, 0).subscribe(spy); @@ -173,6 +181,22 @@ describe('ScrollDispatcher', () => { subscription.unsubscribe(); }); + it('should emit when one of the ancestor scrollable containers is scrolled (HTMLElement API)', + () => { + const spy = jasmine.createSpy('scroll spy'); + const subscription = scroll.ancestorScrolled(element.nativeElement, 0).subscribe(spy); + const grandparent = fixture.debugElement.nativeElement.querySelector('#scrollable-1'); + + dispatchFakeEvent(grandparent, 'scroll', false); + expect(spy).toHaveBeenCalledTimes(1); + + dispatchFakeEvent(window.document, 'scroll', false); + expect(spy).toHaveBeenCalledTimes(2); + + subscription.unsubscribe(); + }); + + it('should not emit when a non-ancestor is scrolled', () => { const spy = jasmine.createSpy('scroll spy'); const subscription = scroll.ancestorScrolled(element, 0).subscribe(spy); diff --git a/src/cdk/scrolling/scroll-dispatcher.ts b/src/cdk/scrolling/scroll-dispatcher.ts index dd3737cadd67..7da1485f3740 100644 --- a/src/cdk/scrolling/scroll-dispatcher.ts +++ b/src/cdk/scrolling/scroll-dispatcher.ts @@ -6,6 +6,7 @@ * found in the LICENSE file at https://angular.io/license */ +import {coerceElement} from '@angular/cdk/coercion'; import {Platform} from '@angular/cdk/platform'; import {ElementRef, Injectable, NgZone, OnDestroy, Optional, Inject} from '@angular/core'; import {fromEvent, of as observableOf, Subject, Subscription, Observable, Observer} from 'rxjs'; @@ -119,11 +120,13 @@ export class ScrollDispatcher implements OnDestroy { /** * Returns an observable that emits whenever any of the * scrollable ancestors of an element are scrolled. - * @param elementRef Element whose ancestors to listen for. + * @param elementOrElementRef Element whose ancestors to listen for. * @param auditTimeInMs Time to throttle the scroll events. */ - ancestorScrolled(elementRef: ElementRef, auditTimeInMs?: number): Observable { - const ancestors = this.getAncestorScrollContainers(elementRef); + ancestorScrolled( + elementOrElementRef: ElementRef|HTMLElement, + auditTimeInMs?: number): Observable { + const ancestors = this.getAncestorScrollContainers(elementOrElementRef); return this.scrolled(auditTimeInMs).pipe(filter(target => { return !target || ancestors.indexOf(target) > -1; @@ -131,11 +134,11 @@ export class ScrollDispatcher implements OnDestroy { } /** Returns all registered Scrollables that contain the provided element. */ - getAncestorScrollContainers(elementRef: ElementRef): CdkScrollable[] { + getAncestorScrollContainers(elementOrElementRef: ElementRef|HTMLElement): CdkScrollable[] { const scrollingContainers: CdkScrollable[] = []; this.scrollContainers.forEach((_subscription: Subscription, scrollable: CdkScrollable) => { - if (this._scrollableContainsElement(scrollable, elementRef)) { + if (this._scrollableContainsElement(scrollable, elementOrElementRef)) { scrollingContainers.push(scrollable); } }); @@ -149,8 +152,10 @@ export class ScrollDispatcher implements OnDestroy { } /** Returns true if the element is contained within the provided Scrollable. */ - private _scrollableContainsElement(scrollable: CdkScrollable, elementRef: ElementRef): boolean { - let element: HTMLElement | null = elementRef.nativeElement; + private _scrollableContainsElement( + scrollable: CdkScrollable, + elementOrElementRef: ElementRef|HTMLElement): boolean { + let element: HTMLElement | null = coerceElement(elementOrElementRef); let scrollableElement = scrollable.getElementRef().nativeElement; // Traverse through the element parents until we reach null, checking if any of the elements diff --git a/tools/public_api_guard/cdk/scrolling.d.ts b/tools/public_api_guard/cdk/scrolling.d.ts index 551297e08455..be64b7de9c1b 100644 --- a/tools/public_api_guard/cdk/scrolling.d.ts +++ b/tools/public_api_guard/cdk/scrolling.d.ts @@ -170,9 +170,9 @@ export declare class ScrollDispatcher implements OnDestroy { _globalSubscription: Subscription | null; scrollContainers: Map; constructor(_ngZone: NgZone, _platform: Platform, document: any); - ancestorScrolled(elementRef: ElementRef, auditTimeInMs?: number): Observable; + ancestorScrolled(elementOrElementRef: ElementRef | HTMLElement, auditTimeInMs?: number): Observable; deregister(scrollable: CdkScrollable): void; - getAncestorScrollContainers(elementRef: ElementRef): CdkScrollable[]; + getAncestorScrollContainers(elementOrElementRef: ElementRef | HTMLElement): CdkScrollable[]; ngOnDestroy(): void; register(scrollable: CdkScrollable): void; scrolled(auditTimeInMs?: number): Observable; diff --git a/yarn.lock b/yarn.lock index acaf9cf13056..f90f4a354145 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8812,6 +8812,23 @@ meow@^8.0.0: type-fest "^0.18.0" yargs-parser "^20.2.3" +meow@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/meow/-/meow-8.0.0.tgz#1aa10ee61046719e334ffdc038bb5069250ec99a" + integrity sha512-nbsTRz2fwniJBFgUkcdISq8y/q9n9VbiHYbfwklFh5V4V2uAcxtKQkDc0yCLPM/kP0d+inZBewn3zJqewHE7kg== + dependencies: + "@types/minimist" "^1.2.0" + camelcase-keys "^6.2.2" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "4.1.0" + normalize-package-data "^3.0.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.18.0" + yargs-parser "^20.2.3" + merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -12787,6 +12804,11 @@ type-fest@^0.18.0: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== +type-fest@^0.18.0: + version "0.18.1" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.18.1.tgz#db4bc151a4a2cf4eebf9add5db75508db6cc841f" + integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== + type-fest@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b"