feat(plugin-form-builder): new wildcards {{*}} and {{*:table}} are now supported in email bodies and emails fall back to a global configuration value in addition to base email configuration (#8271)
Email bodies in the plugin form builder now support wildcards `{{*}}`
and `{{*:table}}` to export all the form submission data in key:value
pairs with the latter formatted as a table.
Emails also fallback to a global plugin configuration item and then to
the `defaultFromAddress` address in email transport config.
This commit is contained in:
@@ -136,6 +136,18 @@ const beforeEmail: BeforeEmail<FormSubmission> = (emailsToSend, beforeChangePara
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `defaultToEmail`
|
||||||
|
|
||||||
|
Provide a fallback for the email address to send form submissions to. If the email in form configuration does not have a to email set, this email address will be used. If this is not provided then it falls back to the `defaultFromAddress` in your [email configuration](https://payloadcms.com/docs/beta/email/overview).
|
||||||
|
|
||||||
|
```ts
|
||||||
|
// payload.config.ts
|
||||||
|
formBuilder({
|
||||||
|
// ...
|
||||||
|
defaultToEmail: 'test@example.com',
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
### `formOverrides`
|
### `formOverrides`
|
||||||
|
|
||||||
Override anything on the `forms` collection by sending a [Payload Collection Config](https://payloadcms.com/docs/configuration/collections) to the `formOverrides` property.
|
Override anything on the `forms` collection by sending a [Payload Collection Config](https://payloadcms.com/docs/configuration/collections) to the `formOverrides` property.
|
||||||
@@ -396,7 +408,19 @@ formBuilder({
|
|||||||
|
|
||||||
## Email
|
## Email
|
||||||
|
|
||||||
This plugin relies on the [email configuration](https://payloadcms.com/docs/email/overview) defined in your `payload.init()`. It will read from your config and attempt to send your emails using the credentials provided.
|
This plugin relies on the [email configuration](https://payloadcms.com/docs/beta/email/overview) defined in your payload configuration. It will read from your config and attempt to send your emails using the credentials provided.
|
||||||
|
|
||||||
|
### Email formatting
|
||||||
|
|
||||||
|
The email contents supports rich text which will be serialised to HTML on the server before being sent. By default it reads the global configuration of your rich text editor.
|
||||||
|
|
||||||
|
The email subject and body supports inserting dynamic fields from the form submission data using the `{{field_name}}` syntax. For example, if you have a field called `name` in your form, you can include this in the email body like so:
|
||||||
|
|
||||||
|
```html
|
||||||
|
Thank you for your submission, {{name}}!
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also use `{{*}}` as a wildcard to output all the data in a key:value format and `{{*:table}}` to output all the data in a table format.
|
||||||
|
|
||||||
## TypeScript
|
## TypeScript
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ export const sendEmail = async (
|
|||||||
|
|
||||||
const { form: formID, submissionData } = data || {}
|
const { form: formID, submissionData } = data || {}
|
||||||
|
|
||||||
const { beforeEmail, formOverrides } = formConfig || {}
|
const { beforeEmail, defaultToEmail, formOverrides } = formConfig || {}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const form = await payload.findByID({
|
const form = await payload.findByID({
|
||||||
@@ -41,12 +41,14 @@ export const sendEmail = async (
|
|||||||
bcc: emailBCC,
|
bcc: emailBCC,
|
||||||
cc: emailCC,
|
cc: emailCC,
|
||||||
emailFrom,
|
emailFrom,
|
||||||
emailTo,
|
emailTo: emailToFromConfig,
|
||||||
message,
|
message,
|
||||||
replyTo: emailReplyTo,
|
replyTo: emailReplyTo,
|
||||||
subject,
|
subject,
|
||||||
} = email
|
} = email
|
||||||
|
|
||||||
|
const emailTo = emailToFromConfig || defaultToEmail || payload.email.defaultFromAddress
|
||||||
|
|
||||||
const to = replaceDoubleCurlys(emailTo, submissionData)
|
const to = replaceDoubleCurlys(emailTo, submissionData)
|
||||||
const cc = emailCC ? replaceDoubleCurlys(emailCC, submissionData) : ''
|
const cc = emailCC ? replaceDoubleCurlys(emailCC, submissionData) : ''
|
||||||
const bcc = emailBCC ? replaceDoubleCurlys(emailBCC, submissionData) : ''
|
const bcc = emailBCC ? replaceDoubleCurlys(emailBCC, submissionData) : ''
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ export const generateFormCollection = (formConfig: FormBuilderPluginConfig): Col
|
|||||||
type: 'array',
|
type: 'array',
|
||||||
admin: {
|
admin: {
|
||||||
description:
|
description:
|
||||||
"Send custom emails when the form submits. Use comma separated lists to send the same email to multiple recipients. To reference a value from this form, wrap that field's name with double curly brackets, i.e. {{firstName}}.",
|
"Send custom emails when the form submits. Use comma separated lists to send the same email to multiple recipients. To reference a value from this form, wrap that field's name with double curly brackets, i.e. {{firstName}}. You can use a wildcard {{*}} to output all data and {{*:table}} to format it as an HTML table in the email.",
|
||||||
},
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -53,6 +53,11 @@ export type FieldsOverride = (args: { defaultFields: Field[] }) => Field[]
|
|||||||
|
|
||||||
export type FormBuilderPluginConfig = {
|
export type FormBuilderPluginConfig = {
|
||||||
beforeEmail?: BeforeEmail
|
beforeEmail?: BeforeEmail
|
||||||
|
/**
|
||||||
|
* Set a default email address to send form submissions to if no email is provided in the form configuration
|
||||||
|
* Falls back to the defaultFromAddress in the email configuration
|
||||||
|
*/
|
||||||
|
defaultToEmail?: string
|
||||||
fields?: FieldsConfig
|
fields?: FieldsConfig
|
||||||
formOverrides?: { fields?: FieldsOverride } & Partial<Omit<CollectionConfig, 'fields'>>
|
formOverrides?: { fields?: FieldsOverride } & Partial<Omit<CollectionConfig, 'fields'>>
|
||||||
formSubmissionOverrides?: { fields?: FieldsOverride } & Partial<Omit<CollectionConfig, 'fields'>>
|
formSubmissionOverrides?: { fields?: FieldsOverride } & Partial<Omit<CollectionConfig, 'fields'>>
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
export function keyValuePairToHtmlTable(obj: { [key: string]: string }): string {
|
||||||
|
let htmlTable = '<table>'
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(obj)) {
|
||||||
|
htmlTable += `<tr><td>${key}</td><td>${value}</td></tr>`
|
||||||
|
}
|
||||||
|
|
||||||
|
htmlTable += '</table>'
|
||||||
|
return htmlTable
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { keyValuePairToHtmlTable } from './keyValuePairToHtmlTable.js'
|
||||||
|
|
||||||
interface EmailVariable {
|
interface EmailVariable {
|
||||||
field: string
|
field: string
|
||||||
value: string
|
value: string
|
||||||
@@ -8,11 +10,27 @@ type EmailVariables = EmailVariable[]
|
|||||||
export const replaceDoubleCurlys = (str: string, variables?: EmailVariables): string => {
|
export const replaceDoubleCurlys = (str: string, variables?: EmailVariables): string => {
|
||||||
const regex = /\{\{(.+?)\}\}/g
|
const regex = /\{\{(.+?)\}\}/g
|
||||||
if (str && variables) {
|
if (str && variables) {
|
||||||
return str.replace(regex, (_, variable) => {
|
return str.replace(regex, (_, variable: string) => {
|
||||||
const foundVariable = variables.find(({ field: fieldName }) => variable === fieldName)
|
if (variable.includes('*')) {
|
||||||
if (foundVariable) {
|
if (variable === '*') {
|
||||||
return foundVariable.value
|
return variables.map(({ field, value }) => `${field} : ${value}`).join(' <br /> ')
|
||||||
|
} else if (variable === '*:table') {
|
||||||
|
return keyValuePairToHtmlTable(
|
||||||
|
variables.reduce((acc, { field, value }) => {
|
||||||
|
acc[field] = value
|
||||||
|
return acc
|
||||||
|
}, {}),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const foundVariable = variables.find(({ field: fieldName }) => {
|
||||||
|
return variable === fieldName
|
||||||
|
})
|
||||||
|
if (foundVariable) {
|
||||||
|
return foundVariable.value
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return variable
|
return variable
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,9 @@ export const serializeSlate = (children?: Node[], submissionData?: any): string
|
|||||||
children
|
children
|
||||||
?.map((node: Node) => {
|
?.map((node: Node) => {
|
||||||
if (isTextNode(node)) {
|
if (isTextNode(node)) {
|
||||||
let text = `<span>${escapeHTML(replaceDoubleCurlys(node.text, submissionData))}</span>`
|
let text = node.text.includes('{{*')
|
||||||
|
? replaceDoubleCurlys(node.text, submissionData)
|
||||||
|
: `<span>${escapeHTML(replaceDoubleCurlys(node.text, submissionData))}</span>`
|
||||||
|
|
||||||
if (node.bold) {
|
if (node.bold) {
|
||||||
text = `
|
text = `
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ const dirname = path.dirname(filename)
|
|||||||
import type { BeforeEmail } from '@payloadcms/plugin-form-builder/types'
|
import type { BeforeEmail } from '@payloadcms/plugin-form-builder/types'
|
||||||
import type { Block } from 'payload'
|
import type { Block } from 'payload'
|
||||||
|
|
||||||
|
//import { nodemailerAdapter } from '@payloadcms/email-nodemailer'
|
||||||
import { formBuilderPlugin, fields as formFields } from '@payloadcms/plugin-form-builder'
|
import { formBuilderPlugin, fields as formFields } from '@payloadcms/plugin-form-builder'
|
||||||
import { slateEditor } from '@payloadcms/richtext-slate'
|
import { lexicalEditor } from '@payloadcms/richtext-lexical'
|
||||||
|
|
||||||
import type { FormSubmission } from './payload-types.js'
|
import type { FormSubmission } from './payload-types.js'
|
||||||
|
|
||||||
@@ -41,7 +42,7 @@ export default buildConfigWithDefaults({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
collections: [Pages, Users],
|
collections: [Pages, Users],
|
||||||
editor: slateEditor({}),
|
editor: lexicalEditor({}),
|
||||||
localization: {
|
localization: {
|
||||||
defaultLocale: 'en',
|
defaultLocale: 'en',
|
||||||
fallback: true,
|
fallback: true,
|
||||||
@@ -58,9 +59,11 @@ export default buildConfigWithDefaults({
|
|||||||
|
|
||||||
await seed(payload)
|
await seed(payload)
|
||||||
},
|
},
|
||||||
|
//email: nodemailerAdapter(),
|
||||||
plugins: [
|
plugins: [
|
||||||
formBuilderPlugin({
|
formBuilderPlugin({
|
||||||
// handlePayment: handleFormPayments,
|
// handlePayment: handleFormPayments,
|
||||||
|
//defaultToEmail: 'devs@payloadcms.com',
|
||||||
fields: {
|
fields: {
|
||||||
colorField,
|
colorField,
|
||||||
payment: true,
|
payment: true,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import type { Form } from './payload-types.js'
|
|||||||
import { serializeLexical } from '../../packages/plugin-form-builder/src/utilities/lexical/serializeLexical.js'
|
import { serializeLexical } from '../../packages/plugin-form-builder/src/utilities/lexical/serializeLexical.js'
|
||||||
import { serializeSlate } from '../../packages/plugin-form-builder/src/utilities/slate/serializeSlate.js'
|
import { serializeSlate } from '../../packages/plugin-form-builder/src/utilities/slate/serializeSlate.js'
|
||||||
import { initPayloadInt } from '../helpers/initPayloadInt.js'
|
import { initPayloadInt } from '../helpers/initPayloadInt.js'
|
||||||
import { formSubmissionsSlug, formsSlug } from './shared.js'
|
import { formsSlug, formSubmissionsSlug } from './shared.js'
|
||||||
|
|
||||||
let payload: Payload
|
let payload: Payload
|
||||||
let form: Form
|
let form: Form
|
||||||
@@ -22,12 +22,38 @@ describe('@payloadcms/plugin-form-builder', () => {
|
|||||||
;({ payload } = await initPayloadInt(dirname))
|
;({ payload } = await initPayloadInt(dirname))
|
||||||
|
|
||||||
const formConfig: Omit<Form, 'createdAt' | 'id' | 'updatedAt'> = {
|
const formConfig: Omit<Form, 'createdAt' | 'id' | 'updatedAt'> = {
|
||||||
confirmationMessage: [
|
confirmationType: 'message',
|
||||||
{
|
confirmationMessage: {
|
||||||
type: 'text',
|
root: {
|
||||||
text: 'Confirmed.',
|
children: [
|
||||||
|
{
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
detail: 0,
|
||||||
|
format: 0,
|
||||||
|
mode: 'normal',
|
||||||
|
style: '',
|
||||||
|
text: 'Confirmed.',
|
||||||
|
type: 'text',
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
direction: 'ltr',
|
||||||
|
format: '',
|
||||||
|
indent: 0,
|
||||||
|
type: 'paragraph',
|
||||||
|
version: 1,
|
||||||
|
textFormat: 0,
|
||||||
|
textStyle: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
direction: 'ltr',
|
||||||
|
format: '',
|
||||||
|
indent: 0,
|
||||||
|
type: 'root',
|
||||||
|
version: 1,
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'name',
|
name: 'name',
|
||||||
@@ -64,12 +90,38 @@ describe('@payloadcms/plugin-form-builder', () => {
|
|||||||
describe('form building', () => {
|
describe('form building', () => {
|
||||||
it('can create a simple form', async () => {
|
it('can create a simple form', async () => {
|
||||||
const formConfig: Omit<Form, 'createdAt' | 'id' | 'updatedAt'> = {
|
const formConfig: Omit<Form, 'createdAt' | 'id' | 'updatedAt'> = {
|
||||||
confirmationMessage: [
|
confirmationType: 'message',
|
||||||
{
|
confirmationMessage: {
|
||||||
type: 'text',
|
root: {
|
||||||
text: 'Confirmed.',
|
children: [
|
||||||
|
{
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
detail: 0,
|
||||||
|
format: 0,
|
||||||
|
mode: 'normal',
|
||||||
|
style: '',
|
||||||
|
text: 'Confirmed.',
|
||||||
|
type: 'text',
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
direction: 'ltr',
|
||||||
|
format: '',
|
||||||
|
indent: 0,
|
||||||
|
type: 'paragraph',
|
||||||
|
version: 1,
|
||||||
|
textFormat: 0,
|
||||||
|
textStyle: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
direction: 'ltr',
|
||||||
|
format: '',
|
||||||
|
indent: 0,
|
||||||
|
type: 'root',
|
||||||
|
version: 1,
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'name',
|
name: 'name',
|
||||||
@@ -91,12 +143,38 @@ describe('@payloadcms/plugin-form-builder', () => {
|
|||||||
|
|
||||||
it('can use form overrides', async () => {
|
it('can use form overrides', async () => {
|
||||||
const formConfig: Omit<Form, 'createdAt' | 'id' | 'updatedAt'> = {
|
const formConfig: Omit<Form, 'createdAt' | 'id' | 'updatedAt'> = {
|
||||||
confirmationMessage: [
|
confirmationType: 'message',
|
||||||
{
|
confirmationMessage: {
|
||||||
type: 'text',
|
root: {
|
||||||
text: 'Confirmed.',
|
children: [
|
||||||
|
{
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
detail: 0,
|
||||||
|
format: 0,
|
||||||
|
mode: 'normal',
|
||||||
|
style: '',
|
||||||
|
text: 'Confirmed.',
|
||||||
|
type: 'text',
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
direction: 'ltr',
|
||||||
|
format: '',
|
||||||
|
indent: 0,
|
||||||
|
type: 'paragraph',
|
||||||
|
version: 1,
|
||||||
|
textFormat: 0,
|
||||||
|
textStyle: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
direction: 'ltr',
|
||||||
|
format: '',
|
||||||
|
indent: 0,
|
||||||
|
type: 'root',
|
||||||
|
version: 1,
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
custom: 'custom',
|
custom: 'custom',
|
||||||
title: 'Test Form',
|
title: 'Test Form',
|
||||||
}
|
}
|
||||||
@@ -152,65 +230,108 @@ describe('@payloadcms/plugin-form-builder', () => {
|
|||||||
await expect(req).rejects.toThrow(ValidationError)
|
await expect(req).rejects.toThrow(ValidationError)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('replaces curly braces with data when using slate serializer', () => {
|
describe('replaces curly braces', () => {
|
||||||
const mockName = 'Test Submission'
|
describe('slate serializer', () => {
|
||||||
const mockEmail = 'dev@payloadcms.com'
|
it('specific field names', () => {
|
||||||
|
const mockName = 'Test Submission'
|
||||||
|
const mockEmail = 'dev@payloadcms.com'
|
||||||
|
|
||||||
const serializedEmail = serializeSlate(
|
const serializedEmail = serializeSlate(
|
||||||
[
|
[
|
||||||
{ text: 'Welcome {{name}}. Here is a dynamic ' },
|
{ text: 'Welcome {{name}}. Here is a dynamic ' },
|
||||||
{
|
|
||||||
type: 'link',
|
|
||||||
children: [
|
|
||||||
{
|
{
|
||||||
text: 'link',
|
type: 'link',
|
||||||
},
|
|
||||||
],
|
|
||||||
url: 'www.test.com?email={{email}}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{ field: 'name', value: mockName },
|
|
||||||
{ field: 'email', value: mockEmail },
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(serializedEmail).toContain(mockName)
|
|
||||||
expect(serializedEmail).toContain(mockEmail)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('replaces curly braces with data when using lexical serializer', async () => {
|
|
||||||
const mockName = 'Test Submission'
|
|
||||||
const mockEmail = 'dev@payloadcms.com'
|
|
||||||
|
|
||||||
const serializedEmail = await serializeLexical(
|
|
||||||
{
|
|
||||||
root: {
|
|
||||||
type: 'root',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
type: 'paragraph',
|
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: 'text',
|
text: 'link',
|
||||||
detail: 0,
|
|
||||||
format: 0,
|
|
||||||
mode: 'normal',
|
|
||||||
style: '',
|
|
||||||
text: 'Name: {{name}}',
|
|
||||||
version: 1,
|
|
||||||
},
|
},
|
||||||
|
],
|
||||||
|
url: 'www.test.com?email={{email}}',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[
|
||||||
|
{ field: 'name', value: mockName },
|
||||||
|
{ field: 'email', value: mockEmail },
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(serializedEmail).toContain(mockName)
|
||||||
|
expect(serializedEmail).toContain(mockEmail)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('wildcard "{{*}}"', () => {
|
||||||
|
const mockName = 'Test Submission'
|
||||||
|
const mockEmail = 'dev@payloadcms.com'
|
||||||
|
|
||||||
|
const serializedEmail = serializeSlate(
|
||||||
|
[{ text: '{{*}}' }],
|
||||||
|
[
|
||||||
|
{ field: 'name', value: mockName },
|
||||||
|
{ field: 'email', value: mockEmail },
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(serializedEmail).toContain(`name : ${mockName}`)
|
||||||
|
expect(serializedEmail).toContain(`email : ${mockEmail}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('wildcard with table formatting "{{*:table}}"', () => {
|
||||||
|
const mockName = 'Test Submission'
|
||||||
|
const mockEmail = 'dev@payloadcms.com'
|
||||||
|
|
||||||
|
const serializedEmail = serializeSlate(
|
||||||
|
[{ text: '{{*:table}}' }],
|
||||||
|
[
|
||||||
|
{ field: 'name', value: mockName },
|
||||||
|
{ field: 'email', value: mockEmail },
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(serializedEmail).toContain(`<table>`)
|
||||||
|
expect(serializedEmail).toContain(`<tr><td>name</td><td>${mockName}</td></tr>`)
|
||||||
|
expect(serializedEmail).toContain(`<tr><td>email</td><td>${mockEmail}</td></tr>`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('lexical serializer', () => {
|
||||||
|
it('specific field names', async () => {
|
||||||
|
const mockName = 'Test Submission'
|
||||||
|
const mockEmail = 'dev@payloadcms.com'
|
||||||
|
|
||||||
|
const serializedEmail = await serializeLexical(
|
||||||
|
{
|
||||||
|
root: {
|
||||||
|
type: 'root',
|
||||||
|
children: [
|
||||||
{
|
{
|
||||||
type: 'linebreak',
|
type: 'paragraph',
|
||||||
version: 1,
|
children: [
|
||||||
},
|
{
|
||||||
{
|
type: 'text',
|
||||||
type: 'text',
|
detail: 0,
|
||||||
detail: 0,
|
format: 0,
|
||||||
format: 0,
|
mode: 'normal',
|
||||||
mode: 'normal',
|
style: '',
|
||||||
style: '',
|
text: 'Name: {{name}}',
|
||||||
text: 'Email: {{email}}',
|
version: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'linebreak',
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
detail: 0,
|
||||||
|
format: 0,
|
||||||
|
mode: 'normal',
|
||||||
|
style: '',
|
||||||
|
text: 'Email: {{email}}',
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
direction: 'ltr',
|
||||||
|
format: '',
|
||||||
|
indent: 0,
|
||||||
version: 1,
|
version: 1,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@@ -219,21 +340,106 @@ describe('@payloadcms/plugin-form-builder', () => {
|
|||||||
indent: 0,
|
indent: 0,
|
||||||
version: 1,
|
version: 1,
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
[
|
||||||
|
{ field: 'name', value: mockName },
|
||||||
|
{ field: 'email', value: mockEmail },
|
||||||
],
|
],
|
||||||
direction: 'ltr',
|
)
|
||||||
format: '',
|
|
||||||
indent: 0,
|
|
||||||
version: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[
|
|
||||||
{ field: 'name', value: mockName },
|
|
||||||
{ field: 'email', value: mockEmail },
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
expect(serializedEmail).toContain(`Name: ${mockName}`)
|
expect(serializedEmail).toContain(`Name: ${mockName}`)
|
||||||
expect(serializedEmail).toContain(`Email: ${mockEmail}`)
|
expect(serializedEmail).toContain(`Email: ${mockEmail}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('wildcard "{{*}}"', async () => {
|
||||||
|
const mockName = 'Test Submission'
|
||||||
|
const mockEmail = 'dev@payloadcms.com'
|
||||||
|
|
||||||
|
const serializedEmail = await serializeLexical(
|
||||||
|
{
|
||||||
|
root: {
|
||||||
|
type: 'root',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
detail: 0,
|
||||||
|
format: 0,
|
||||||
|
mode: 'normal',
|
||||||
|
style: '',
|
||||||
|
text: '{{*}}',
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
direction: 'ltr',
|
||||||
|
format: '',
|
||||||
|
indent: 0,
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
direction: 'ltr',
|
||||||
|
format: '',
|
||||||
|
indent: 0,
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[
|
||||||
|
{ field: 'name', value: mockName },
|
||||||
|
{ field: 'email', value: mockEmail },
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(serializedEmail).toContain(`name : ${mockName}`)
|
||||||
|
expect(serializedEmail).toContain(`email : ${mockEmail}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('wildcard with table formatting "{{*:table}}"', async () => {
|
||||||
|
const mockName = 'Test Submission'
|
||||||
|
const mockEmail = 'dev@payloadcms.com'
|
||||||
|
|
||||||
|
const serializedEmail = await serializeLexical(
|
||||||
|
{
|
||||||
|
root: {
|
||||||
|
type: 'root',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'paragraph',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: 'text',
|
||||||
|
detail: 0,
|
||||||
|
format: 0,
|
||||||
|
mode: 'normal',
|
||||||
|
style: '',
|
||||||
|
text: '{{*:table}}',
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
direction: 'ltr',
|
||||||
|
format: '',
|
||||||
|
indent: 0,
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
direction: 'ltr',
|
||||||
|
format: '',
|
||||||
|
indent: 0,
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[
|
||||||
|
{ field: 'name', value: mockName },
|
||||||
|
{ field: 'email', value: mockEmail },
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(serializedEmail).toContain(`<table>`)
|
||||||
|
expect(serializedEmail).toContain(`<tr><td>name</td><td>${mockName}</td></tr>`)
|
||||||
|
expect(serializedEmail).toContain(`<tr><td>email</td><td>${mockEmail}</td></tr>`)
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -95,11 +95,21 @@ export interface Form {
|
|||||||
blockType: 'email';
|
blockType: 'email';
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
message?:
|
message?: {
|
||||||
| {
|
root: {
|
||||||
|
type: string;
|
||||||
|
children: {
|
||||||
|
type: string;
|
||||||
|
version: number;
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
}[]
|
}[];
|
||||||
| null;
|
direction: ('ltr' | 'rtl') | null;
|
||||||
|
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||||
|
indent: number;
|
||||||
|
version: number;
|
||||||
|
};
|
||||||
|
[k: string]: unknown;
|
||||||
|
} | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'message';
|
blockType: 'message';
|
||||||
@@ -191,11 +201,21 @@ export interface Form {
|
|||||||
| null;
|
| null;
|
||||||
submitButtonLabel?: string | null;
|
submitButtonLabel?: string | null;
|
||||||
confirmationType?: ('message' | 'redirect') | null;
|
confirmationType?: ('message' | 'redirect') | null;
|
||||||
confirmationMessage?:
|
confirmationMessage?: {
|
||||||
| {
|
root: {
|
||||||
|
type: string;
|
||||||
|
children: {
|
||||||
|
type: string;
|
||||||
|
version: number;
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
}[]
|
}[];
|
||||||
| null;
|
direction: ('ltr' | 'rtl') | null;
|
||||||
|
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||||
|
indent: number;
|
||||||
|
version: number;
|
||||||
|
};
|
||||||
|
[k: string]: unknown;
|
||||||
|
} | null;
|
||||||
redirect?: {
|
redirect?: {
|
||||||
type?: ('reference' | 'custom') | null;
|
type?: ('reference' | 'custom') | null;
|
||||||
reference?: {
|
reference?: {
|
||||||
@@ -212,11 +232,21 @@ export interface Form {
|
|||||||
replyTo?: string | null;
|
replyTo?: string | null;
|
||||||
emailFrom?: string | null;
|
emailFrom?: string | null;
|
||||||
subject: string;
|
subject: string;
|
||||||
message?:
|
message?: {
|
||||||
| {
|
root: {
|
||||||
|
type: string;
|
||||||
|
children: {
|
||||||
|
type: string;
|
||||||
|
version: number;
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
}[]
|
}[];
|
||||||
| null;
|
direction: ('ltr' | 'rtl') | null;
|
||||||
|
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
||||||
|
indent: number;
|
||||||
|
version: number;
|
||||||
|
};
|
||||||
|
[k: string]: unknown;
|
||||||
|
} | null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { Payload, PayloadRequest } from 'payload'
|
import type { Payload, PayloadRequest } from 'payload'
|
||||||
|
|
||||||
import { formSubmissionsSlug, formsSlug, pagesSlug } from '../shared.js'
|
import { formsSlug, formSubmissionsSlug, pagesSlug } from '../shared.js'
|
||||||
|
|
||||||
export const seed = async (payload: Payload): Promise<boolean> => {
|
export const seed = async (payload: Payload): Promise<boolean> => {
|
||||||
payload.logger.info('Seeding data...')
|
payload.logger.info('Seeding data...')
|
||||||
@@ -28,12 +28,38 @@ export const seed = async (payload: Payload): Promise<boolean> => {
|
|||||||
const { id: formID } = await payload.create({
|
const { id: formID } = await payload.create({
|
||||||
collection: formsSlug,
|
collection: formsSlug,
|
||||||
data: {
|
data: {
|
||||||
confirmationMessage: [
|
confirmationType: 'message',
|
||||||
{
|
confirmationMessage: {
|
||||||
type: 'paragraph',
|
root: {
|
||||||
text: 'Confirmed',
|
children: [
|
||||||
|
{
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
detail: 0,
|
||||||
|
format: 0,
|
||||||
|
mode: 'normal',
|
||||||
|
style: '',
|
||||||
|
text: 'Confirmed',
|
||||||
|
type: 'text',
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
direction: 'ltr',
|
||||||
|
format: '',
|
||||||
|
indent: 0,
|
||||||
|
type: 'paragraph',
|
||||||
|
version: 1,
|
||||||
|
textFormat: 0,
|
||||||
|
textStyle: '',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
direction: 'ltr',
|
||||||
|
format: '',
|
||||||
|
indent: 0,
|
||||||
|
type: 'root',
|
||||||
|
version: 1,
|
||||||
},
|
},
|
||||||
],
|
},
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'name',
|
name: 'name',
|
||||||
|
|||||||
Reference in New Issue
Block a user