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:
parent
7d55cf68d8
commit
7742fd7dc2
4 changed files with 86 additions and 8 deletions
5
.changeset/nine-houses-attend.md
Normal file
5
.changeset/nine-houses-attend.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
'astro': patch
|
||||
---
|
||||
|
||||
View Transitions: handle clicks on SVGAElements and image maps"
|
|
@ -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',
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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>
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue