0
Fork 0
mirror of https://github.com/withastro/astro.git synced 2025-01-06 22:10:10 -05:00

Respect forms with enctype set for view transitions (#9466)

* Respect forms with enctype set for view transitions

* Add changeset

* Revert "Respect forms with enctype set for view transitions"

This reverts commit 6d3e04a959.

* Review feedback

* Handle submitter case

* Move comment

* Update .changeset/rude-geckos-rush.md

* Add tests

---------

Co-authored-by: Nate Moore <natemoo-re@users.noreply.github.com>
This commit is contained in:
Ken Powers 2023-12-20 07:37:48 -05:00 committed by GitHub
parent 1f3d72b8d3
commit 5062d27a18
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 107 additions and 5 deletions

View file

@ -0,0 +1,5 @@
---
'astro': patch
---
Updates view transitions `form` handling with logic for the [`enctype`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/enctype) attribute

View file

@ -1,13 +1,15 @@
---
import Layout from '../components/Layout.astro';
const method = Astro.url.searchParams.get('method') ?? 'POST';
const enctype = Astro.url.searchParams.get('enctype');
const postShowThrow = Astro.url.searchParams.has('throw') ?? false;
---
<Layout>
<h2>Contact Form</h2>
<form action="/contact" method={method}>
<input type="hidden" name="name" value="Testing">
{postShowThrow ? <input type="hidden" name="throw" value="true"> : ''}
<input type="submit" value="Submit" id="submit">
<form action="/contact" method={method} {...enctype ? { enctype } : {}}>
<input type="hidden" name="name" value="Testing" />
{postShowThrow ? <input type="hidden" name="throw" value="true" /> : ''}
<input type="submit" value="Submit" id="submit" />
</form>
</Layout>

View file

@ -976,6 +976,83 @@ test.describe('View Transitions', () => {
).toEqual(1);
});
test('form POST defaults to multipart/form-data (Astro 4.x compatibility)', async ({
page,
astro,
}) => {
const loads = [];
page.addListener('load', async (p) => {
loads.push(p);
});
const postedEncodings = [];
await page.route('**/contact', async (route) => {
const request = route.request();
if (request.method() === 'POST') {
postedEncodings.push(request.headers()['content-type'].split(';')[0]);
}
await route.continue();
});
await page.goto(astro.resolveUrl('/form-one'));
// Submit the form
await page.click('#submit');
expect(
loads.length,
'There should be only 1 page load. No additional loads for the form submission'
).toEqual(1);
expect(
postedEncodings,
'There should be 1 POST, with encoding set to `multipart/form-data`'
).toEqual(['multipart/form-data']);
});
test('form POST respects enctype attribute', async ({ page, astro }) => {
const loads = [];
page.addListener('load', async (p) => {
loads.push(p);
});
const postedEncodings = [];
await page.route('**/contact', async (route) => {
const request = route.request();
if (request.method() === 'POST') {
postedEncodings.push(request.headers()['content-type'].split(';')[0]);
}
await route.continue();
});
await page.goto(
astro.resolveUrl(
`/form-one?${new URLSearchParams({ enctype: 'application/x-www-form-urlencoded' })}`
)
);
// Submit the form
await page.click('#submit');
expect(
loads.length,
'There should be only 1 page load. No additional loads for the form submission'
).toEqual(1);
expect(
postedEncodings,
'There should be 1 POST, with encoding set to `multipart/form-data`'
).toEqual(['application/x-www-form-urlencoded']);
});
test('Route announcer is invisible on page transition', async ({ page, astro }) => {
await page.goto(astro.resolveUrl('/no-directive-one'));

View file

@ -463,7 +463,25 @@ async function transition(
const init: RequestInit = {};
if (preparationEvent.formData) {
init.method = 'POST';
init.body = preparationEvent.formData;
const form =
preparationEvent.sourceElement instanceof HTMLFormElement
? preparationEvent.sourceElement
: preparationEvent.sourceElement instanceof HTMLElement &&
'form' in preparationEvent.sourceElement
? (preparationEvent.sourceElement.form as HTMLFormElement)
: preparationEvent.sourceElement?.closest('form');
// Form elements without enctype explicitly set default to application/x-www-form-urlencoded.
// In order to maintain compatibility with Astro 4.x, we need to check the value of enctype
// on the attributes property rather than accessing .enctype directly. Astro 5.x may
// introduce defaulting to application/x-www-form-urlencoded as a breaking change, and then
// we can access .enctype directly.
//
// Note: getNamedItem can return null in real life, even if TypeScript doesn't think so, hence
// the ?.
init.body =
form?.attributes.getNamedItem('enctype')?.value === 'application/x-www-form-urlencoded'
? new URLSearchParams(preparationEvent.formData as any)
: preparationEvent.formData;
}
const response = await fetchHTML(href, init);
// If there is a problem fetching the new page, just do an MPA navigation to it.