diff --git a/packages/payload/src/queues/localAPI.ts b/packages/payload/src/queues/localAPI.ts index 9b3080b53d..ada2ec5395 100644 --- a/packages/payload/src/queues/localAPI.ts +++ b/packages/payload/src/queues/localAPI.ts @@ -22,18 +22,18 @@ export const getJobsLocalAPI = (payload: Payload) => ({ req?: PayloadRequest // TTaskOrWorkflowlug with keyof TypedJobs['workflows'] removed: task: TTaskOrWorkflowSlug extends keyof TypedJobs['tasks'] ? TTaskOrWorkflowSlug : never - workflow?: never waitUntil?: Date + workflow?: never } | { input: TypedJobs['workflows'][TTaskOrWorkflowSlug]['input'] queue?: string req?: PayloadRequest task?: never + waitUntil?: Date workflow: TTaskOrWorkflowSlug extends keyof TypedJobs['workflows'] ? TTaskOrWorkflowSlug : never - waitUntil?: Date }, ): Promise< TTaskOrWorkflowSlug extends keyof TypedJobs['workflows'] @@ -60,8 +60,8 @@ export const getJobsLocalAPI = (payload: Payload) => ({ input: args.input, queue, taskSlug: 'task' in args ? args.task : undefined, - workflowSlug: 'workflow' in args ? args.workflow : undefined, waitUntil: args.waitUntil?.toISOString() ?? undefined, + workflowSlug: 'workflow' in args ? args.workflow : undefined, } as BaseJob, req: args.req, })) as TTaskOrWorkflowSlug extends keyof TypedJobs['workflows'] diff --git a/packages/richtext-lexical/src/field/Field.tsx b/packages/richtext-lexical/src/field/Field.tsx index eba34342e7..a970c956b2 100644 --- a/packages/richtext-lexical/src/field/Field.tsx +++ b/packages/richtext-lexical/src/field/Field.tsx @@ -9,7 +9,6 @@ import { RenderCustomComponent, useEditDepth, useField, - withCondition, } from '@payloadcms/ui' import { mergeFieldStyles } from '@payloadcms/ui/shared' import React, { useCallback, useMemo } from 'react' @@ -143,4 +142,4 @@ function fallbackRender({ error }: { error: Error }) { ) } -export const RichText: typeof RichTextComponent = withCondition(RichTextComponent) +export const RichText: typeof RichTextComponent = RichTextComponent diff --git a/packages/richtext-slate/src/field/RichText.tsx b/packages/richtext-slate/src/field/RichText.tsx index 76f88cf899..0040b787b4 100644 --- a/packages/richtext-slate/src/field/RichText.tsx +++ b/packages/richtext-slate/src/field/RichText.tsx @@ -14,7 +14,6 @@ import { useEditDepth, useField, useTranslation, - withCondition, } from '@payloadcms/ui' import { mergeFieldStyles } from '@payloadcms/ui/shared' import { isHotkey } from 'is-hotkey' @@ -459,4 +458,4 @@ const RichTextField: React.FC = (props) => { ) } -export const RichText = withCondition(RichTextField) +export const RichText = RichTextField diff --git a/packages/ui/src/exports/client/index.ts b/packages/ui/src/exports/client/index.ts index f1332bbb13..38ae0c6296 100644 --- a/packages/ui/src/exports/client/index.ts +++ b/packages/ui/src/exports/client/index.ts @@ -196,6 +196,7 @@ export { useField } from '../../forms/useField/index.js' export type { FieldType, Options } from '../../forms/useField/types.js' export { withCondition } from '../../forms/withCondition/index.js' +export { WatchCondition } from '../../forms/withCondition/WatchCondition.js' // graphics export { Account } from '../../graphics/Account/index.js' diff --git a/packages/ui/src/forms/RenderFields/RenderField.tsx b/packages/ui/src/forms/RenderFields/RenderField.tsx index 8b485786c9..a754e1f122 100644 --- a/packages/ui/src/forms/RenderFields/RenderField.tsx +++ b/packages/ui/src/forms/RenderFields/RenderField.tsx @@ -51,10 +51,10 @@ export function RenderField({ readOnly, schemaPath, }: RenderFieldProps) { - const Field = useFormFields(([fields]) => fields && fields?.[path]?.customComponents?.Field) + const CustomField = useFormFields(([fields]) => fields && fields?.[path]?.customComponents?.Field) - if (Field !== undefined) { - return Field || null + if (CustomField !== undefined) { + return CustomField || null } const baseFieldProps: Pick = { diff --git a/packages/ui/src/forms/fieldSchemasToFormState/renderField.tsx b/packages/ui/src/forms/fieldSchemasToFormState/renderField.tsx index 2b7d5cbff7..9baafc725f 100644 --- a/packages/ui/src/forms/fieldSchemasToFormState/renderField.tsx +++ b/packages/ui/src/forms/fieldSchemasToFormState/renderField.tsx @@ -9,7 +9,7 @@ import type { RenderFieldMethod } from './types.js' import { RenderServerComponent } from '../../elements/RenderServerComponent/index.js' // eslint-disable-next-line payload/no-imports-from-exports-dir -- need this to reference already existing bundle. Otherwise, bundle size increases., payload/no-imports-from-exports-dir -import { FieldDescription } from '../../exports/client/index.js' +import { FieldDescription, WatchCondition } from '../../exports/client/index.js' const defaultUIFieldComponentKeys: Array<'Cell' | 'Description' | 'Field' | 'Filter'> = [ 'Cell', @@ -135,12 +135,16 @@ export const renderField: RenderFieldMethod = ({ fieldConfig.admin.components = {} } - fieldState.customComponents.Field = RenderServerComponent({ - clientProps, - Component: fieldConfig.editor.FieldComponent, - importMap: req.payload.importMap, - serverProps, - }) + fieldState.customComponents.Field = ( + + {RenderServerComponent({ + clientProps, + Component: fieldConfig.editor.FieldComponent, + importMap: req.payload.importMap, + serverProps, + })} + + ) break } @@ -236,13 +240,17 @@ export const renderField: RenderFieldMethod = ({ } if ('Field' in fieldConfig.admin.components) { - fieldState.customComponents.Field = RenderServerComponent({ - clientProps, - Component: fieldConfig.admin.components.Field, - importMap: req.payload.importMap, - key: 'field.admin.components.Field', - serverProps, - }) + fieldState.customComponents.Field = ( + + {RenderServerComponent({ + clientProps, + Component: fieldConfig.admin.components.Field, + importMap: req.payload.importMap, + key: 'field.admin.components.Field', + serverProps, + })} + + ) } } } diff --git a/packages/ui/src/forms/withCondition/WatchCondition.tsx b/packages/ui/src/forms/withCondition/WatchCondition.tsx index 7aa50bee39..27534f2e86 100644 --- a/packages/ui/src/forms/withCondition/WatchCondition.tsx +++ b/packages/ui/src/forms/withCondition/WatchCondition.tsx @@ -6,7 +6,6 @@ import { useFormFields } from '../Form/context.js' export const WatchCondition: React.FC<{ children: React.ReactNode - indexPath: string path: string }> = (props) => { const { children, path } = props diff --git a/packages/ui/src/forms/withCondition/index.tsx b/packages/ui/src/forms/withCondition/index.tsx index 496a92c1e6..c6a92446cd 100644 --- a/packages/ui/src/forms/withCondition/index.tsx +++ b/packages/ui/src/forms/withCondition/index.tsx @@ -10,10 +10,10 @@ export const withCondition =

, ): React.FC

=> { const CheckForCondition: React.FC

= (props) => { - const { indexPath, path } = props + const { path } = props return ( - + ) diff --git a/templates/_template/.vscode/settings.json b/templates/_template/.vscode/settings.json index ae15582ace..5918b30792 100644 --- a/templates/_template/.vscode/settings.json +++ b/templates/_template/.vscode/settings.json @@ -36,5 +36,5 @@ "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" } - }, + } } diff --git a/templates/_template/package.json b/templates/_template/package.json index f2b69e604a..b606c75e60 100644 --- a/templates/_template/package.json +++ b/templates/_template/package.json @@ -28,12 +28,12 @@ "sharp": "0.32.6" }, "devDependencies": { + "@eslint/eslintrc": "^3.2.0", "@types/node": "^22.5.4", "@types/react": "19.0.1", "@types/react-dom": "19.0.1", "eslint": "^9.16.0", "eslint-config-next": "15.1.0", - "@eslint/eslintrc": "^3.2.0", "prettier": "^3.4.2", "typescript": "5.7.2" }, diff --git a/templates/blank/.vscode/settings.json b/templates/blank/.vscode/settings.json index ae15582ace..5918b30792 100644 --- a/templates/blank/.vscode/settings.json +++ b/templates/blank/.vscode/settings.json @@ -36,5 +36,5 @@ "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" } - }, + } } diff --git a/templates/blank/package.json b/templates/blank/package.json index f2b69e604a..b606c75e60 100644 --- a/templates/blank/package.json +++ b/templates/blank/package.json @@ -28,12 +28,12 @@ "sharp": "0.32.6" }, "devDependencies": { + "@eslint/eslintrc": "^3.2.0", "@types/node": "^22.5.4", "@types/react": "19.0.1", "@types/react-dom": "19.0.1", "eslint": "^9.16.0", "eslint-config-next": "15.1.0", - "@eslint/eslintrc": "^3.2.0", "prettier": "^3.4.2", "typescript": "5.7.2" }, diff --git a/templates/website/.vscode/settings.json b/templates/website/.vscode/settings.json index ae15582ace..5918b30792 100644 --- a/templates/website/.vscode/settings.json +++ b/templates/website/.vscode/settings.json @@ -36,5 +36,5 @@ "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" } - }, + } } diff --git a/templates/website/package.json b/templates/website/package.json index c2eab798ef..62d6660251 100644 --- a/templates/website/package.json +++ b/templates/website/package.json @@ -54,6 +54,7 @@ "tailwindcss-animate": "^1.0.7" }, "devDependencies": { + "@eslint/eslintrc": "^3.2.0", "@tailwindcss/typography": "^0.5.13", "@types/escape-html": "^1.0.2", "@types/jsonwebtoken": "^9.0.6", @@ -64,9 +65,8 @@ "copyfiles": "^2.4.1", "eslint": "^9.16.0", "eslint-config-next": "15.1.0", - "@eslint/eslintrc": "^3.2.0", - "prettier": "^3.4.2", "postcss": "^8.4.38", + "prettier": "^3.4.2", "tailwindcss": "^3.4.3", "typescript": "5.7.2" }, diff --git a/templates/with-payload-cloud/.vscode/settings.json b/templates/with-payload-cloud/.vscode/settings.json index ae15582ace..5918b30792 100644 --- a/templates/with-payload-cloud/.vscode/settings.json +++ b/templates/with-payload-cloud/.vscode/settings.json @@ -36,5 +36,5 @@ "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" } - }, + } } diff --git a/templates/with-payload-cloud/package.json b/templates/with-payload-cloud/package.json index 09f734d2f7..d404e9bdef 100644 --- a/templates/with-payload-cloud/package.json +++ b/templates/with-payload-cloud/package.json @@ -28,12 +28,12 @@ "sharp": "0.32.6" }, "devDependencies": { + "@eslint/eslintrc": "^3.2.0", "@types/node": "^22.5.4", "@types/react": "19.0.1", "@types/react-dom": "19.0.1", "eslint": "^9.16.0", "eslint-config-next": "15.1.0", - "@eslint/eslintrc": "^3.2.0", "prettier": "^3.4.2", "typescript": "5.7.2" }, diff --git a/templates/with-postgres/.vscode/settings.json b/templates/with-postgres/.vscode/settings.json index ae15582ace..5918b30792 100644 --- a/templates/with-postgres/.vscode/settings.json +++ b/templates/with-postgres/.vscode/settings.json @@ -36,5 +36,5 @@ "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" } - }, + } } diff --git a/templates/with-postgres/package.json b/templates/with-postgres/package.json index d68b5f3254..46750844b1 100644 --- a/templates/with-postgres/package.json +++ b/templates/with-postgres/package.json @@ -29,12 +29,12 @@ "sharp": "0.32.6" }, "devDependencies": { + "@eslint/eslintrc": "^3.2.0", "@types/node": "^22.5.4", "@types/react": "19.0.1", "@types/react-dom": "19.0.1", "eslint": "^9.16.0", "eslint-config-next": "15.1.0", - "@eslint/eslintrc": "^3.2.0", "prettier": "^3.4.2", "typescript": "5.7.2" }, diff --git a/templates/with-vercel-mongodb/.vscode/settings.json b/templates/with-vercel-mongodb/.vscode/settings.json index ae15582ace..5918b30792 100644 --- a/templates/with-vercel-mongodb/.vscode/settings.json +++ b/templates/with-vercel-mongodb/.vscode/settings.json @@ -36,5 +36,5 @@ "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" } - }, + } } diff --git a/templates/with-vercel-mongodb/package.json b/templates/with-vercel-mongodb/package.json index a76466ff68..1e00736a96 100644 --- a/templates/with-vercel-mongodb/package.json +++ b/templates/with-vercel-mongodb/package.json @@ -28,12 +28,12 @@ "react-dom": "19.0.0" }, "devDependencies": { + "@eslint/eslintrc": "^3.2.0", "@types/node": "^22.5.4", "@types/react": "19.0.1", "@types/react-dom": "19.0.1", "eslint": "^9.16.0", "eslint-config-next": "15.1.0", - "@eslint/eslintrc": "^3.2.0", "prettier": "^3.4.2", "typescript": "5.7.2" }, diff --git a/templates/with-vercel-postgres/.vscode/settings.json b/templates/with-vercel-postgres/.vscode/settings.json index ae15582ace..5918b30792 100644 --- a/templates/with-vercel-postgres/.vscode/settings.json +++ b/templates/with-vercel-postgres/.vscode/settings.json @@ -36,5 +36,5 @@ "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" } - }, + } } diff --git a/templates/with-vercel-postgres/package.json b/templates/with-vercel-postgres/package.json index 1c23c07003..92108c2d8a 100644 --- a/templates/with-vercel-postgres/package.json +++ b/templates/with-vercel-postgres/package.json @@ -29,12 +29,12 @@ "react-dom": "19.0.0" }, "devDependencies": { + "@eslint/eslintrc": "^3.2.0", "@types/node": "^22.5.4", "@types/react": "19.0.1", "@types/react-dom": "19.0.1", "eslint": "^9.16.0", "eslint-config-next": "15.1.0", - "@eslint/eslintrc": "^3.2.0", "prettier": "^3.4.2", "typescript": "5.7.2" }, diff --git a/templates/with-vercel-website/.vscode/settings.json b/templates/with-vercel-website/.vscode/settings.json index ae15582ace..5918b30792 100644 --- a/templates/with-vercel-website/.vscode/settings.json +++ b/templates/with-vercel-website/.vscode/settings.json @@ -36,5 +36,5 @@ "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" } - }, + } } diff --git a/templates/with-vercel-website/package.json b/templates/with-vercel-website/package.json index 49f9f93216..dded8e7a1f 100644 --- a/templates/with-vercel-website/package.json +++ b/templates/with-vercel-website/package.json @@ -56,6 +56,7 @@ "tailwindcss-animate": "^1.0.7" }, "devDependencies": { + "@eslint/eslintrc": "^3.2.0", "@tailwindcss/typography": "^0.5.13", "@types/escape-html": "^1.0.2", "@types/jsonwebtoken": "^9.0.6", @@ -66,9 +67,8 @@ "copyfiles": "^2.4.1", "eslint": "^9.16.0", "eslint-config-next": "^15.1.0", - "@eslint/eslintrc": "^3.2.0", - "prettier": "^3.4.2", "postcss": "^8.4.38", + "prettier": "^3.4.2", "tailwindcss": "^3.4.3", "typescript": "5.7.2" }, diff --git a/test/fields/collections/ConditionalLogic/CustomClientField.tsx b/test/fields/collections/ConditionalLogic/CustomClientField.tsx new file mode 100644 index 0000000000..59cc9a2072 --- /dev/null +++ b/test/fields/collections/ConditionalLogic/CustomClientField.tsx @@ -0,0 +1,11 @@ +'use client' + +import type { TextFieldClientComponent } from 'payload' + +import React from 'react' + +const CustomClientField: TextFieldClientComponent = () => { + return

Custom Client Field
+} + +export default CustomClientField diff --git a/test/fields/collections/ConditionalLogic/CustomFieldWithField.tsx b/test/fields/collections/ConditionalLogic/CustomFieldWithField.tsx new file mode 100644 index 0000000000..7d50393910 --- /dev/null +++ b/test/fields/collections/ConditionalLogic/CustomFieldWithField.tsx @@ -0,0 +1,11 @@ +'use client' +import type { TextFieldClientComponent } from 'payload' + +import { TextField } from '@payloadcms/ui' +import React from 'react' + +const CustomFieldWithField: TextFieldClientComponent = (props) => { + return +} + +export default CustomFieldWithField diff --git a/test/fields/collections/ConditionalLogic/CustomFieldWithHOC.tsx b/test/fields/collections/ConditionalLogic/CustomFieldWithHOC.tsx new file mode 100644 index 0000000000..ebe3e3971d --- /dev/null +++ b/test/fields/collections/ConditionalLogic/CustomFieldWithHOC.tsx @@ -0,0 +1,11 @@ +'use client' +import type { TextFieldClientComponent } from 'payload' + +import { TextField, withCondition } from '@payloadcms/ui' +import React from 'react' + +const MyField: TextFieldClientComponent = (props) => { + return +} + +export default withCondition(MyField) diff --git a/test/fields/collections/ConditionalLogic/CustomServerField.tsx b/test/fields/collections/ConditionalLogic/CustomServerField.tsx new file mode 100644 index 0000000000..f0ec740f09 --- /dev/null +++ b/test/fields/collections/ConditionalLogic/CustomServerField.tsx @@ -0,0 +1,9 @@ +import type { TextFieldServerComponent } from 'payload' + +import React from 'react' + +const CustomServerField: TextFieldServerComponent = () => { + return
Custom Server Field
+} + +export default CustomServerField diff --git a/test/fields/collections/ConditionalLogic/e2e.spec.ts b/test/fields/collections/ConditionalLogic/e2e.spec.ts index 9f1f16c833..4cad02b081 100644 --- a/test/fields/collections/ConditionalLogic/e2e.spec.ts +++ b/test/fields/collections/ConditionalLogic/e2e.spec.ts @@ -84,6 +84,49 @@ describe('Conditional Logic', () => { expect(true).toBe(true) }) + test('should conditionally render custom field that renders a Payload field', async () => { + await page.goto(url.create) + + await toggleConditionAndCheckField( + 'label[for=field-toggleField]', + 'label[for=field-customFieldWithField]', + ) + + expect(true).toBe(true) + }) + + test('should conditionally render custom field that wraps itself with the withCondition HOC (legacy)', async () => { + await page.goto(url.create) + + await toggleConditionAndCheckField( + 'label[for=field-toggleField]', + 'label[for=field-customFieldWithHOC]', + ) + + expect(true).toBe(true) + }) + + test('should toggle conditional custom client field', async () => { + await page.goto(url.create) + await toggleConditionAndCheckField('label[for=field-toggleField]', '#custom-client-field') + expect(true).toBe(true) + }) + + test('should conditionally render custom server field', async () => { + await page.goto(url.create) + await toggleConditionAndCheckField('label[for=field-toggleField]', '#custom-server-field') + expect(true).toBe(true) + }) + + test('should conditionally render rich text fields', async () => { + await page.goto(url.create) + await toggleConditionAndCheckField( + 'label[for=field-toggleField]', + '.field-type.rich-text-lexical', + ) + expect(true).toBe(true) + }) + test('should show conditional field based on user data', async () => { await page.goto(url.create) const userConditional = page.locator('input#field-userConditional') diff --git a/test/fields/collections/ConditionalLogic/index.ts b/test/fields/collections/ConditionalLogic/index.ts index 90f79a2f21..8929aa3ee5 100644 --- a/test/fields/collections/ConditionalLogic/index.ts +++ b/test/fields/collections/ConditionalLogic/index.ts @@ -20,7 +20,54 @@ const ConditionalLogic: CollectionConfig = { { name: 'fieldWithCondition', type: 'text', - required: true, + admin: { + condition: ({ toggleField }) => Boolean(toggleField), + }, + }, + { + name: 'customFieldWithField', + type: 'text', + admin: { + components: { + Field: '/collections/ConditionalLogic/CustomFieldWithField', + }, + condition: ({ toggleField }) => Boolean(toggleField), + }, + }, + { + name: 'customFieldWithHOC', + label: 'Custom Field With HOC (legacy)', + type: 'text', + admin: { + components: { + Field: '/collections/ConditionalLogic/CustomFieldWithHOC', + }, + condition: ({ toggleField }) => Boolean(toggleField), + }, + }, + { + name: 'customClientFieldWithCondition', + type: 'text', + admin: { + components: { + Field: '/collections/ConditionalLogic/CustomClientField', + }, + condition: ({ toggleField }) => Boolean(toggleField), + }, + }, + { + name: 'customServerFieldWithCondition', + type: 'text', + admin: { + components: { + Field: '/collections/ConditionalLogic/CustomServerField', + }, + condition: ({ toggleField }) => Boolean(toggleField), + }, + }, + { + name: 'conditionalRichText', + type: 'richText', admin: { condition: ({ toggleField }) => Boolean(toggleField), }, diff --git a/test/fields/collections/ConditionalLogic/shared.ts b/test/fields/collections/ConditionalLogic/shared.ts index 3d86f9031c..52cef0e76a 100644 --- a/test/fields/collections/ConditionalLogic/shared.ts +++ b/test/fields/collections/ConditionalLogic/shared.ts @@ -5,5 +5,4 @@ import type { ConditionalLogic } from '../../payload-types.js' export const conditionalLogicDoc: RequiredDataFromCollection = { text: 'Seeded conditional logic document', toggleField: true, - fieldWithCondition: 'spiderman', } diff --git a/test/fields/components/CustomField.tsx b/test/fields/components/CustomField.tsx index 8cf37860a5..51327976d2 100644 --- a/test/fields/components/CustomField.tsx +++ b/test/fields/components/CustomField.tsx @@ -1,9 +1,9 @@ 'use client' -import type { TextFieldServerComponent } from 'payload' +import type { TextFieldClientComponent } from 'payload' import React from 'react' -export const CustomField: TextFieldServerComponent = ({ schemaPath }) => { +export const CustomField: TextFieldClientComponent = ({ schemaPath }) => { return
{schemaPath}
}