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

Respect forms with enctype set for view transitions

This commit is contained in:
Ken Powers 2023-12-18 19:16:04 -05:00
parent 429be8cc3e
commit 6d3e04a959
4 changed files with 22 additions and 11 deletions
packages/astro

View file

@ -117,7 +117,18 @@ const { fallback = 'animate' } = Astro.props;
url.search = params.toString(); url.search = params.toString();
action = url.toString(); action = url.toString();
} else { } 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(); ev.preventDefault();

View file

@ -66,7 +66,7 @@ export const isTransitionBeforePreparationEvent = (
value: any value: any
): value is TransitionBeforePreparationEvent => value.type === TRANSITION_BEFORE_PREPARATION; ): value is TransitionBeforePreparationEvent => value.type === TRANSITION_BEFORE_PREPARATION;
export class TransitionBeforePreparationEvent extends BeforeEvent { export class TransitionBeforePreparationEvent extends BeforeEvent {
formData: FormData | undefined; body: FormData | URLSearchParams | undefined;
loader: () => Promise<void>; loader: () => Promise<void>;
constructor( constructor(
from: URL, from: URL,
@ -76,7 +76,7 @@ export class TransitionBeforePreparationEvent extends BeforeEvent {
sourceElement: Element | undefined, sourceElement: Element | undefined,
info: any, info: any,
newDocument: Document, newDocument: Document,
formData: FormData | undefined, body: FormData | URLSearchParams | undefined,
loader: (event: TransitionBeforePreparationEvent) => Promise<void> loader: (event: TransitionBeforePreparationEvent) => Promise<void>
) { ) {
super( super(
@ -90,7 +90,7 @@ export class TransitionBeforePreparationEvent extends BeforeEvent {
info, info,
newDocument newDocument
); );
this.formData = formData; this.body = body;
this.loader = loader.bind(this, this); this.loader = loader.bind(this, this);
Object.defineProperties(this, { Object.defineProperties(this, {
formData: { enumerable: true }, formData: { enumerable: true },
@ -145,7 +145,7 @@ export async function doPreparation(
navigationType: NavigationTypeString, navigationType: NavigationTypeString,
sourceElement: Element | undefined, sourceElement: Element | undefined,
info: any, info: any,
formData: FormData | undefined, body: FormData | URLSearchParams | undefined,
defaultLoader: (event: TransitionBeforePreparationEvent) => Promise<void> defaultLoader: (event: TransitionBeforePreparationEvent) => Promise<void>
) { ) {
const event = new TransitionBeforePreparationEvent( const event = new TransitionBeforePreparationEvent(
@ -156,7 +156,7 @@ export async function doPreparation(
sourceElement, sourceElement,
info, info,
window.document, window.document,
formData, body,
defaultLoader defaultLoader
); );
if (document.dispatchEvent(event)) { if (document.dispatchEvent(event)) {

View file

@ -449,7 +449,7 @@ async function transition(
navigationType, navigationType,
options.sourceElement, options.sourceElement,
options.info, options.info,
options.formData, options.body,
defaultLoader defaultLoader
); );
if (prepEvent.defaultPrevented) { if (prepEvent.defaultPrevented) {
@ -460,9 +460,9 @@ async function transition(
async function defaultLoader(preparationEvent: TransitionBeforePreparationEvent) { async function defaultLoader(preparationEvent: TransitionBeforePreparationEvent) {
const href = preparationEvent.to.href; const href = preparationEvent.to.href;
const init: RequestInit = {}; const init: RequestInit = {};
if (preparationEvent.formData) { if (preparationEvent.body) {
init.method = 'POST'; init.method = 'POST';
init.body = preparationEvent.formData; init.body = preparationEvent.body;
} }
const response = await fetchHTML(href, init); const response = await fetchHTML(href, init);
// If there is a problem fetching the new page, just do an MPA navigation to it. // 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. // Unless this was a form submission, in which case we do not want to trigger another mutation.
if ( if (
!preparationEvent.newDocument.querySelector('[name="astro-view-transitions-enabled"]') && !preparationEvent.newDocument.querySelector('[name="astro-view-transitions-enabled"]') &&
!preparationEvent.formData !preparationEvent.body
) { ) {
preparationEvent.preventDefault(); preparationEvent.preventDefault();
return; return;

View file

@ -5,6 +5,6 @@ export type Options = {
history?: 'auto' | 'push' | 'replace'; history?: 'auto' | 'push' | 'replace';
info?: any; info?: any;
state?: any; state?: any;
formData?: FormData; body?: FormData | URLSearchParams;
sourceElement?: Element; // more than HTMLElement, e.g. SVGAElement sourceElement?: Element; // more than HTMLElement, e.g. SVGAElement
}; };