Skip to content

Commit

Permalink
feat(cdk/scrolling): update ScrollDispatcher to allow HTMLElement par…
Browse files Browse the repository at this point in the history
…ameters (angular#21201)

Update ScrollDispatcher API to take elements of either ElementRef or HTMLElement. This is
to maintain consistency with newer APIs.

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
  • Loading branch information
BobobUnicorn and dependabot[bot] authored Jan 9, 2021
1 parent 7a8762a commit ddc2e23
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 9 deletions.
24 changes: 24 additions & 0 deletions src/cdk/scrolling/scroll-dispatcher.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down
19 changes: 12 additions & 7 deletions src/cdk/scrolling/scroll-dispatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -119,23 +120,25 @@ 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<CdkScrollable|void> {
const ancestors = this.getAncestorScrollContainers(elementRef);
ancestorScrolled(
elementOrElementRef: ElementRef|HTMLElement,
auditTimeInMs?: number): Observable<CdkScrollable|void> {
const ancestors = this.getAncestorScrollContainers(elementOrElementRef);

return this.scrolled(auditTimeInMs).pipe(filter(target => {
return !target || ancestors.indexOf(target) > -1;
}));
}

/** 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);
}
});
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions tools/public_api_guard/cdk/scrolling.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,9 @@ export declare class ScrollDispatcher implements OnDestroy {
_globalSubscription: Subscription | null;
scrollContainers: Map<CdkScrollable, Subscription>;
constructor(_ngZone: NgZone, _platform: Platform, document: any);
ancestorScrolled(elementRef: ElementRef, auditTimeInMs?: number): Observable<CdkScrollable | void>;
ancestorScrolled(elementOrElementRef: ElementRef | HTMLElement, auditTimeInMs?: number): Observable<CdkScrollable | void>;
deregister(scrollable: CdkScrollable): void;
getAncestorScrollContainers(elementRef: ElementRef): CdkScrollable[];
getAncestorScrollContainers(elementOrElementRef: ElementRef | HTMLElement): CdkScrollable[];
ngOnDestroy(): void;
register(scrollable: CdkScrollable): void;
scrolled(auditTimeInMs?: number): Observable<CdkScrollable | void>;
Expand Down
22 changes: 22 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"

[email protected]:
version "1.0.1"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
Expand Down Expand Up @@ -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"
Expand Down

0 comments on commit ddc2e23

Please sign in to comment.