From 6d3e04a9598a2de8ccc153911fd6ab629abcaf7a Mon Sep 17 00:00:00 2001 From: Ken Powers Date: Mon, 18 Dec 2023 19:16:04 -0500 Subject: [PATCH] Respect forms with enctype set for view transitions --- packages/astro/components/ViewTransitions.astro | 13 ++++++++++++- packages/astro/src/transitions/events.ts | 10 +++++----- packages/astro/src/transitions/router.ts | 8 ++++---- packages/astro/src/transitions/types.ts | 2 +- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/packages/astro/components/ViewTransitions.astro b/packages/astro/components/ViewTransitions.astro index 310f1865a9..00376a5782 100644 --- a/packages/astro/components/ViewTransitions.astro +++ b/packages/astro/components/ViewTransitions.astro @@ -117,7 +117,18 @@ const { fallback = 'animate' } = Astro.props; url.search = params.toString(); action = url.toString(); } else { - options.formData = formData; + // 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. + const enctype = form.attributes.getNamedItem('enctype')?.value; + options.body = + enctype === 'application/x-www-form-urlencoded' + ? new URLSearchParams(formData as any) + : formData; } ev.preventDefault(); diff --git a/packages/astro/src/transitions/events.ts b/packages/astro/src/transitions/events.ts index b3921b31f0..691352d017 100644 --- a/packages/astro/src/transitions/events.ts +++ b/packages/astro/src/transitions/events.ts @@ -66,7 +66,7 @@ export const isTransitionBeforePreparationEvent = ( value: any ): value is TransitionBeforePreparationEvent => value.type === TRANSITION_BEFORE_PREPARATION; export class TransitionBeforePreparationEvent extends BeforeEvent { - formData: FormData | undefined; + body: FormData | URLSearchParams | undefined; loader: () => Promise; constructor( from: URL, @@ -76,7 +76,7 @@ export class TransitionBeforePreparationEvent extends BeforeEvent { sourceElement: Element | undefined, info: any, newDocument: Document, - formData: FormData | undefined, + body: FormData | URLSearchParams | undefined, loader: (event: TransitionBeforePreparationEvent) => Promise ) { super( @@ -90,7 +90,7 @@ export class TransitionBeforePreparationEvent extends BeforeEvent { info, newDocument ); - this.formData = formData; + this.body = body; this.loader = loader.bind(this, this); Object.defineProperties(this, { formData: { enumerable: true }, @@ -145,7 +145,7 @@ export async function doPreparation( navigationType: NavigationTypeString, sourceElement: Element | undefined, info: any, - formData: FormData | undefined, + body: FormData | URLSearchParams | undefined, defaultLoader: (event: TransitionBeforePreparationEvent) => Promise ) { const event = new TransitionBeforePreparationEvent( @@ -156,7 +156,7 @@ export async function doPreparation( sourceElement, info, window.document, - formData, + body, defaultLoader ); if (document.dispatchEvent(event)) { diff --git a/packages/astro/src/transitions/router.ts b/packages/astro/src/transitions/router.ts index e710c2e1b2..ca160fa5d4 100644 --- a/packages/astro/src/transitions/router.ts +++ b/packages/astro/src/transitions/router.ts @@ -449,7 +449,7 @@ async function transition( navigationType, options.sourceElement, options.info, - options.formData, + options.body, defaultLoader ); if (prepEvent.defaultPrevented) { @@ -460,9 +460,9 @@ async function transition( async function defaultLoader(preparationEvent: TransitionBeforePreparationEvent) { const href = preparationEvent.to.href; const init: RequestInit = {}; - if (preparationEvent.formData) { + if (preparationEvent.body) { init.method = 'POST'; - init.body = preparationEvent.formData; + init.body = preparationEvent.body; } const response = await fetchHTML(href, init); // If there is a problem fetching the new page, just do an MPA navigation to it. @@ -488,7 +488,7 @@ async function transition( // Unless this was a form submission, in which case we do not want to trigger another mutation. if ( !preparationEvent.newDocument.querySelector('[name="astro-view-transitions-enabled"]') && - !preparationEvent.formData + !preparationEvent.body ) { preparationEvent.preventDefault(); return; diff --git a/packages/astro/src/transitions/types.ts b/packages/astro/src/transitions/types.ts index 0e70825e59..9ad2ecdbbc 100644 --- a/packages/astro/src/transitions/types.ts +++ b/packages/astro/src/transitions/types.ts @@ -5,6 +5,6 @@ export type Options = { history?: 'auto' | 'push' | 'replace'; info?: any; state?: any; - formData?: FormData; + body?: FormData | URLSearchParams; sourceElement?: Element; // more than HTMLElement, e.g. SVGAElement };