0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-03-24 23:21:57 -05:00

Extend the 'click' listener of view transitions to handle links in SVGs and areas in image maps (#9140)

* handle clicks on SVGAElements and image maps

* add changeset
This commit is contained in:
Martin Trapp 2023-11-20 21:10:35 +01:00 committed by GitHub
parent 7d55cf68d8
commit 7742fd7dc2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 86 additions and 8 deletions

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
View Transitions: handle clicks on SVGAElements and image maps"

View file

@ -30,6 +30,7 @@ const { fallback = 'animate', handleForms } = Astro.props;
import type { Options } from 'astro:transitions/client';
import { supportsViewTransitions, navigate } from 'astro:transitions/client';
// NOTE: import from `astro/prefetch` as `astro:prefetch` requires the `prefetch` config to be enabled
// @ts-ignore
import { init } from 'astro/prefetch';
export type Fallback = 'none' | 'animate' | 'swap';
@ -42,27 +43,34 @@ const { fallback = 'animate', handleForms } = Astro.props;
return 'animate';
}
function isReloadEl(el: HTMLElement): boolean {
function isReloadEl(el: HTMLElement | SVGAElement): boolean {
return el.dataset.astroReload !== undefined;
}
if (supportsViewTransitions || getFallback() !== 'none') {
document.addEventListener('click', (ev) => {
let link = ev.target;
if (link instanceof Element && link.tagName !== 'A') {
link = link.closest('a');
if (link instanceof Element) {
link = link.closest('a, area');
}
if (
!(link instanceof HTMLAnchorElement) &&
!(link instanceof SVGAElement) &&
!(link instanceof HTMLAreaElement)
)
return;
// This check verifies that the click is happening on an anchor
// that is going to another page within the same origin. Basically it determines
// same-origin navigation, but omits special key combos for new tabs, etc.
const linkTarget = link instanceof HTMLElement ? link.target : link.target.baseVal;
const href = link instanceof HTMLElement ? link.href : link.href.baseVal;
const origin = new URL(href, location.href).origin;
if (
!link ||
!(link instanceof HTMLAnchorElement) ||
isReloadEl(link) ||
link.hasAttribute('download') ||
!link.href ||
(link.target && link.target !== '_self') ||
link.origin !== location.origin ||
(linkTarget && linkTarget !== '_self') ||
origin !== location.origin ||
ev.button !== 0 || // left clicks only
ev.metaKey || // new tab (mac)
ev.ctrlKey || // new tab (windows)
@ -75,7 +83,7 @@ const { fallback = 'animate', handleForms } = Astro.props;
return;
}
ev.preventDefault();
navigate(link.href, {
navigate(href, {
history: link.dataset.astroHistory === 'replace' ? 'replace' : 'auto',
});
});

View file

@ -0,0 +1,22 @@
---
import Layout from '../components/Layout.astro';
---
<Layout>
<h1>SVGA and Image Map links</h1>
<svg viewBox="0 0 160 40" xmlns="http://www.w3.org/2000/svg">
<a href="/two" id="svga">
<text x="10" y="25" id="insidesvga">text within a svga</text>
</a>
</svg>
<map name="map">
<area shape="default" href="/two" alt="logo" id="area"/>
</map>
<img id="logo" usemap="#map" src="/logo.svg" alt="logo" />
<style>
body {
background: #888;
}
</style>

View file

@ -1016,4 +1016,47 @@ test.describe('View Transitions', () => {
const result = page.locator('#three-result');
await expect(result, 'should have content').toHaveText('Got: Testing');
});
test('click on an svg anchor should trigger navigation', async ({ page, astro }) => {
const loads = [];
page.addListener('load', (p) => {
loads.push(p.title());
});
await page.goto(astro.resolveUrl('/non-html-anchor'));
let locator = page.locator('#insidesvga');
await expect(locator, 'should have attribute').toHaveAttribute('x', '10');
await page.click('#svga');
const p = page.locator('#two');
await expect(p, 'should have content').toHaveText('Page 2');
expect(loads.length, 'There should only be 1 page load').toEqual(1);
});
test('click inside an svg anchor should trigger navigation', async ({ page, astro }) => {
const loads = [];
page.addListener('load', (p) => {
loads.push(p.title());
});
await page.goto(astro.resolveUrl('/non-html-anchor'));
let locator = page.locator('#insidesvga');
await expect(locator, 'should have content').toHaveText('text within a svga');
await page.click('#insidesvga');
const p = page.locator('#two');
await expect(p, 'should have content').toHaveText('Page 2');
expect(loads.length, 'There should only be 1 page load').toEqual(1);
});
test('click on an area in an image map should trigger navigation', async ({ page, astro }) => {
const loads = [];
page.addListener('load', (p) => {
loads.push(p.title());
});
await page.goto(astro.resolveUrl('/non-html-anchor'));
let locator = page.locator('#area');
await expect(locator, 'should have attribute').toHaveAttribute('shape', 'default');
await page.click('#logo');
const p = page.locator('#two');
await expect(p, 'should have content').toHaveText('Page 2');
expect(loads.length, 'There should only be 1 page load').toEqual(1);
});
});