mirror of
https://github.com/bitwarden/browser
synced 2026-01-29 15:53:45 +00:00
fix scroll top in firefox
This commit is contained in:
@@ -266,6 +266,7 @@ export class ViewV2Component {
|
||||
}
|
||||
|
||||
this.popupScrollPositionService.stop(true);
|
||||
this.popupScrollPositionService.forceTopOnNextVaultStart();
|
||||
await this.popupRouterCacheService.back();
|
||||
|
||||
this.toastService.showToast({
|
||||
|
||||
@@ -108,6 +108,28 @@ describe("VaultPopupScrollPositionService", () => {
|
||||
});
|
||||
expect((scrollElement as any).scrollTop).toBe(500);
|
||||
}));
|
||||
|
||||
it("forces scroll to top on next start when requested", fakeAsync(() => {
|
||||
service["scrollPosition"] = 500;
|
||||
service.forceTopOnNextVaultStart();
|
||||
|
||||
service.start(scrollElement);
|
||||
|
||||
tick();
|
||||
expect((scrollElement as any).scrollTo).toHaveBeenCalledWith({
|
||||
behavior: "instant",
|
||||
top: 0,
|
||||
});
|
||||
expect((scrollElement as any).scrollTop).toBe(0);
|
||||
|
||||
// A follow-up scroll is scheduled to defeat Firefox scroll restoration.
|
||||
((scrollElement as any).scrollTo as jest.Mock).mockClear();
|
||||
tick(50);
|
||||
expect((scrollElement as any).scrollTo).toHaveBeenCalledWith({
|
||||
behavior: "instant",
|
||||
top: 0,
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe("scroll listener", () => {
|
||||
|
||||
@@ -18,6 +18,9 @@ export class VaultPopupScrollPositionService {
|
||||
/** Subscription associated with the virtual scroll element. */
|
||||
private scrollSubscription: Subscription | null = null;
|
||||
|
||||
/** When true, the next call to `start()` will force scroll position back to the top. */
|
||||
private forceTopOnNextStart = false;
|
||||
|
||||
constructor() {
|
||||
this.router.events
|
||||
.pipe(
|
||||
@@ -31,8 +34,22 @@ export class VaultPopupScrollPositionService {
|
||||
|
||||
/** Scrolls the user to the stored scroll position and starts tracking scroll of the page. */
|
||||
start(scrollElement: HTMLElement) {
|
||||
if (this.hasScrollPosition()) {
|
||||
// Use `setTimeout` to scroll after rendering is complete
|
||||
// Use `setTimeout` to scroll after rendering is complete.
|
||||
// Firefox can sometimes restore scroll position on history navigation (back/forward)
|
||||
// after our initial scroll call. When we explicitly want to reset to the top (e.g. after
|
||||
// deleting an item), we schedule an extra follow-up scroll to ensure the final position is 0.
|
||||
if (this.forceTopOnNextStart) {
|
||||
this.forceTopOnNextStart = false;
|
||||
this.scrollPosition = 0;
|
||||
// try to set scroll to top immediately
|
||||
setTimeout(() => {
|
||||
scrollElement.scrollTo({ top: 0, behavior: "instant" });
|
||||
}, 0);
|
||||
// wait for FF to possibly restore scroll position after UI settles, then enforce top=0
|
||||
setTimeout(() => {
|
||||
scrollElement.scrollTo({ top: 0, behavior: "instant" });
|
||||
}, 250);
|
||||
} else if (this.hasScrollPosition()) {
|
||||
setTimeout(() => {
|
||||
scrollElement.scrollTo({ top: this.scrollPosition!, behavior: "instant" });
|
||||
});
|
||||
@@ -66,6 +83,14 @@ export class VaultPopupScrollPositionService {
|
||||
return this.scrollPosition !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the next vault list render to start at scroll position 0.
|
||||
* Useful for flows where we don't want browser back/forward scroll restoration (e.g. after delete).
|
||||
*/
|
||||
forceTopOnNextVaultStart() {
|
||||
this.forceTopOnNextStart = true;
|
||||
}
|
||||
|
||||
/** Conditionally resets the scroll listeners based on the ending path of the navigation */
|
||||
private resetListenerForNavigation(event: NavigationEnd): void {
|
||||
// The vault page is the target of the scroll listener, return early
|
||||
|
||||
Reference in New Issue
Block a user