ContactForm relay wiring (recipient, thank-you, honeypot, marker) #2

Merged
jknapp merged 3 commits from contact-form-relay into main 2026-07-07 19:35:28 +00:00
2 changed files with 57 additions and 1 deletions
Showing only changes of commit 66117d375e - Show all commits
@@ -0,0 +1,24 @@
import { describe, test, expect } from 'vitest';
import { ContactForm } from './ContactForm';
const toHtml = (ContactForm as any).toHtml;
describe('ContactForm.toHtml relay wiring', () => {
test('with recipientEmail: emits marker, placeholder action, honeypot', () => {
const { html } = toHtml({ recipientEmail: 'a@b.com', thankYouUrl: '/thx', fields: [] }, '');
expect(html).toMatch(/<!--WHP-FORM id="F[0-9a-z]+" recipient="a@b.com" thankyou="\/thx"-->/);
expect(html).toMatch(/action="__WHP_FORM_ACTION__F[0-9a-z]+__"/);
expect(html).toContain('method="POST"');
expect(html).toContain('name="_gotcha"');
// marker id and action id match
const mid = html.match(/id="(F[0-9a-z]+)"/)![1];
expect(html).toContain(`__WHP_FORM_ACTION__${mid}__`);
});
test('without recipientEmail: no marker, falls back to formAction', () => {
const { html } = toHtml({ formAction: '/legacy', fields: [] }, '');
expect(html).not.toContain('WHP-FORM');
expect(html).toContain('action="/legacy"');
expect(html).not.toContain('_gotcha');
});
});
+33 -1
View File
@@ -21,6 +21,8 @@ interface ContactFormProps {
labelColor?: string;
inputBg?: string;
inputBorder?: string;
recipientEmail?: string;
thankYouUrl?: string;
}
const defaultFields: ContactFormField[] = [
@@ -187,6 +189,23 @@ const ContactFormSettings: React.FC = () => {
/>
</div>
{/* Relay recipient */}
<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: ContactFormProps) => { p.recipientEmail = e.target.value; })}
placeholder="you@example.com" style={{ ...inputStyle, padding: '4px 8px', 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.
</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: ContactFormProps) => { p.thankYouUrl = e.target.value; })}
placeholder="/thank-you (blank = hosted page)" style={{ ...inputStyle, padding: '4px 8px', fontSize: 12 }} />
</div>
{/* Success Message */}
<div>
<label style={{ fontSize: 11, color: '#a1a1aa', display: 'block', marginBottom: 6 }}>Success Message</label>
@@ -358,6 +377,8 @@ ContactForm.craft = {
labelColor: '#374151',
inputBg: '#ffffff',
inputBorder: '#d1d5db',
recipientEmail: '',
thankYouUrl: '',
},
rules: {
canDrag: () => true,
@@ -414,8 +435,19 @@ ContactForm.craft = {
alignSelf: 'flex-start',
});
const useRelay = !!props.recipientEmail;
const fid = 'F' + Math.random().toString(36).slice(2, 8);
const actionAttr = useRelay ? `__WHP_FORM_ACTION__${fid}__` : esc(props.formAction || '#');
const honeypot = useRelay
? `<input type="text" name="_gotcha" tabindex="-1" autocomplete="off" style="position:absolute;left:-9999px" aria-hidden="true">`
: '';
const marker = useRelay
? `<!--WHP-FORM id="${fid}" recipient="${esc(props.recipientEmail)}" thankyou="${esc(props.thankYouUrl || '')}"-->`
: '';
return {
html: `<form action="${esc(props.formAction || '#')}" method="POST"${formStyle ? ` style="${formStyle}"` : ''}>
html: `${marker}<form action="${actionAttr}" method="POST"${formStyle ? ` style="${formStyle}"` : ''}>
${honeypot}
${fieldsHtml}
<button type="submit"${btnStyle ? ` style="${btnStyle}"` : ''}>${esc(props.submitText || 'Send Message')}</button>
</form>`,