diff --git a/packages/payload/src/admin/fields/Array.ts b/packages/payload/src/admin/fields/Array.ts index d67c0c315a8..603dcaf3d63 100644 --- a/packages/payload/src/admin/fields/Array.ts +++ b/packages/payload/src/admin/fields/Array.ts @@ -21,8 +21,7 @@ type ArrayFieldClientWithoutType = MarkOptional type ArrayFieldBaseClientProps = { readonly validate?: ArrayFieldValidation -} & FieldPaths & - Pick +} & FieldPaths export type ArrayFieldClientProps = ArrayFieldBaseClientProps & ClientFieldBase diff --git a/packages/payload/src/admin/fields/Blocks.ts b/packages/payload/src/admin/fields/Blocks.ts index 921005a03f6..0c379d0ff39 100644 --- a/packages/payload/src/admin/fields/Blocks.ts +++ b/packages/payload/src/admin/fields/Blocks.ts @@ -21,8 +21,7 @@ type BlocksFieldClientWithoutType = MarkOptional type BlocksFieldBaseClientProps = { readonly validate?: BlocksFieldValidation -} & FieldPaths & - Pick +} & FieldPaths export type BlocksFieldClientProps = BlocksFieldBaseClientProps & ClientFieldBase diff --git a/packages/payload/src/admin/fields/Collapsible.ts b/packages/payload/src/admin/fields/Collapsible.ts index 01b90fa4ce5..94581cef495 100644 --- a/packages/payload/src/admin/fields/Collapsible.ts +++ b/packages/payload/src/admin/fields/Collapsible.ts @@ -16,7 +16,7 @@ import type { FieldLabelServerComponent, } from '../types.js' -type CollapsibleFieldBaseClientProps = FieldPaths & Pick +type CollapsibleFieldBaseClientProps = FieldPaths type CollapsibleFieldClientWithoutType = MarkOptional diff --git a/packages/payload/src/admin/fields/Group.ts b/packages/payload/src/admin/fields/Group.ts index deda3081dc1..cabbff5077e 100644 --- a/packages/payload/src/admin/fields/Group.ts +++ b/packages/payload/src/admin/fields/Group.ts @@ -18,7 +18,7 @@ import type { type GroupFieldClientWithoutType = MarkOptional -export type GroupFieldBaseClientProps = FieldPaths & Pick +export type GroupFieldBaseClientProps = FieldPaths export type GroupFieldClientProps = ClientFieldBase & GroupFieldBaseClientProps diff --git a/packages/payload/src/admin/fields/Row.ts b/packages/payload/src/admin/fields/Row.ts index 9a179b3e713..0e508981b97 100644 --- a/packages/payload/src/admin/fields/Row.ts +++ b/packages/payload/src/admin/fields/Row.ts @@ -21,8 +21,7 @@ type RowFieldClientWithoutType = MarkOptional type RowFieldBaseClientProps = { readonly forceRender?: boolean -} & Omit & - Pick +} & Omit export type RowFieldClientProps = Omit, 'path'> & RowFieldBaseClientProps diff --git a/packages/payload/src/admin/fields/Tabs.ts b/packages/payload/src/admin/fields/Tabs.ts index 2cbb4bbd97c..76e992596a8 100644 --- a/packages/payload/src/admin/fields/Tabs.ts +++ b/packages/payload/src/admin/fields/Tabs.ts @@ -26,7 +26,7 @@ export type ClientTab = | ({ fields: ClientField[]; readonly path?: string } & Omit) | ({ fields: ClientField[] } & Omit) -type TabsFieldBaseClientProps = FieldPaths & Pick +type TabsFieldBaseClientProps = FieldPaths type TabsFieldClientWithoutType = MarkOptional diff --git a/packages/payload/src/admin/forms/Field.ts b/packages/payload/src/admin/forms/Field.ts index 1157f4d3e9b..93c2e7fb475 100644 --- a/packages/payload/src/admin/forms/Field.ts +++ b/packages/payload/src/admin/forms/Field.ts @@ -21,6 +21,7 @@ export type ClientComponentProps = { customComponents: FormField['customComponents'] field: ClientBlock | ClientField | ClientTab forceRender?: boolean + permissions?: SanitizedFieldPermissions readOnly?: boolean renderedBlocks?: RenderedField[] /** diff --git a/packages/ui/src/forms/RenderFields/RenderField.tsx b/packages/ui/src/forms/RenderFields/RenderField.tsx index a754e1f122c..9c13b8541ee 100644 --- a/packages/ui/src/forms/RenderFields/RenderField.tsx +++ b/packages/ui/src/forms/RenderFields/RenderField.tsx @@ -57,8 +57,12 @@ export function RenderField({ return CustomField || null } - const baseFieldProps: Pick = { + const baseFieldProps: Pick< + ClientComponentProps, + 'forceRender' | 'permissions' | 'readOnly' | 'schemaPath' + > = { forceRender, + permissions, readOnly, schemaPath, } @@ -68,7 +72,6 @@ export function RenderField({ indexPath, parentPath, parentSchemaPath, - permissions, } if (clientFieldConfig.admin?.hidden) { diff --git a/packages/ui/src/forms/fieldSchemasToFormState/renderField.tsx b/packages/ui/src/forms/fieldSchemasToFormState/renderField.tsx index 9baafc725f8..deca48d7dba 100644 --- a/packages/ui/src/forms/fieldSchemasToFormState/renderField.tsx +++ b/packages/ui/src/forms/fieldSchemasToFormState/renderField.tsx @@ -55,6 +55,7 @@ export const renderField: RenderFieldMethod = ({ customComponents: fieldState?.customComponents || {}, field: clientField, path, + permissions, readOnly: typeof permissions === 'boolean' ? !permissions : !permissions?.[operation], schemaPath, } diff --git a/test/fields/collections/Array/CustomArrayField.tsx b/test/fields/collections/Array/CustomArrayField.tsx new file mode 100644 index 00000000000..adad96b2976 --- /dev/null +++ b/test/fields/collections/Array/CustomArrayField.tsx @@ -0,0 +1,12 @@ +'use client' +import type { ArrayFieldClientComponent } from 'payload' + +import { ArrayField } from '@payloadcms/ui' + +export const CustomArrayField: ArrayFieldClientComponent = (props) => { + return ( +
+ +
+ ) +} diff --git a/test/fields/collections/Array/CustomField.tsx b/test/fields/collections/Array/CustomTextField.tsx similarity index 61% rename from test/fields/collections/Array/CustomField.tsx rename to test/fields/collections/Array/CustomTextField.tsx index 6119d02322c..091fcf45914 100644 --- a/test/fields/collections/Array/CustomField.tsx +++ b/test/fields/collections/Array/CustomTextField.tsx @@ -2,9 +2,9 @@ import type { TextFieldServerComponent } from 'payload' import { TextField } from '@payloadcms/ui' -export const CustomField: TextFieldServerComponent = ({ clientField, path }) => { +export const CustomTextField: TextFieldServerComponent = ({ clientField, path }) => { return ( -
+
) diff --git a/test/fields/collections/Array/e2e.spec.ts b/test/fields/collections/Array/e2e.spec.ts index ff4ab5f3969..5b3c704dcb4 100644 --- a/test/fields/collections/Array/e2e.spec.ts +++ b/test/fields/collections/Array/e2e.spec.ts @@ -106,6 +106,12 @@ describe('Array', () => { await expect(customRowLabel).toHaveCSS('text-transform', 'uppercase') }) + test('should render default array field within custom component', async () => { + await page.goto(url.create) + await page.locator('#field-customArrayField >> .array-field__add-row').click() + await expect(page.locator('#field-customArrayField__0__text')).toBeVisible() + }) + // eslint-disable-next-line playwright/expect-expect test('should bypass min rows validation when no rows present and field is not required', async () => { await page.goto(url.create) @@ -313,7 +319,7 @@ describe('Array', () => { test('should externally update array rows and render custom fields', async () => { await page.goto(url.create) await page.locator('#updateArrayExternally').click() - await expect(page.locator('#custom-field')).toBeVisible() + await expect(page.locator('#custom-text-field')).toBeVisible() }) test('should not re-close initCollapsed true array rows on input in create new view', async () => { diff --git a/test/fields/collections/Array/index.ts b/test/fields/collections/Array/index.ts index dad95be90d6..df1abd2d150 100644 --- a/test/fields/collections/Array/index.ts +++ b/test/fields/collections/Array/index.ts @@ -194,16 +194,31 @@ const ArrayFields: CollectionConfig = { type: 'array', fields: [ { - name: 'customField', + name: 'customTextField', type: 'ui', admin: { components: { - Field: '/collections/Array/CustomField.js#CustomField', + Field: '/collections/Array/CustomTextField.js#CustomTextField', }, }, }, ], }, + { + name: 'customArrayField', + type: 'array', + admin: { + components: { + Field: '/collections/Array/CustomArrayField.js#CustomArrayField', + }, + }, + fields: [ + { + name: 'text', + type: 'text', + }, + ], + }, { name: 'ui', type: 'ui', diff --git a/test/fields/payload-types.ts b/test/fields/payload-types.ts index 33c53194056..69aca10d184 100644 --- a/test/fields/payload-types.ts +++ b/test/fields/payload-types.ts @@ -505,6 +505,12 @@ export interface ArrayField { id?: string | null; }[] | null; + customArrayField?: + | { + text?: string | null; + id?: string | null; + }[] + | null; updatedAt: string; createdAt: string; } @@ -2060,6 +2066,12 @@ export interface ArrayFieldsSelect { | { id?: T; }; + customArrayField?: + | T + | { + text?: T; + id?: T; + }; updatedAt?: T; createdAt?: T; } @@ -2071,257 +2083,42 @@ export interface BlockFieldsSelect { blocks?: | T | { - content?: - | T - | { - text?: T; - richText?: T; - id?: T; - blockName?: T; - }; - number?: - | T - | { - number?: T; - id?: T; - blockName?: T; - }; - subBlocks?: - | T - | { - subBlocks?: - | T - | { - text?: - | T - | { - text?: T; - id?: T; - blockName?: T; - }; - number?: - | T - | { - number?: T; - id?: T; - blockName?: T; - }; - }; - id?: T; - blockName?: T; - }; - tabs?: - | T - | { - textInCollapsible?: T; - textInRow?: T; - id?: T; - blockName?: T; - }; + content?: T | ContentBlockSelect; + number?: T | NumberBlockSelect; + subBlocks?: T | SubBlocksBlockSelect; + tabs?: T | TabsBlockSelect; }; duplicate?: | T | { - content?: - | T - | { - text?: T; - richText?: T; - id?: T; - blockName?: T; - }; - number?: - | T - | { - number?: T; - id?: T; - blockName?: T; - }; - subBlocks?: - | T - | { - subBlocks?: - | T - | { - text?: - | T - | { - text?: T; - id?: T; - blockName?: T; - }; - number?: - | T - | { - number?: T; - id?: T; - blockName?: T; - }; - }; - id?: T; - blockName?: T; - }; - tabs?: - | T - | { - textInCollapsible?: T; - textInRow?: T; - id?: T; - blockName?: T; - }; + content?: T | ContentBlockSelect; + number?: T | NumberBlockSelect; + subBlocks?: T | SubBlocksBlockSelect; + tabs?: T | TabsBlockSelect; }; collapsedByDefaultBlocks?: | T | { - localizedContent?: - | T - | { - text?: T; - richText?: T; - id?: T; - blockName?: T; - }; - localizedNumber?: - | T - | { - number?: T; - id?: T; - blockName?: T; - }; - localizedSubBlocks?: - | T - | { - subBlocks?: - | T - | { - text?: - | T - | { - text?: T; - id?: T; - blockName?: T; - }; - number?: - | T - | { - number?: T; - id?: T; - blockName?: T; - }; - }; - id?: T; - blockName?: T; - }; - localizedTabs?: - | T - | { - textInCollapsible?: T; - textInRow?: T; - id?: T; - blockName?: T; - }; + localizedContent?: T | LocalizedContentBlockSelect; + localizedNumber?: T | LocalizedNumberBlockSelect; + localizedSubBlocks?: T | LocalizedSubBlocksBlockSelect; + localizedTabs?: T | LocalizedTabsBlockSelect; }; disableSort?: | T | { - localizedContent?: - | T - | { - text?: T; - richText?: T; - id?: T; - blockName?: T; - }; - localizedNumber?: - | T - | { - number?: T; - id?: T; - blockName?: T; - }; - localizedSubBlocks?: - | T - | { - subBlocks?: - | T - | { - text?: - | T - | { - text?: T; - id?: T; - blockName?: T; - }; - number?: - | T - | { - number?: T; - id?: T; - blockName?: T; - }; - }; - id?: T; - blockName?: T; - }; - localizedTabs?: - | T - | { - textInCollapsible?: T; - textInRow?: T; - id?: T; - blockName?: T; - }; + localizedContent?: T | LocalizedContentBlockSelect; + localizedNumber?: T | LocalizedNumberBlockSelect; + localizedSubBlocks?: T | LocalizedSubBlocksBlockSelect; + localizedTabs?: T | LocalizedTabsBlockSelect; }; localizedBlocks?: | T | { - localizedContent?: - | T - | { - text?: T; - richText?: T; - id?: T; - blockName?: T; - }; - localizedNumber?: - | T - | { - number?: T; - id?: T; - blockName?: T; - }; - localizedSubBlocks?: - | T - | { - subBlocks?: - | T - | { - text?: - | T - | { - text?: T; - id?: T; - blockName?: T; - }; - number?: - | T - | { - number?: T; - id?: T; - blockName?: T; - }; - }; - id?: T; - blockName?: T; - }; - localizedTabs?: - | T - | { - textInCollapsible?: T; - textInRow?: T; - id?: T; - blockName?: T; - }; + localizedContent?: T | LocalizedContentBlockSelect; + localizedNumber?: T | LocalizedNumberBlockSelect; + localizedSubBlocks?: T | LocalizedSubBlocksBlockSelect; + localizedTabs?: T | LocalizedTabsBlockSelect; }; i18nBlocks?: | T @@ -2459,6 +2256,116 @@ export interface BlockFieldsSelect { updatedAt?: T; createdAt?: T; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "ContentBlock_select". + */ +export interface ContentBlockSelect { + text?: T; + richText?: T; + id?: T; + blockName?: T; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "NumberBlock_select". + */ +export interface NumberBlockSelect { + number?: T; + id?: T; + blockName?: T; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "SubBlocksBlock_select". + */ +export interface SubBlocksBlockSelect { + subBlocks?: + | T + | { + text?: + | T + | { + text?: T; + id?: T; + blockName?: T; + }; + number?: + | T + | { + number?: T; + id?: T; + blockName?: T; + }; + }; + id?: T; + blockName?: T; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "TabsBlock_select". + */ +export interface TabsBlockSelect { + textInCollapsible?: T; + textInRow?: T; + id?: T; + blockName?: T; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "localizedContentBlock_select". + */ +export interface LocalizedContentBlockSelect { + text?: T; + richText?: T; + id?: T; + blockName?: T; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "localizedNumberBlock_select". + */ +export interface LocalizedNumberBlockSelect { + number?: T; + id?: T; + blockName?: T; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "localizedSubBlocksBlock_select". + */ +export interface LocalizedSubBlocksBlockSelect { + subBlocks?: + | T + | { + text?: + | T + | { + text?: T; + id?: T; + blockName?: T; + }; + number?: + | T + | { + number?: T; + id?: T; + blockName?: T; + }; + }; + id?: T; + blockName?: T; +} +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "localizedTabsBlock_select". + */ +export interface LocalizedTabsBlockSelect { + textInCollapsible?: T; + textInRow?: T; + id?: T; + blockName?: T; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "checkbox-fields_select". @@ -3021,53 +2928,10 @@ export interface TabsFieldsSelect { blocks?: | T | { - content?: - | T - | { - text?: T; - richText?: T; - id?: T; - blockName?: T; - }; - number?: - | T - | { - number?: T; - id?: T; - blockName?: T; - }; - subBlocks?: - | T - | { - subBlocks?: - | T - | { - text?: - | T - | { - text?: T; - id?: T; - blockName?: T; - }; - number?: - | T - | { - number?: T; - id?: T; - blockName?: T; - }; - }; - id?: T; - blockName?: T; - }; - tabs?: - | T - | { - textInCollapsible?: T; - textInRow?: T; - id?: T; - blockName?: T; - }; + content?: T | ContentBlockSelect; + number?: T | NumberBlockSelect; + subBlocks?: T | SubBlocksBlockSelect; + tabs?: T | TabsBlockSelect; }; group?: | T @@ -3077,24 +2941,7 @@ export interface TabsFieldsSelect { textInRow?: T; numberInRow?: T; json?: T; - tab?: - | T - | { - array?: - | T - | { - text?: T; - id?: T; - }; - text?: T; - defaultValue?: T; - arrayInRow?: - | T - | { - textInArrayInRow?: T; - id?: T; - }; - }; + tab?: T | TabWithNameSelect; namedTabWithDefaultValue?: | T | { @@ -3144,6 +2991,26 @@ export interface TabsFieldsSelect { updatedAt?: T; createdAt?: T; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "TabWithName_select". + */ +export interface TabWithNameSelect { + array?: + | T + | { + text?: T; + id?: T; + }; + text?: T; + defaultValue?: T; + arrayInRow?: + | T + | { + textInArrayInRow?: T; + id?: T; + }; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "text-fields_select".