feat(plugin-form-builder): add new date field (#12416)

Adds a new date field to take submission values for.

It can help form serialisers render the right input for this kind of
field as the submissions themselves don't do any validation right now.

Disabled by default as to not cause any conflicts with existing projects
potentially inserting their own date blocks.

Can be enabled like this

```ts
formBuilderPlugin({
   fields: {
     date: true
   }
})
```
This commit is contained in:
Paul
2025-05-21 10:34:21 -07:00
committed by GitHub
parent 230128b92e
commit 4dfb2d24bb
7 changed files with 269 additions and 4 deletions

View File

@@ -74,6 +74,26 @@ export default buildConfigWithDefaults({
singular: 'Custom Text Field',
},
},
date: {
...formFields.date,
fields: [
...(formFields.date && 'fields' in formFields.date
? formFields.date.fields.map((field) => {
if ('name' in field && field.name === 'defaultValue') {
return {
...field,
timezone: true,
admin: {
...field.admin,
description: 'This is a date field',
},
} as Field
}
return field
})
: []),
],
},
// payment: {
// paymentProcessor: {
// options: [

View File

@@ -46,7 +46,7 @@ test.describe('Form Builder Plugin', () => {
timeout: POLL_TOPASS_TIMEOUT,
})
const titleCell = page.locator('.row-1 .cell-title a')
const titleCell = page.locator('.row-2 .cell-title a')
await expect(titleCell).toHaveText('Contact Form')
const href = await titleCell.getAttribute('href')
@@ -92,10 +92,10 @@ test.describe('Form Builder Plugin', () => {
timeout: POLL_TOPASS_TIMEOUT,
})
const idCell = page.locator('.row-1 .cell-id a')
const href = await idCell.getAttribute('href')
const firstSubmissionCell = page.locator('.table .cell-id a').last()
const href = await firstSubmissionCell.getAttribute('href')
await idCell.click()
await firstSubmissionCell.click()
await expect(() => expect(page.url()).toContain(href)).toPass({
timeout: POLL_TOPASS_TIMEOUT,
})
@@ -109,6 +109,11 @@ test.describe('Form Builder Plugin', () => {
test('can create form submission', async () => {
const { docs } = await payload.find({
collection: 'forms',
where: {
title: {
contains: 'Contact',
},
},
})
const createdSubmission = await payload.create({
@@ -137,5 +142,49 @@ test.describe('Form Builder Plugin', () => {
await expect(page.locator('#field-submissionData__0__value')).toHaveValue('New tester')
await expect(page.locator('#field-submissionData__1__value')).toHaveValue('new@example.com')
})
test('can create form submission - with date field', async () => {
const { docs } = await payload.find({
collection: 'forms',
where: {
title: {
contains: 'Booking',
},
},
})
const createdSubmission = await payload.create({
collection: 'form-submissions',
data: {
form: docs[0].id,
submissionData: [
{
field: 'name',
value: 'New tester',
},
{
field: 'email',
value: 'new@example.com',
},
{
field: 'date',
value: '2025-10-01T00:00:00.000Z',
},
],
},
})
await page.goto(submissionsUrl.edit(createdSubmission.id))
await expect(() => expect(page.url()).toContain(createdSubmission.id)).toPass({
timeout: POLL_TOPASS_TIMEOUT,
})
await expect(page.locator('#field-submissionData__0__value')).toHaveValue('New tester')
await expect(page.locator('#field-submissionData__1__value')).toHaveValue('new@example.com')
await expect(page.locator('#field-submissionData__2__value')).toHaveValue(
'2025-10-01T00:00:00.000Z',
)
})
})
})

View File

@@ -270,6 +270,20 @@ export interface Form {
blockName?: string | null;
blockType: 'color';
}
| {
name: string;
label?: string | null;
width?: number | null;
required?: boolean | null;
/**
* This is a date field
*/
defaultValue?: string | null;
defaultValue_tz?: SupportedTimezones;
id?: string | null;
blockName?: string | null;
blockType: 'date';
}
)[]
| null;
submitButtonLabel?: string | null;
@@ -615,6 +629,18 @@ export interface FormsSelect<T extends boolean = true> {
id?: T;
blockName?: T;
};
date?:
| T
| {
name?: T;
label?: T;
width?: T;
required?: T;
defaultValue?: T;
defaultValue_tz?: T;
id?: T;
blockName?: T;
};
};
submitButtonLabel?: T;
confirmationType?: T;

View File

@@ -78,6 +78,65 @@ export const seed = async (payload: Payload): Promise<boolean> => {
},
})
const { id: dateFormID } = await payload.create({
collection: formsSlug,
data: {
confirmationType: 'message',
confirmationMessage: {
root: {
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: [
{
name: 'name',
blockType: 'text',
label: 'Name',
required: true,
},
{
name: 'email',
blockType: 'email',
label: 'Email',
required: true,
},
{
name: 'date',
width: null,
required: null,
blockType: 'date',
},
],
title: 'Booking Form',
},
})
await payload.create({
collection: formSubmissionsSlug,
data: {