Bugfixes for back navigation in the view transition client-side router (#8491)
* Bugfixes for back navigation in the view transition client-side router * re-introduced pushState on self links as required for update of browser's address bar * format
This commit is contained in:
parent
78b82bb392
commit
0ca332ba4a
3 changed files with 42 additions and 8 deletions
5
.changeset/witty-readers-behave.md
Normal file
5
.changeset/witty-readers-behave.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
Bugfixes for back navigation in the view transition client-side router
|
|
@ -262,6 +262,9 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|||
return;
|
||||
}
|
||||
|
||||
// Now we are sure that we will push state, and it is time to create a state if it is still missing.
|
||||
!state && history.replaceState({ index: currentHistoryIndex, scrollY }, '');
|
||||
|
||||
document.documentElement.dataset.astroTransition = dir;
|
||||
if (supportsViewTransitions) {
|
||||
finished = document.startViewTransition(() => updateDOM(doc, loc, state)).finished;
|
||||
|
@ -335,28 +338,28 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|||
// But we want to handle it like any other same page navigation
|
||||
// So we scroll to the top of the page but do not start page transitions
|
||||
ev.preventDefault();
|
||||
persistState({ ...history.state, scrollY });
|
||||
scrollTo({ left: 0, top: 0, behavior: 'instant' });
|
||||
// push state on the first navigation but not if we were here already
|
||||
if (location.hash) {
|
||||
// last target was different
|
||||
history.replaceState({ index: currentHistoryIndex, scrollY: -(scrollY + 1) }, '');
|
||||
const newState: State = { index: ++currentHistoryIndex, scrollY: 0 };
|
||||
history.pushState(newState, '', link.href);
|
||||
}
|
||||
scrollTo({ left: 0, top: 0, behavior: 'instant' });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// these are the cases we will handle: same origin, different page
|
||||
ev.preventDefault();
|
||||
persistState({ index: currentHistoryIndex, scrollY });
|
||||
navigate('forward', new URL(link.href));
|
||||
});
|
||||
|
||||
addEventListener('popstate', (ev) => {
|
||||
if (!transitionEnabledOnThisPage() && ev.state) {
|
||||
// The current page doesn't haven't View Transitions,
|
||||
// respect that with a full page reload
|
||||
// -- but only for transition managed by us (ev.state is set)
|
||||
// The current page doesn't have View Transitions enabled
|
||||
// but the page we navigate to does (because it set the state).
|
||||
// Do a full page refresh to reload the client-side router from the new page.
|
||||
// Scroll restauration will then happen during the reload when the router's code is re-executed
|
||||
history.scrollRestoration && (history.scrollRestoration = 'manual');
|
||||
location.reload();
|
||||
return;
|
||||
|
@ -383,7 +386,11 @@ const { fallback = 'animate' } = Astro.props as Props;
|
|||
const nextIndex = state.index;
|
||||
const direction: Direction = nextIndex > currentHistoryIndex ? 'forward' : 'back';
|
||||
currentHistoryIndex = nextIndex;
|
||||
navigate(direction, new URL(location.href), state);
|
||||
if (state.scrollY < 0) {
|
||||
scrollTo(0, -(state.scrollY + 1));
|
||||
} else {
|
||||
navigate(direction, new URL(location.href), state);
|
||||
}
|
||||
});
|
||||
|
||||
['mouseenter', 'touchstart', 'focus'].forEach((evName) => {
|
||||
|
|
|
@ -282,6 +282,28 @@ test.describe('View Transitions', () => {
|
|||
await expect(locator).toBeInViewport();
|
||||
});
|
||||
|
||||
test('Scroll position restored when transitioning back to fragment', async ({ page, astro }) => {
|
||||
// Go to the long page
|
||||
await page.goto(astro.resolveUrl('/long-page'));
|
||||
let locator = page.locator('#longpage');
|
||||
await expect(locator).toBeInViewport();
|
||||
|
||||
// Scroll down to middle fragment
|
||||
await page.click('#click-scroll-down');
|
||||
locator = page.locator('#click-one-again');
|
||||
await expect(locator).toBeInViewport();
|
||||
|
||||
// Scroll up to top fragment
|
||||
await page.click('#click-one-again');
|
||||
locator = page.locator('#one');
|
||||
await expect(locator).toHaveText('Page 1');
|
||||
|
||||
// Back to middle of the page
|
||||
await page.goBack();
|
||||
locator = page.locator('#click-one-again');
|
||||
await expect(locator).toBeInViewport();
|
||||
});
|
||||
|
||||
test('Scroll position restored on forward button', async ({ page, astro }) => {
|
||||
// Go to page 1
|
||||
await page.goto(astro.resolveUrl('/one'));
|
||||
|
|
Loading…
Reference in a new issue