mirror of
https://github.com/withastro/astro.git
synced 2025-02-24 22:46:02 -05:00
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;
|
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;
|
document.documentElement.dataset.astroTransition = dir;
|
||||||
if (supportsViewTransitions) {
|
if (supportsViewTransitions) {
|
||||||
finished = document.startViewTransition(() => updateDOM(doc, loc, state)).finished;
|
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
|
// 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
|
// So we scroll to the top of the page but do not start page transitions
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
persistState({ ...history.state, scrollY });
|
// push state on the first navigation but not if we were here already
|
||||||
scrollTo({ left: 0, top: 0, behavior: 'instant' });
|
|
||||||
if (location.hash) {
|
if (location.hash) {
|
||||||
// last target was different
|
history.replaceState({ index: currentHistoryIndex, scrollY: -(scrollY + 1) }, '');
|
||||||
const newState: State = { index: ++currentHistoryIndex, scrollY: 0 };
|
const newState: State = { index: ++currentHistoryIndex, scrollY: 0 };
|
||||||
history.pushState(newState, '', link.href);
|
history.pushState(newState, '', link.href);
|
||||||
}
|
}
|
||||||
|
scrollTo({ left: 0, top: 0, behavior: 'instant' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// these are the cases we will handle: same origin, different page
|
// these are the cases we will handle: same origin, different page
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
persistState({ index: currentHistoryIndex, scrollY });
|
|
||||||
navigate('forward', new URL(link.href));
|
navigate('forward', new URL(link.href));
|
||||||
});
|
});
|
||||||
|
|
||||||
addEventListener('popstate', (ev) => {
|
addEventListener('popstate', (ev) => {
|
||||||
if (!transitionEnabledOnThisPage() && ev.state) {
|
if (!transitionEnabledOnThisPage() && ev.state) {
|
||||||
// The current page doesn't haven't View Transitions,
|
// The current page doesn't have View Transitions enabled
|
||||||
// respect that with a full page reload
|
// but the page we navigate to does (because it set the state).
|
||||||
// -- but only for transition managed by us (ev.state is set)
|
// 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');
|
history.scrollRestoration && (history.scrollRestoration = 'manual');
|
||||||
location.reload();
|
location.reload();
|
||||||
return;
|
return;
|
||||||
|
@ -383,7 +386,11 @@ const { fallback = 'animate' } = Astro.props as Props;
|
||||||
const nextIndex = state.index;
|
const nextIndex = state.index;
|
||||||
const direction: Direction = nextIndex > currentHistoryIndex ? 'forward' : 'back';
|
const direction: Direction = nextIndex > currentHistoryIndex ? 'forward' : 'back';
|
||||||
currentHistoryIndex = nextIndex;
|
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) => {
|
['mouseenter', 'touchstart', 'focus'].forEach((evName) => {
|
||||||
|
|
|
@ -282,6 +282,28 @@ test.describe('View Transitions', () => {
|
||||||
await expect(locator).toBeInViewport();
|
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 }) => {
|
test('Scroll position restored on forward button', async ({ page, astro }) => {
|
||||||
// Go to page 1
|
// Go to page 1
|
||||||
await page.goto(astro.resolveUrl('/one'));
|
await page.goto(astro.resolveUrl('/one'));
|
||||||
|
|
Loading…
Add table
Reference in a new issue