From bcc765e38a3be7e049bfb4af3a92f20865fdce98 Mon Sep 17 00:00:00 2001 From: Jacob Fletcher Date: Mon, 15 May 2023 17:06:52 -0400 Subject: [PATCH] wip: strictly types fields --- demo/src/payload.config.ts | 4 ++++ src/collections/Forms/fields.ts | 10 ++++----- src/collections/Forms/index.ts | 38 +++++++++++++++++++-------------- src/index.ts | 2 +- src/types.ts | 27 ++++++++++++----------- 5 files changed, 45 insertions(+), 36 deletions(-) diff --git a/demo/src/payload.config.ts b/demo/src/payload.config.ts index 4e16864..f916dcf 100644 --- a/demo/src/payload.config.ts +++ b/demo/src/payload.config.ts @@ -68,6 +68,10 @@ export default buildConfig({ fields: { payment: true, colorField, + newFieldBlock: { + slug: 'custom-text-field', + fields: [], + }, text: { ...fields.text, labels: { diff --git a/src/collections/Forms/fields.ts b/src/collections/Forms/fields.ts index e6b0f87..9d2d5b2 100644 --- a/src/collections/Forms/fields.ts +++ b/src/collections/Forms/fields.ts @@ -1,6 +1,6 @@ import type { Block, Field } from 'payload/types' -import type { FieldConfig, PaymentFieldConfig } from '../../types' +import type { FunctionThatReturnsBlock } from '../../types' import { DynamicFieldSelector } from './DynamicFieldSelector' import { DynamicPriceSelector } from './DynamicPriceSelector' @@ -392,9 +392,10 @@ const Checkbox: Block = { ], } -const Payment = (fieldConfig: PaymentFieldConfig): Block => { +const Payment: FunctionThatReturnsBlock = fieldConfig => { let paymentProcessorField = null - if (fieldConfig?.paymentProcessor) { + + if (fieldConfig && typeof fieldConfig === 'object' && 'paymentProcessor' in fieldConfig) { paymentProcessorField = { type: 'select', options: [], @@ -571,7 +572,6 @@ const Message: Block = { ], } -// eslint-disable-next-line @typescript-eslint/consistent-type-assertions export const fields = { select: Select, checkbox: Checkbox, @@ -583,8 +583,6 @@ export const fields = { country: Country, state: State, payment: Payment, -} as { - [key: string]: Block | ((fieldConfig?: boolean | FieldConfig) => Block) } export default fields diff --git a/src/collections/Forms/index.ts b/src/collections/Forms/index.ts index a7a0886..0cd42ff 100644 --- a/src/collections/Forms/index.ts +++ b/src/collections/Forms/index.ts @@ -2,7 +2,7 @@ import merge from 'deepmerge' import type { Block, CollectionConfig, Field } from 'payload/types' import type { FieldConfig, PluginConfig } from '../../types' -import { fields } from './fields' +import { fields as baseFields } from './fields' // all settings can be overridden by the config export const generateFormCollection = (formConfig: PluginConfig): CollectionConfig => { @@ -86,27 +86,33 @@ export const generateFormCollection = (formConfig: PluginConfig): CollectionConf blocks: Object.entries(formConfig?.fields || {}) .map(([fieldKey, fieldConfig]) => { // let the config enable/disable fields with either boolean values or objects - if (fieldConfig !== false) { - let block = fields[fieldKey] + if (typeof fieldConfig === 'boolean' && fieldConfig === false) { + return null + } - if (block === undefined && typeof fieldConfig === 'object') { - return fieldConfig - } + let baseField = baseFields?.[fieldKey as keyof typeof baseFields] - if (typeof block === 'object' && typeof fieldConfig === 'object') { - return merge(block, fieldConfig, { - arrayMerge: (_, sourceArray) => sourceArray, - }) - } + // this is a new, custom block that does not have a default + // it is of `Block` type, so we can just return it + if (baseField === undefined && typeof fieldConfig === 'object') { + return fieldConfig + } - if (typeof block === 'function') { - return block(fieldConfig) - } + // this is a partial field, so merge it into the default block + if (typeof baseField === 'object' && typeof fieldConfig === 'object') { + return merge(baseField, fieldConfig, { + arrayMerge: (_, sourceArray) => sourceArray, + }) + } - return block + // this is a custom block that has a default + // it is a function so they can use their own merge strategy + if (typeof baseField === 'function' && typeof fieldConfig === 'object') { + return baseField(fieldConfig) } - return null + // do nothing, return the default block as is with no modifications + return baseField }) .filter(Boolean) as Block[], }, diff --git a/src/index.ts b/src/index.ts index eaf6a92..42dd937 100644 --- a/src/index.ts +++ b/src/index.ts @@ -23,7 +23,7 @@ const FormBuilder = checkbox: true, message: true, payment: false, - ...incomingFormConfig.fields, + ...(incomingFormConfig?.fields || {}), }, } diff --git a/src/types.ts b/src/types.ts index 46d1d8b..42a44a0 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,7 @@ import type { Block, CollectionConfig, Field } from 'payload/types' +import type formFields from './collections/Forms/fields' + export interface BlockConfig { block: Block validate?: (value: unknown) => boolean | string @@ -21,23 +23,22 @@ export type PaymentFieldConfig = Partial & { paymentProcessor: Partial } -export type FieldConfig = Partial | PaymentFieldConfig +export interface FunctionThatReturnsBlock { + (fieldConfig?: boolean | Partial | PaymentFieldConfig): Block +} -export interface FieldsConfig { - select?: boolean | FieldConfig - text?: boolean | FieldConfig - textarea?: boolean | FieldConfig - email?: boolean | FieldConfig - state?: boolean | FieldConfig - country?: boolean | FieldConfig - checkbox?: boolean | FieldConfig - number?: boolean | FieldConfig - message?: boolean | FieldConfig - payment?: boolean | FieldConfig - [key: string]: boolean | FieldConfig | undefined +export type FieldsConfig = { + [key in keyof typeof formFields]: boolean | Partial +} & { + payment: boolean | PaymentFieldConfig +} & { + [key: string]: Block | FunctionThatReturnsBlock } +export type FieldConfig = FieldsConfig[keyof FieldsConfig] + export type BeforeEmail = (emails: FormattedEmail[]) => FormattedEmail[] | Promise + export type HandlePayment = (data: any) => void export interface PluginConfig {