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:
parent
1f3d72b8d3
commit
5062d27a18
4 changed files with 107 additions and 5 deletions
5
.changeset/rude-geckos-rush.md
Normal file
5
.changeset/rude-geckos-rush.md
Normal 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
|
|
@ -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>
|
||||
|
|
|
@ -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'));
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in a new issue