diff --git a/goldens/public-api/common/index.api.md b/goldens/public-api/common/index.api.md index 2742f9e94c3e..2a9babb62ce5 100644 --- a/goldens/public-api/common/index.api.md +++ b/goldens/public-api/common/index.api.md @@ -959,8 +959,8 @@ export const VERSION: Version; // @public export abstract class ViewportScroller { abstract getScrollPosition(): [number, number]; - abstract scrollToAnchor(anchor: string): void; - abstract scrollToPosition(position: [number, number]): void; + abstract scrollToAnchor(anchor: string, options?: ScrollOptions): void; + abstract scrollToPosition(position: [number, number], options?: ScrollOptions): void; abstract setHistoryScrollRestoration(scrollRestoration: 'auto' | 'manual'): void; abstract setOffset(offset: [number, number] | (() => [number, number])): void; // (undocumented) diff --git a/packages/common/src/viewport_scroller.ts b/packages/common/src/viewport_scroller.ts index d6983457d58d..e6a36d5e52fe 100644 --- a/packages/common/src/viewport_scroller.ts +++ b/packages/common/src/viewport_scroller.ts @@ -44,13 +44,13 @@ export abstract class ViewportScroller { * Scrolls to a specified position. * @param position A position in screen coordinates (a tuple with x and y values). */ - abstract scrollToPosition(position: [number, number]): void; + abstract scrollToPosition(position: [number, number], options?: ScrollOptions): void; /** * Scrolls to an anchor element. * @param anchor The ID of the anchor element. */ - abstract scrollToAnchor(anchor: string): void; + abstract scrollToAnchor(anchor: string, options?: ScrollOptions): void; /** * Disables automatic scroll restoration provided by the browser. @@ -97,8 +97,8 @@ export class BrowserViewportScroller implements ViewportScroller { * Sets the scroll position. * @param position The new position in screen coordinates. */ - scrollToPosition(position: [number, number]): void { - this.window.scrollTo(position[0], position[1]); + scrollToPosition(position: [number, number], options?: ScrollOptions): void { + this.window.scrollTo({...options, left: position[0], top: position[1]}); } /** @@ -112,11 +112,11 @@ export class BrowserViewportScroller implements ViewportScroller { * @see https://html.spec.whatwg.org/#the-indicated-part-of-the-document * @see https://html.spec.whatwg.org/#scroll-to-fragid */ - scrollToAnchor(target: string): void { + scrollToAnchor(target: string, options?: ScrollOptions): void { const elSelected = findAnchorFromDocument(this.document, target); if (elSelected) { - this.scrollToElement(elSelected); + this.scrollToElement(elSelected, options); // After scrolling to the element, the spec dictates that we follow the focus steps for the // target. Rather than following the robust steps, simply attempt focus. // @@ -140,12 +140,16 @@ export class BrowserViewportScroller implements ViewportScroller { * The offset can be used when we know that there is a floating header and scrolling naively to an * element (ex: `scrollIntoView`) leaves the element hidden behind the floating header. */ - private scrollToElement(el: HTMLElement): void { + private scrollToElement(el: HTMLElement, options?: ScrollOptions): void { const rect = el.getBoundingClientRect(); const left = rect.left + this.window.pageXOffset; const top = rect.top + this.window.pageYOffset; const offset = this.offset(); - this.window.scrollTo(left - offset[0], top - offset[1]); + this.window.scrollTo({ + ...options, + left: left - offset[0], + top: top - offset[1], + }); } } diff --git a/packages/common/test/viewport_scroller_spec.ts b/packages/common/test/viewport_scroller_spec.ts index 097f70ac6b9b..aa01bb444670 100644 --- a/packages/common/test/viewport_scroller_spec.ts +++ b/packages/common/test/viewport_scroller_spec.ts @@ -36,10 +36,15 @@ describe('BrowserViewportScroller', () => { expect(() => scroller.setHistoryScrollRestoration('manual')).not.toThrow(); }); + it('should not allow overwriting position with options', () => { + scroller.scrollToPosition([10, 10], {top: 0, left: 0} as any); + expect(windowSpy.scrollTo).toHaveBeenCalledWith({top: 10, left: 10}); + }); + it('should still allow scrolling if scrollRestoration is not writable', () => { createNonWritableScrollRestoration(); scroller.scrollToPosition([10, 10]); - expect(windowSpy.scrollTo as jasmine.Spy).toHaveBeenCalledWith(10, 10); + expect(windowSpy.scrollTo).toHaveBeenCalledWith({top: 10, left: 10}); }); }); @@ -98,6 +103,14 @@ describe('BrowserViewportScroller', () => { cleanup(); }); + it('should not allow overwriting position with options', () => { + const {anchorNode, cleanup} = createTallElementWithShadowRoot(); + anchorNode.name = anchor; + scroller.scrollToAnchor(anchor, {top: 0, left: 0} as any); + expect(scroller.getScrollPosition()[1]).not.toEqual(0); + cleanup(); + }); + function createTallElement() { const tallItem = document.createElement('div'); tallItem.style.height = '3000px';