mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(docs-infra): focus elements when scrolling to them (if focusable) (#43460)
When using `ScrollService#scrollToElement()` to scroll to a specific element, also focus the element if it is focusable (i.e. if it has a `focus()` method). This will be useful for implementing a "skip to content" link as described in #42936. PR Close #43460
This commit is contained in:
committed by
Jessica Janiuk
parent
0e2ff28627
commit
235e4aaa25
@@ -15,9 +15,9 @@ describe('ScrollService', () => {
|
||||
return instance;
|
||||
};
|
||||
|
||||
const topOfPageElem = {} as Element;
|
||||
const topOfPageElem = {} as HTMLElement;
|
||||
let injector: Injector;
|
||||
let document: MockDocument;
|
||||
let document: Document & MockDocument;
|
||||
let platformLocation: MockPlatformLocation;
|
||||
let scrollService: ScrollService;
|
||||
let location: SpyLocation;
|
||||
@@ -60,7 +60,7 @@ describe('ScrollService', () => {
|
||||
});
|
||||
|
||||
platformLocation = injector.get(PlatformLocation);
|
||||
document = injector.get(DOCUMENT) as unknown as MockDocument;
|
||||
document = injector.get(DOCUMENT) as unknown as Document & MockDocument;
|
||||
scrollService = injector.get(ScrollService);
|
||||
location = injector.get(Location) as unknown as SpyLocation;
|
||||
sessionStorage = injector.get(SessionStorage);
|
||||
@@ -241,14 +241,22 @@ describe('ScrollService', () => {
|
||||
|
||||
describe('#scrollToElement', () => {
|
||||
it('should scroll to element', () => {
|
||||
const element: Element = new MockElement() as any;
|
||||
const element: HTMLElement = new MockElement() as any;
|
||||
scrollService.scrollToElement(element);
|
||||
expect(element.scrollIntoView).toHaveBeenCalled();
|
||||
expect(window.scrollBy).toHaveBeenCalledWith(0, -scrollService.topOffset);
|
||||
});
|
||||
|
||||
it('should focus the element if focusable', () => {
|
||||
const element: HTMLElement = Object.assign(new MockElement() as any, {
|
||||
focus: jasmine.createSpy('Element focus'),
|
||||
});
|
||||
scrollService.scrollToElement(element);
|
||||
expect(element.focus).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not scroll more than necessary (e.g. for elements close to the bottom)', () => {
|
||||
const element: Element = new MockElement() as any;
|
||||
const element: HTMLElement = new MockElement() as any;
|
||||
const getBoundingClientRect = element.getBoundingClientRect as jasmine.Spy;
|
||||
const topOffset = scrollService.topOffset;
|
||||
|
||||
@@ -264,7 +272,7 @@ describe('ScrollService', () => {
|
||||
});
|
||||
|
||||
it('should scroll all the way to the top if close enough', () => {
|
||||
const element: Element = new MockElement() as any;
|
||||
const element: HTMLElement = new MockElement() as any;
|
||||
|
||||
(window as any).pageYOffset = 25;
|
||||
scrollService.scrollToElement(element);
|
||||
|
||||
@@ -17,7 +17,7 @@ export const topMargin = 16;
|
||||
@Injectable()
|
||||
export class ScrollService implements OnDestroy {
|
||||
private _topOffset: number|null;
|
||||
private _topOfPageElement: Element;
|
||||
private _topOfPageElement: HTMLElement;
|
||||
private onDestroy = new Subject<void>();
|
||||
|
||||
// The scroll position which has to be restored, after a `popstate` event.
|
||||
@@ -44,7 +44,7 @@ export class ScrollService implements OnDestroy {
|
||||
}
|
||||
|
||||
constructor(
|
||||
@Inject(DOCUMENT) private document: any, private platformLocation: PlatformLocation,
|
||||
@Inject(DOCUMENT) private document: Document, private platformLocation: PlatformLocation,
|
||||
private viewportScroller: ViewportScroller, private location: Location,
|
||||
@Inject(SessionStorage) private storage: Storage) {
|
||||
// On resize, the toolbar might change height, so "invalidate" the top offset.
|
||||
@@ -100,7 +100,7 @@ export class ScrollService implements OnDestroy {
|
||||
*/
|
||||
scroll() {
|
||||
const hash = this.getCurrentHash();
|
||||
const element: HTMLElement = hash ? this.document.getElementById(hash) : this.topOfPageElement;
|
||||
const element = hash ? this.document.getElementById(hash) ?? null : this.topOfPageElement;
|
||||
this.scrollToElement(element);
|
||||
}
|
||||
|
||||
@@ -147,9 +147,10 @@ export class ScrollService implements OnDestroy {
|
||||
* Scroll to the element.
|
||||
* Don't scroll if no element.
|
||||
*/
|
||||
scrollToElement(element: Element|null) {
|
||||
scrollToElement(element: HTMLElement|null) {
|
||||
if (element) {
|
||||
element.scrollIntoView();
|
||||
element.focus?.();
|
||||
|
||||
if (window && window.scrollBy) {
|
||||
// Scroll as much as necessary to align the top of `element` at `topOffset`.
|
||||
|
||||
Reference in New Issue
Block a user