summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.changeset/nine-houses-attend.md5
-rw-r--r--packages/astro/components/ViewTransitions.astro24
-rw-r--r--packages/astro/e2e/fixtures/view-transitions/src/pages/non-html-anchor.astro22
-rw-r--r--packages/astro/e2e/view-transitions.test.js43
4 files changed, 86 insertions, 8 deletions
diff --git a/.changeset/nine-houses-attend.md b/.changeset/nine-houses-attend.md
new file mode 100644
index 000000000..a4cbb7144
--- /dev/null
+++ b/.changeset/nine-houses-attend.md
@@ -0,0 +1,5 @@
+---
+'astro': patch
+---
+
+View Transitions: handle clicks on SVGAElements and image maps"
diff --git a/packages/astro/components/ViewTransitions.astro b/packages/astro/components/ViewTransitions.astro
index 089d8d8e5..a06f1c2a6 100644
--- a/packages/astro/components/ViewTransitions.astro
+++ b/packages/astro/components/ViewTransitions.astro
@@ -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',
});
});
diff --git a/packages/astro/e2e/fixtures/view-transitions/src/pages/non-html-anchor.astro b/packages/astro/e2e/fixtures/view-transitions/src/pages/non-html-anchor.astro
new file mode 100644
index 000000000..8d5ea8d46
--- /dev/null
+++ b/packages/astro/e2e/fixtures/view-transitions/src/pages/non-html-anchor.astro
@@ -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>
diff --git a/packages/astro/e2e/view-transitions.test.js b/packages/astro/e2e/view-transitions.test.js
index 20ea8adbc..c56abae24 100644
--- a/packages/astro/e2e/view-transitions.test.js
+++ b/packages/astro/e2e/view-transitions.test.js
@@ -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);
+ });
});