fix: error when passing functions to array or block fields labels property (#11056)
Fixes https://github.com/payloadcms/payload/issues/11055 Functions passed to array field, block field or block `labels` were not properly handled in the client config, causing those functions to be sent to the client. This leads to a "Functions cannot be passed directly to Client Component" error
This commit is contained in:
@@ -1,7 +1,10 @@
|
|||||||
|
/* eslint-disable perfectionist/sort-switch-case */
|
||||||
|
// Keep perfectionist/sort-switch-case disabled - it incorrectly messes up the ordering of the switch cases, causing it to break
|
||||||
import type { I18nClient } from '@payloadcms/translations'
|
import type { I18nClient } from '@payloadcms/translations'
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
AdminClient,
|
AdminClient,
|
||||||
|
ArrayFieldClient,
|
||||||
BlockJSX,
|
BlockJSX,
|
||||||
BlocksFieldClient,
|
BlocksFieldClient,
|
||||||
ClientBlock,
|
ClientBlock,
|
||||||
@@ -144,7 +147,27 @@ export const createClientField = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (incomingField.type) {
|
switch (incomingField.type) {
|
||||||
case 'array':
|
case 'array': {
|
||||||
|
if (incomingField.labels) {
|
||||||
|
const field = clientField as unknown as ArrayFieldClient
|
||||||
|
|
||||||
|
field.labels = {} as unknown as LabelsClient
|
||||||
|
|
||||||
|
if (incomingField.labels.singular) {
|
||||||
|
if (typeof incomingField.labels.singular === 'function') {
|
||||||
|
field.labels.singular = incomingField.labels.singular({ t: i18n.t })
|
||||||
|
} else {
|
||||||
|
field.labels.singular = incomingField.labels.singular
|
||||||
|
}
|
||||||
|
if (typeof incomingField.labels.plural === 'function') {
|
||||||
|
field.labels.plural = incomingField.labels.plural({ t: i18n.t })
|
||||||
|
} else {
|
||||||
|
field.labels.plural = incomingField.labels.plural
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// falls through
|
||||||
case 'collapsible':
|
case 'collapsible':
|
||||||
case 'group':
|
case 'group':
|
||||||
case 'row': {
|
case 'row': {
|
||||||
@@ -168,6 +191,23 @@ export const createClientField = ({
|
|||||||
case 'blocks': {
|
case 'blocks': {
|
||||||
const field = clientField as unknown as BlocksFieldClient
|
const field = clientField as unknown as BlocksFieldClient
|
||||||
|
|
||||||
|
if (incomingField.labels) {
|
||||||
|
field.labels = {} as unknown as LabelsClient
|
||||||
|
|
||||||
|
if (incomingField.labels.singular) {
|
||||||
|
if (typeof incomingField.labels.singular === 'function') {
|
||||||
|
field.labels.singular = incomingField.labels.singular({ t: i18n.t })
|
||||||
|
} else {
|
||||||
|
field.labels.singular = incomingField.labels.singular
|
||||||
|
}
|
||||||
|
if (typeof incomingField.labels.plural === 'function') {
|
||||||
|
field.labels.plural = incomingField.labels.plural({ t: i18n.t })
|
||||||
|
} else {
|
||||||
|
field.labels.plural = incomingField.labels.plural
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (incomingField.blocks?.length) {
|
if (incomingField.blocks?.length) {
|
||||||
for (let i = 0; i < incomingField.blocks.length; i++) {
|
for (let i = 0; i < incomingField.blocks.length; i++) {
|
||||||
const block = incomingField.blocks[i]
|
const block = incomingField.blocks[i]
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export interface Config {
|
|||||||
collections: {
|
collections: {
|
||||||
posts: Post;
|
posts: Post;
|
||||||
media: Media;
|
media: Media;
|
||||||
|
test: Test;
|
||||||
users: User;
|
users: User;
|
||||||
'payload-locked-documents': PayloadLockedDocument;
|
'payload-locked-documents': PayloadLockedDocument;
|
||||||
'payload-preferences': PayloadPreference;
|
'payload-preferences': PayloadPreference;
|
||||||
@@ -22,6 +23,7 @@ export interface Config {
|
|||||||
collectionsSelect: {
|
collectionsSelect: {
|
||||||
posts: PostsSelect<false> | PostsSelect<true>;
|
posts: PostsSelect<false> | PostsSelect<true>;
|
||||||
media: MediaSelect<false> | MediaSelect<true>;
|
media: MediaSelect<false> | MediaSelect<true>;
|
||||||
|
test: TestSelect<false> | TestSelect<true>;
|
||||||
users: UsersSelect<false> | UsersSelect<true>;
|
users: UsersSelect<false> | UsersSelect<true>;
|
||||||
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
||||||
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
|
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
|
||||||
@@ -133,6 +135,28 @@ export interface Media {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "test".
|
||||||
|
*/
|
||||||
|
export interface Test {
|
||||||
|
id: string;
|
||||||
|
test_array?:
|
||||||
|
| {
|
||||||
|
test_text?:
|
||||||
|
| {
|
||||||
|
text?: string | null;
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: 'test_text';
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
|
id?: string | null;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "users".
|
* via the `definition` "users".
|
||||||
@@ -165,6 +189,10 @@ export interface PayloadLockedDocument {
|
|||||||
relationTo: 'media';
|
relationTo: 'media';
|
||||||
value: string | Media;
|
value: string | Media;
|
||||||
} | null)
|
} | null)
|
||||||
|
| ({
|
||||||
|
relationTo: 'test';
|
||||||
|
value: string | Test;
|
||||||
|
} | null)
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'users';
|
relationTo: 'users';
|
||||||
value: string | User;
|
value: string | User;
|
||||||
@@ -273,6 +301,30 @@ export interface MediaSelect<T extends boolean = true> {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "test_select".
|
||||||
|
*/
|
||||||
|
export interface TestSelect<T extends boolean = true> {
|
||||||
|
test_array?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
test_text?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
test_text?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
text?: T;
|
||||||
|
id?: T;
|
||||||
|
blockName?: T;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
updatedAt?: T;
|
||||||
|
createdAt?: T;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "users_select".
|
* via the `definition` "users_select".
|
||||||
|
|||||||
@@ -133,6 +133,13 @@ describe('Array', () => {
|
|||||||
await expect(page.locator('#field-items #items-row-0 .row-label')).toContainText('Item 01')
|
await expect(page.locator('#field-items #items-row-0 .row-label')).toContainText('Item 01')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('ensure functions passed to array field labels property are respected', async () => {
|
||||||
|
await page.goto(url.create)
|
||||||
|
|
||||||
|
const arrayWithLabelsField = page.locator('#field-arrayWithLabels')
|
||||||
|
await expect(arrayWithLabelsField.locator('.array-field__add-row')).toHaveText('Add Account')
|
||||||
|
})
|
||||||
|
|
||||||
describe('row manipulation', () => {
|
describe('row manipulation', () => {
|
||||||
test('should add, remove and duplicate rows', async () => {
|
test('should add, remove and duplicate rows', async () => {
|
||||||
const assertText0 = 'array row 1'
|
const assertText0 = 'array row 1'
|
||||||
|
|||||||
@@ -246,6 +246,20 @@ const ArrayFields: CollectionConfig = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'arrayWithLabels',
|
||||||
|
type: 'array',
|
||||||
|
labels: {
|
||||||
|
singular: ({ t }) => t('authentication:account'),
|
||||||
|
plural: ({ t }) => t('authentication:generate'),
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'text',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
slug: arrayFieldsSlug,
|
slug: arrayFieldsSlug,
|
||||||
versions: true,
|
versions: true,
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ describe('Block fields', () => {
|
|||||||
.click()
|
.click()
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
await page.locator('#field-blocks .blocks-field__row .blocks-field__block-header', {
|
page.locator('#field-blocks .blocks-field__row .blocks-field__block-header', {
|
||||||
hasText: 'Custom Block Label',
|
hasText: 'Custom Block Label',
|
||||||
}),
|
}),
|
||||||
).toBeVisible()
|
).toBeVisible()
|
||||||
@@ -292,6 +292,16 @@ describe('Block fields', () => {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('ensure functions passed to blocks field labels property are respected', async () => {
|
||||||
|
await page.goto(url.create)
|
||||||
|
|
||||||
|
const blocksFieldWithLabels = page.locator('#field-blockWithLabels')
|
||||||
|
|
||||||
|
await expect(blocksFieldWithLabels.locator('.blocks-field__drawer-toggler')).toHaveText(
|
||||||
|
'Add Account',
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
describe('row manipulation', () => {
|
describe('row manipulation', () => {
|
||||||
describe('react hooks', () => {
|
describe('react hooks', () => {
|
||||||
test('should add 2 new block rows', async () => {
|
test('should add 2 new block rows', async () => {
|
||||||
|
|||||||
@@ -373,6 +373,29 @@ const BlockFields: CollectionConfig = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'blockWithLabels',
|
||||||
|
type: 'blocks',
|
||||||
|
labels: {
|
||||||
|
singular: ({ t }) => t('authentication:account'),
|
||||||
|
plural: ({ t }) => t('authentication:generate'),
|
||||||
|
},
|
||||||
|
blocks: [
|
||||||
|
{
|
||||||
|
labels: {
|
||||||
|
singular: ({ t }) => t('authentication:account'),
|
||||||
|
plural: ({ t }) => t('authentication:generate'),
|
||||||
|
},
|
||||||
|
slug: 'text',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'text',
|
||||||
|
type: 'text',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -595,6 +595,12 @@ export interface ArrayField {
|
|||||||
id?: string | null;
|
id?: string | null;
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
|
arrayWithLabels?:
|
||||||
|
| {
|
||||||
|
text?: string | null;
|
||||||
|
id?: string | null;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
@@ -727,6 +733,14 @@ export interface BlockField {
|
|||||||
blockType: 'relationships';
|
blockType: 'relationships';
|
||||||
}[]
|
}[]
|
||||||
| null;
|
| null;
|
||||||
|
blockWithLabels?:
|
||||||
|
| {
|
||||||
|
text?: string | null;
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: 'text';
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
@@ -2265,6 +2279,12 @@ export interface ArrayFieldsSelect<T extends boolean = true> {
|
|||||||
text?: T;
|
text?: T;
|
||||||
id?: T;
|
id?: T;
|
||||||
};
|
};
|
||||||
|
arrayWithLabels?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
text?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
}
|
}
|
||||||
@@ -2446,6 +2466,17 @@ export interface BlockFieldsSelect<T extends boolean = true> {
|
|||||||
blockName?: T;
|
blockName?: T;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
blockWithLabels?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
text?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
text?: T;
|
||||||
|
id?: T;
|
||||||
|
blockName?: T;
|
||||||
|
};
|
||||||
|
};
|
||||||
updatedAt?: T;
|
updatedAt?: T;
|
||||||
createdAt?: T;
|
createdAt?: T;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user