site-builder: relay wiring on FormContainer (template forms) + shared helper

The recipient field was only on the ContactForm block; templates build forms
from FormContainer + InputField, so template-based contact forms had no way to
set a target address. Add 'Send submissions to' + thank-you fields to
FormContainer, and extract the marker/placeholder/honeypot into a shared
form-relay-wiring helper so ContactForm and FormContainer can't drift.
This commit is contained in:
2026-07-07 13:35:18 -07:00
parent 6b9c258d26
commit b9c5d3dd1c
4 changed files with 109 additions and 10 deletions
+34 -1
View File
@@ -2,10 +2,13 @@ import React, { CSSProperties } from 'react';
import { useNode, Element, UserComponent } from '@craftjs/core';
import { Container } from '../layout/Container';
import { cssPropsToString } from '../../utils/style-helpers';
import { relayFormWiring } from '../../utils/form-relay-wiring';
interface FormContainerProps {
action?: string;
method?: 'GET' | 'POST';
recipientEmail?: string;
thankYouUrl?: string;
style?: CSSProperties;
children?: React.ReactNode;
}
@@ -51,6 +54,31 @@ const FormContainerSettings: React.FC = () => {
return (
<div style={{ padding: '12px', display: 'flex', flexDirection: 'column', gap: '14px' }}>
<div>
<label style={{ fontSize: 11, color: '#a1a1aa', display: 'block', marginBottom: 6 }}>Send submissions to (email)</label>
<input
type="email"
value={props.recipientEmail || ''}
onChange={(e) => setProp((p: FormContainerProps) => { p.recipientEmail = e.target.value; })}
placeholder="you@example.com"
style={{ width: '100%', padding: '4px 8px', background: '#27272a', color: '#e4e4e7', border: '1px solid #3f3f46', borderRadius: 4, fontSize: 12 }}
/>
<p style={{ fontSize: 10, color: '#71717a', margin: '4px 0 0' }}>
Delivered via the site's contact-form relay. Requires the relay to be enabled on this server. Leave blank to use the Form Action URL below instead.
</p>
</div>
<div>
<label style={{ fontSize: 11, color: '#a1a1aa', display: 'block', marginBottom: 6 }}>Thank-you page URL (optional)</label>
<input
type="text"
value={props.thankYouUrl || ''}
onChange={(e) => setProp((p: FormContainerProps) => { p.thankYouUrl = e.target.value; })}
placeholder="/thank-you (blank = hosted page)"
style={{ width: '100%', padding: '4px 8px', background: '#27272a', color: '#e4e4e7', border: '1px solid #3f3f46', borderRadius: 4, fontSize: 12 }}
/>
</div>
<div>
<label style={{ fontSize: 11, color: '#a1a1aa', display: 'block', marginBottom: 6 }}>Form Action URL</label>
<input
@@ -110,6 +138,8 @@ FormContainer.craft = {
props: {
action: '#',
method: 'POST',
recipientEmail: '',
thankYouUrl: '',
style: {
padding: '24px',
backgroundColor: '#ffffff',
@@ -134,7 +164,10 @@ FormContainer.craft = {
padding: '24px',
...props.style,
});
const { useRelay, marker, actionAttr, honeypot } = relayFormWiring(props.recipientEmail, props.thankYouUrl, props.action);
const method = useRelay ? 'POST' : (props.method || 'POST'); // relay requires POST
const body = honeypot + childrenHtml; // honeypot as first child
return {
html: `<form action="${props.action || '#'}" method="${props.method || 'POST'}"${styleStr ? ` style="${styleStr}"` : ''}>${childrenHtml}</form>`,
html: `${marker}<form action="${actionAttr}" method="${method}"${styleStr ? ` style="${styleStr}"` : ''}>${body}</form>`,
};
};