diff --git a/packages/plugin-form-builder/demo/.env.example b/packages/plugin-form-builder/demo/.env.example deleted file mode 100644 index 586b75f990..0000000000 --- a/packages/plugin-form-builder/demo/.env.example +++ /dev/null @@ -1,2 +0,0 @@ -MONGODB_URI=mongodb://localhost/payload-plugin-form-builder -PAYLOAD_SECRET=kajsnfkjhabndsfgseaniluanbsrkdgbhyasfg diff --git a/packages/plugin-form-builder/demo/nodemon.json b/packages/plugin-form-builder/demo/nodemon.json deleted file mode 100644 index 346913d466..0000000000 --- a/packages/plugin-form-builder/demo/nodemon.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "exec": "ts-node src/server.ts", - "ext": "ts" -} diff --git a/packages/plugin-form-builder/demo/package.json b/packages/plugin-form-builder/demo/package.json deleted file mode 100644 index e18ada1c6e..0000000000 --- a/packages/plugin-form-builder/demo/package.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "name": "payload-starter-typescript", - "description": "Blank template - no collections", - "version": "1.0.0", - "main": "dist/server.js", - "license": "MIT", - "scripts": { - "dev": "cross-env PAYLOAD_SEED=true PAYLOAD_DROP_DATABASE=true PAYLOAD_CONFIG_PATH=src/payload.config.ts nodemon", - "build:payload": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload build", - "build:server": "tsc", - "build": "yarn build:payload && yarn build:server", - "serve": "cross-env PAYLOAD_CONFIG_PATH=dist/payload.config.js NODE_ENV=production node dist/server.js", - "generate:types": "cross-env PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:types" - }, - "dependencies": { - "deepmerge": "^4.2.2", - "dotenv": "^8.2.0", - "express": "^4.17.1", - "payload": "^1.8.2" - }, - "devDependencies": { - "@types/express": "^4.17.9", - "@types/react": "^18.2.6", - "cross-env": "^7.0.3", - "nodemon": "^2.0.6", - "ts-node": "^9.1.1", - "typescript": "^4.1.3" - } -} diff --git a/packages/plugin-form-builder/demo/src/payload-types.ts b/packages/plugin-form-builder/demo/src/payload-types.ts deleted file mode 100644 index b3c3cb55df..0000000000 --- a/packages/plugin-form-builder/demo/src/payload-types.ts +++ /dev/null @@ -1,205 +0,0 @@ -/* tslint:disable */ -/** - * This file was automatically generated by Payload CMS. - * DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config, - * and re-run `payload generate:types` to regenerate this file. - */ - -export interface Config { - collections: { - users: User - pages: Page - forms: Form - 'form-submissions': FormSubmission - } - globals: {} -} -export interface User { - id: string - updatedAt: string - createdAt: string - email?: string - resetPasswordToken?: string - resetPasswordExpiration?: string - loginAttempts?: number - lockUntil?: string - password?: string -} -export interface Page { - id: string - title: string - form?: string | Form - updatedAt: string - createdAt: string -} -export interface Form { - id: string - title: string - fields?: ( - | { - name: string - label?: string - width?: number - defaultValue?: string - required?: boolean - id?: string - blockName?: string - blockType: 'text' - } - | { - name: string - label?: string - width?: number - defaultValue?: string - required?: boolean - id?: string - blockName?: string - blockType: 'textarea' - } - | { - name: string - label?: string - width?: number - defaultValue?: string - options: { - label: string - value: string - id?: string - }[] - required?: boolean - id?: string - blockName?: string - blockType: 'select' - } - | { - name: string - label?: string - width?: number - required?: boolean - id?: string - blockName?: string - blockType: 'email' - } - | { - name: string - label?: string - width?: number - required?: boolean - id?: string - blockName?: string - blockType: 'state' - } - | { - name: string - label?: string - width?: number - required?: boolean - id?: string - blockName?: string - blockType: 'country' - } - | { - name: string - label?: string - width?: number - defaultValue?: number - required?: boolean - id?: string - blockName?: string - blockType: 'number' - } - | { - name: string - label?: string - width?: number - required?: boolean - defaultValue?: boolean - id?: string - blockName?: string - blockType: 'checkbox' - } - | { - message?: { - [k: string]: unknown - }[] - id?: string - blockName?: string - blockType: 'message' - } - | { - name: string - label?: string - width?: number - basePrice?: number - priceConditions?: { - fieldToUse?: string - condition?: 'hasValue' | 'equals' | 'notEquals' - valueForCondition?: string - operator?: 'add' | 'subtract' | 'multiply' | 'divide' - valueType?: 'static' | 'valueOfField' - valueForOperator?: string - id?: string - }[] - required?: boolean - id?: string - blockName?: string - blockType: 'payment' - } - | { - value?: string - id?: string - blockName?: string - blockType: 'color' - } - )[] - submitButtonLabel?: string - confirmationType?: 'message' | 'redirect' - confirmationMessage: { - [k: string]: unknown - }[] - redirect?: { - type?: 'reference' | 'custom' - reference: { - value: string | Page - relationTo: 'pages' - } - url: string - } - emails: { - emailTo?: string - cc?: string - bcc?: string - replyTo?: string - emailFrom?: string - subject: string - message?: { - [k: string]: unknown - }[] - id?: string - }[] - name?: string - updatedAt: string - createdAt: string -} -export interface FormSubmission { - id: string - form: string | Form - submissionData: { - field: string - value: string - id?: string - }[] - payment?: { - field?: string - status?: string - amount?: number - paymentProcessor?: string - creditCard?: { - token?: string - brand?: string - number?: string - } - } - updatedAt: string - createdAt: string -} diff --git a/packages/plugin-form-builder/demo/src/seed/index.ts b/packages/plugin-form-builder/demo/src/seed/index.ts deleted file mode 100644 index abdb4c4151..0000000000 --- a/packages/plugin-form-builder/demo/src/seed/index.ts +++ /dev/null @@ -1,48 +0,0 @@ -import type { Payload } from 'payload' - -export const seed = async (payload: Payload): Promise => { - payload.logger.info('Seeding data...') - - await payload.create({ - collection: 'users', - data: { - email: 'dev@payloadcms.com', - password: 'test', - }, - }) - - const { id: formID } = await payload.create({ - collection: 'forms', - data: { - title: 'Contact Form', - confirmationMessage: [ - { - type: 'paragraph', - text: 'Confirmed', - }, - ], - fields: [ - { - blockType: 'text', - label: 'Name', - name: 'name', - required: true, - }, - { - blockType: 'email', - label: 'Email', - name: 'email', - required: true, - }, - ], - }, - }) - - await payload.create({ - collection: 'pages', - data: { - title: 'Contact', - form: formID, - }, - }) -} diff --git a/packages/plugin-form-builder/demo/src/server.ts b/packages/plugin-form-builder/demo/src/server.ts deleted file mode 100644 index dc4b68450d..0000000000 --- a/packages/plugin-form-builder/demo/src/server.ts +++ /dev/null @@ -1,33 +0,0 @@ -import dotenv from 'dotenv' -import express from 'express' -import payload from 'payload' - -import { seed } from './seed' - -dotenv.config() -const app = express() - -// Redirect root to Admin panel -app.get('/', (_, res) => { - res.redirect('/admin') -}) - -// Initialize Payload -const start = async (): Promise => { - await payload.init({ - secret: process.env.PAYLOAD_SECRET, - mongoURL: process.env.MONGODB_URI, - express: app, - onInit: () => { - payload.logger.info(`Payload Admin URL: ${payload.getAdminURL()}`) - }, - }) - - if (process.env.PAYLOAD_SEED === 'true') { - await seed(payload) - } - - app.listen(3000) -} - -start() diff --git a/packages/plugin-form-builder/demo/tsconfig.json b/packages/plugin-form-builder/demo/tsconfig.json deleted file mode 100644 index 6fc62185aa..0000000000 --- a/packages/plugin-form-builder/demo/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], - "strict": false, - "esModuleInterop": true, - "skipLibCheck": true, - "outDir": "./dist", - "rootDir": "../", - "jsx": "react" - }, - "ts-node": { - "transpileOnly": true - } -} diff --git a/packages/plugin-form-builder/demo/src/collections/Pages.ts b/test/plugin-form-builder/collections/Pages.ts similarity index 58% rename from packages/plugin-form-builder/demo/src/collections/Pages.ts rename to test/plugin-form-builder/collections/Pages.ts index 8a091087f5..e4376bd431 100644 --- a/packages/plugin-form-builder/demo/src/collections/Pages.ts +++ b/test/plugin-form-builder/collections/Pages.ts @@ -1,4 +1,5 @@ -import type { CollectionConfig } from 'payload/types' +// const payload = require('payload'); +import type { CollectionConfig } from '../../../packages/payload/src/collections/config/types' export const Pages: CollectionConfig = { slug: 'pages', @@ -9,6 +10,9 @@ export const Pages: CollectionConfig = { admin: { useAsTitle: 'title', }, + access: { + read: () => true, + }, fields: [ { name: 'title', @@ -16,6 +20,12 @@ export const Pages: CollectionConfig = { type: 'text', required: true, }, + { + name: 'slug', + label: 'Slug', + type: 'text', + required: true, + }, { name: 'form', label: 'Form', diff --git a/packages/plugin-form-builder/demo/src/collections/Users.ts b/test/plugin-form-builder/collections/Users.ts similarity index 71% rename from packages/plugin-form-builder/demo/src/collections/Users.ts rename to test/plugin-form-builder/collections/Users.ts index a621159274..8bc9d81767 100644 --- a/packages/plugin-form-builder/demo/src/collections/Users.ts +++ b/test/plugin-form-builder/collections/Users.ts @@ -1,4 +1,4 @@ -import type { CollectionConfig } from 'payload/types' +import type { CollectionConfig } from '../../../packages/payload/src/collections/config/types' export const Users: CollectionConfig = { slug: 'users', diff --git a/packages/plugin-form-builder/demo/src/payload.config.ts b/test/plugin-form-builder/config.ts similarity index 53% rename from packages/plugin-form-builder/demo/src/payload.config.ts rename to test/plugin-form-builder/config.ts index 300f752beb..0ec5537e65 100644 --- a/packages/plugin-form-builder/demo/src/payload.config.ts +++ b/test/plugin-form-builder/config.ts @@ -1,12 +1,11 @@ -import path from 'path' -import { buildConfig } from 'payload/config' -import type { Block } from 'payload/types' +import type { Block } from '../../packages/payload/src/fields/config/types' -// import formBuilderPlugin from '../../dist'; -// eslint-disable-next-line import/no-relative-packages -import formBuilderPlugin, { fields } from '../../src' +import formBuilder, { fields as formFields } from '../../packages/plugin-form-builder/src' +import { buildConfigWithDefaults } from '../buildConfigWithDefaults' +import { devUser } from '../credentials' import { Pages } from './collections/Pages' import { Users } from './collections/Users' +import { seed } from './seed' const colorField: Block = { slug: 'color', @@ -22,34 +21,26 @@ const colorField: Block = { ], } -export default buildConfig({ - serverURL: 'http://localhost:3000', +export default buildConfigWithDefaults({ + collections: [Pages, Users], localization: { - locales: ['en', 'it'], defaultLocale: 'en', + fallback: true, + locales: ['en', 'es', 'de'], }, - admin: { - user: Users.slug, - webpack: (config) => { - const newConfig = { - ...config, - resolve: { - ...config.resolve, - alias: { - ...config.resolve.alias, - react: path.join(__dirname, '../node_modules/react'), - 'react-dom': path.join(__dirname, '../node_modules/react-dom'), - payload: path.join(__dirname, '../node_modules/payload'), - }, - }, - } + onInit: async (payload) => { + await payload.create({ + collection: 'users', + data: { + email: devUser.email, + password: devUser.password, + }, + }) - return newConfig - }, + await seed(payload) }, - collections: [Users, Pages], plugins: [ - formBuilderPlugin({ + formBuilder({ // handlePayment: handleFormPayments, // beforeEmail: prepareFormEmails, redirectRelationships: ['pages'], @@ -60,7 +51,7 @@ export default buildConfig({ // }, fields: [ { - name: 'name', + name: 'custom', type: 'text', }, ], @@ -69,7 +60,7 @@ export default buildConfig({ payment: true, colorField, text: { - ...fields.text, + ...formFields.text, labels: { singular: 'Custom Text Field', plural: 'Custom Text Fields', @@ -89,7 +80,4 @@ export default buildConfig({ }, }), ], - typescript: { - outputFile: path.resolve(__dirname, 'payload-types.ts'), - }, }) diff --git a/test/plugin-form-builder/int.spec.ts b/test/plugin-form-builder/int.spec.ts new file mode 100644 index 0000000000..4b5a8b7afa --- /dev/null +++ b/test/plugin-form-builder/int.spec.ts @@ -0,0 +1,119 @@ +import type { Form } from './payload-types' + +import payload from '../../packages/payload/src' +import { initPayloadTest } from '../helpers/configHelpers' +import { formSubmissionsSlug, formsSlug } from './shared' + +describe('Form Builder Plugin', () => { + let form: Form + + beforeAll(async () => { + await initPayloadTest({ __dirname, init: { local: true } }) + + const formConfig: Omit = { + title: 'Test Form', + fields: [ + { + name: 'name', + blockType: 'text', + }, + ], + confirmationMessage: [ + { + type: 'text', + text: 'Confirmed.', + }, + ], + } + + form = (await payload.create({ + collection: formsSlug, + data: formConfig, + })) as unknown as Form + }) + + describe('plugin collections', () => { + it('adds forms collection', async () => { + const { docs: forms } = await payload.find({ collection: formsSlug }) + expect(forms.length).toBeGreaterThan(0) + }) + + it('adds form submissions collection', async () => { + const { docs: formSubmissions } = await payload.find({ collection: formSubmissionsSlug }) + expect(formSubmissions).toHaveLength(0) + }) + }) + + describe('form building', () => { + it('can create a simple form', async () => { + const formConfig: Omit = { + title: 'Test Form', + fields: [ + { + name: 'name', + blockType: 'text', + }, + ], + confirmationMessage: [ + { + type: 'text', + text: 'Confirmed.', + }, + ], + } + + const testForm = await payload.create({ + collection: formsSlug, + data: formConfig, + }) + + expect(testForm).toHaveProperty('fields') + expect(testForm.fields).toHaveLength(1) + expect(testForm.fields[0]).toHaveProperty('name', 'name') + }) + + it('can use form overrides', async () => { + const formConfig: Omit = { + custom: 'custom', + title: 'Test Form', + confirmationMessage: [ + { + type: 'text', + text: 'Confirmed.', + }, + ], + } + + const testForm = await payload.create({ + collection: formsSlug, + data: formConfig, + }) + + expect(testForm).toHaveProperty('custom', 'custom') + }) + }) + + describe('form submissions and validations', () => { + it('can create a form submission', async () => { + const formSubmission = await payload.create({ + collection: formSubmissionsSlug, + data: { + form: form.id, + submissionData: [ + { + field: 'name', + value: 'Test Submission', + }, + ], + }, + depth: 0, + }) + + expect(formSubmission).toHaveProperty('form', form.id) + expect(formSubmission).toHaveProperty('submissionData') + expect(formSubmission.submissionData).toHaveLength(1) + expect(formSubmission.submissionData[0]).toHaveProperty('field', 'name') + expect(formSubmission.submissionData[0]).toHaveProperty('value', 'Test Submission') + }) + }) +}) diff --git a/test/plugin-form-builder/payload-types.ts b/test/plugin-form-builder/payload-types.ts new file mode 100644 index 0000000000..7846b556f4 --- /dev/null +++ b/test/plugin-form-builder/payload-types.ts @@ -0,0 +1,257 @@ +/* tslint:disable */ +/* eslint-disable */ +/** + * This file was automatically generated by Payload. + * DO NOT MODIFY IT BY HAND. Instead, modify your source Payload config, + * and re-run `payload generate:types` to regenerate this file. + */ + +export interface Config { + collections: { + pages: Page + users: User + forms: Form + 'form-submissions': FormSubmission + 'payload-preferences': PayloadPreference + 'payload-migrations': PayloadMigration + } + globals: {} +} +export interface Page { + id: string + title: string + slug: string + form?: (string | null) | Form + updatedAt: string + createdAt: string +} +export interface Form { + id: string + title: string + fields?: + | ( + | { + name: string + label?: string | null + width?: number | null + required?: boolean | null + defaultValue?: boolean | null + id?: string | null + blockName?: string | null + blockType: 'checkbox' + } + | { + name: string + label?: string | null + width?: number | null + required?: boolean | null + id?: string | null + blockName?: string | null + blockType: 'country' + } + | { + name: string + label?: string | null + width?: number | null + required?: boolean | null + id?: string | null + blockName?: string | null + blockType: 'email' + } + | { + message?: + | { + [k: string]: unknown + }[] + | null + id?: string | null + blockName?: string | null + blockType: 'message' + } + | { + name: string + label?: string | null + width?: number | null + defaultValue?: number | null + required?: boolean | null + id?: string | null + blockName?: string | null + blockType: 'number' + } + | { + name: string + label?: string | null + width?: number | null + basePrice?: number | null + priceConditions?: + | { + fieldToUse?: string | null + condition?: ('hasValue' | 'equals' | 'notEquals') | null + valueForCondition?: string | null + operator?: ('add' | 'subtract' | 'multiply' | 'divide') | null + valueType?: ('static' | 'valueOfField') | null + valueForOperator?: string | null + id?: string | null + }[] + | null + required?: boolean | null + id?: string | null + blockName?: string | null + blockType: 'payment' + } + | { + name: string + label?: string | null + width?: number | null + defaultValue?: string | null + options?: + | { + label: string + value: string + id?: string | null + }[] + | null + required?: boolean | null + id?: string | null + blockName?: string | null + blockType: 'select' + } + | { + name: string + label?: string | null + width?: number | null + required?: boolean | null + id?: string | null + blockName?: string | null + blockType: 'state' + } + | { + name: string + label?: string | null + width?: number | null + defaultValue?: string | null + required?: boolean | null + id?: string | null + blockName?: string | null + blockType: 'text' + } + | { + name: string + label?: string | null + width?: number | null + defaultValue?: string | null + required?: boolean | null + id?: string | null + blockName?: string | null + blockType: 'textarea' + } + | { + value?: string | null + id?: string | null + blockName?: string | null + blockType: 'color' + } + )[] + | null + submitButtonLabel?: string | null + confirmationType?: ('message' | 'redirect') | null + confirmationMessage?: + | { + [k: string]: unknown + }[] + | null + redirect?: { + type?: ('reference' | 'custom') | null + reference?: { + relationTo: 'pages' + value: string | Page + } | null + url?: string | null + } + emails?: + | { + emailTo?: string | null + cc?: string | null + bcc?: string | null + replyTo?: string | null + emailFrom?: string | null + subject: string + message?: + | { + [k: string]: unknown + }[] + | null + id?: string | null + }[] + | null + custom?: string | null + updatedAt: string + createdAt: string +} +export interface User { + id: string + updatedAt: string + createdAt: string + email: string + resetPasswordToken?: string | null + resetPasswordExpiration?: string | null + salt?: string | null + hash?: string | null + loginAttempts?: number | null + lockUntil?: string | null + password: string | null +} +export interface FormSubmission { + id: string + form: string | Form + submissionData?: + | { + field: string + value: string + id?: string | null + }[] + | null + payment?: { + field?: string | null + status?: string | null + amount?: number | null + paymentProcessor?: string | null + creditCard?: { + token?: string | null + brand?: string | null + number?: string | null + } + } + updatedAt: string + createdAt: string +} +export interface PayloadPreference { + id: string + user: { + relationTo: 'users' + value: string | User + } + key?: string | null + value?: + | { + [k: string]: unknown + } + | unknown[] + | string + | number + | boolean + | null + updatedAt: string + createdAt: string +} +export interface PayloadMigration { + id: string + name?: string | null + batch?: number | null + updatedAt: string + createdAt: string +} + +declare module 'payload' { + export interface GeneratedTypes extends Config {} +} diff --git a/test/plugin-form-builder/seed/index.ts b/test/plugin-form-builder/seed/index.ts new file mode 100644 index 0000000000..0c6add235f --- /dev/null +++ b/test/plugin-form-builder/seed/index.ts @@ -0,0 +1,70 @@ +import type { Payload } from '../../../packages/payload/src' +import type { PayloadRequest } from '../../../packages/payload/src/express/types' + +import { formsSlug, pagesSlug } from '../shared' + +export const seed = async (payload: Payload): Promise => { + payload.logger.info('Seeding data...') + const req = {} as PayloadRequest + + try { + await payload.create({ + collection: 'users', + data: { + email: 'demo@payloadcms.com', + password: 'demo', + }, + req, + }) + + await payload.create({ + collection: pagesSlug, + data: { + slug: 'home', + title: 'Home page', + }, + req, + }) + + const { id: formID } = await payload.create({ + collection: formsSlug, + data: { + title: 'Contact Form', + confirmationMessage: [ + { + type: 'paragraph', + text: 'Confirmed', + }, + ], + fields: [ + { + blockType: 'text', + label: 'Name', + name: 'name', + required: true, + }, + { + blockType: 'email', + label: 'Email', + name: 'email', + required: true, + }, + ], + }, + }) + + await payload.create({ + collection: pagesSlug, + data: { + title: 'Contact', + slug: 'contact', + form: formID, + }, + }) + + return true + } catch (err) { + console.error(err) + return false + } +} diff --git a/test/plugin-form-builder/shared.ts b/test/plugin-form-builder/shared.ts new file mode 100644 index 0000000000..33ad1efbfc --- /dev/null +++ b/test/plugin-form-builder/shared.ts @@ -0,0 +1,5 @@ +export const pagesSlug = 'pages' + +export const formsSlug = 'forms' + +export const formSubmissionsSlug = 'form-submissions'