From 35df899547bb41da3f45dbb36b4301b7a5f0279d Mon Sep 17 00:00:00 2001 From: Alessio Gravili Date: Mon, 30 Dec 2024 23:33:43 -0700 Subject: [PATCH] fix(richtext-lexical): various issues for lexical sub-fields due to incorrectly generated client field schema map (#10276) Fixes https://github.com/payloadcms/payload/issues/9905, https://github.com/payloadcms/payload/issues/9660 Single lexical fields were represented file in the schema map (`path => field`). In the client schema map however, they were incorrectly represented like this: `path => { fields: [field] } ` --- .../richtext-lexical/src/field/rscEntry.tsx | 4 +++ .../traverseFields.ts | 18 +++++++++--- .../Lexical/e2e/blocks/e2e.spec.ts | 20 +++++++++++++ .../collections/LexicalInBlock/index.ts | 29 +++++++++++++++++++ test/fields/payload-types.ts | 16 ++++++++++ test/fields/seed.ts | 22 ++++++++++++++ 6 files changed, 105 insertions(+), 4 deletions(-) diff --git a/packages/richtext-lexical/src/field/rscEntry.tsx b/packages/richtext-lexical/src/field/rscEntry.tsx index 058c8dc34e5..3fe985e6bd3 100644 --- a/packages/richtext-lexical/src/field/rscEntry.tsx +++ b/packages/richtext-lexical/src/field/rscEntry.tsx @@ -30,6 +30,10 @@ export const RscEntryLexicalField: React.FC< const path = args.path ?? (args.clientField as RichTextFieldClient).name const schemaPath = args.schemaPath ?? path + if (!(args?.clientField as RichTextFieldClient)?.name) { + throw new Error('Initialized lexical RSC field without a field name') + } + const { clientFeatures, featureClientSchemaMap } = initLexicalFeatures({ clientFieldSchemaMap: args.clientFieldSchemaMap, fieldSchemaMap: args.fieldSchemaMap, diff --git a/packages/ui/src/utilities/buildClientFieldSchemaMap/traverseFields.ts b/packages/ui/src/utilities/buildClientFieldSchemaMap/traverseFields.ts index 7bea4991ebe..42dfac268df 100644 --- a/packages/ui/src/utilities/buildClientFieldSchemaMap/traverseFields.ts +++ b/packages/ui/src/utilities/buildClientFieldSchemaMap/traverseFields.ts @@ -5,6 +5,7 @@ import { type ClientField, type ClientFieldSchemaMap, createClientFields, + type Field, type FieldSchemaMap, type Payload, } from 'payload' @@ -108,16 +109,25 @@ export const traverseFields = ({ // Now loop through them, convert each entry to a client field and add it to the client schema map for (const [path, subField] of richTextFieldSchemaMap.entries()) { + // check if fields is the only key in the subField object + const isFieldsOnly = Object.keys(subField).length === 1 && 'fields' in subField + const clientFields = createClientFields({ defaultIDType: payload.config.db.defaultIDType, disableAddingID: true, - fields: 'fields' in subField ? subField.fields : [subField], + fields: isFieldsOnly ? subField.fields : [subField as Field], i18n, importMap: payload.importMap, }) - clientSchemaMap.set(path, { - fields: clientFields, - }) + + clientSchemaMap.set( + path, + isFieldsOnly + ? { + fields: clientFields, + } + : clientFields[0], + ) } break } diff --git a/test/fields/collections/Lexical/e2e/blocks/e2e.spec.ts b/test/fields/collections/Lexical/e2e/blocks/e2e.spec.ts index 4f2f770b807..9e1ff90a583 100644 --- a/test/fields/collections/Lexical/e2e/blocks/e2e.spec.ts +++ b/test/fields/collections/Lexical/e2e/blocks/e2e.spec.ts @@ -1174,5 +1174,25 @@ describe('lexicalBlocks', () => { expect(height2).toBe(74) }).toPass() }) + + test('ensure nested lexical field displays field label and description', async () => { + // Previously, we had the issue that nested lexical fields did not display the field label and description, as + // their client field configs were generated incorrectly on the server. + await page.goto('http://localhost:3000/admin/collections/LexicalInBlock?limit=10') + await page.locator('.cell-id a').first().click() + await page.waitForURL(`**/collections/LexicalInBlock/**`) + + await expect( + page.locator('.lexical-block-blockInLexical .render-fields label.field-label'), + ).toHaveText('My Label*') + await expect( + page.locator('.lexical-block-blockInLexical .render-fields .required'), + ).toHaveText('*') + await expect( + page.locator( + '.lexical-block-blockInLexical .render-fields .field-description-lexicalInBlock', + ), + ).toHaveText('Some Description') + }) }) }) diff --git a/test/fields/collections/LexicalInBlock/index.ts b/test/fields/collections/LexicalInBlock/index.ts index b3e3c2e0fa8..81bb3ca737c 100644 --- a/test/fields/collections/LexicalInBlock/index.ts +++ b/test/fields/collections/LexicalInBlock/index.ts @@ -1,8 +1,37 @@ import type { CollectionConfig } from 'payload' +import { BlocksFeature, lexicalEditor } from '@payloadcms/richtext-lexical' + export const LexicalInBlock: CollectionConfig = { slug: 'LexicalInBlock', fields: [ + { + name: 'content', + type: 'richText', + editor: lexicalEditor({ + features: [ + BlocksFeature({ + blocks: [ + { + slug: 'blockInLexical', + fields: [ + { + name: 'lexicalInBlock', + label: 'My Label', + type: 'richText', + required: true, + editor: lexicalEditor(), + admin: { + description: 'Some Description', + }, + }, + ], + }, + ], + }), + ], + }), + }, { name: 'blocks', type: 'blocks', diff --git a/test/fields/payload-types.ts b/test/fields/payload-types.ts index 087a0204851..d5b4cf705b6 100644 --- a/test/fields/payload-types.ts +++ b/test/fields/payload-types.ts @@ -412,6 +412,21 @@ export interface User { */ export interface LexicalInBlock { id: string; + content?: { + root: { + type: string; + children: { + type: string; + version: number; + [k: string]: unknown; + }[]; + direction: ('ltr' | 'rtl') | null; + format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; + indent: number; + version: number; + }; + [k: string]: unknown; + } | null; blocks?: | { lexical?: { @@ -2041,6 +2056,7 @@ export interface UsersSelect { * via the `definition` "LexicalInBlock_select". */ export interface LexicalInBlockSelect { + content?: T; blocks?: | T | { diff --git a/test/fields/seed.ts b/test/fields/seed.ts index cb7abec9d77..456667995cd 100644 --- a/test/fields/seed.ts +++ b/test/fields/seed.ts @@ -481,6 +481,28 @@ export const seed = async (_payload: Payload) => { await _payload.create({ collection: 'LexicalInBlock', data: { + content: { + root: { + children: [ + { + format: '', + type: 'block', + version: 2, + fields: { + id: '6773773284be8978db7a498d', + lexicalInBlock: textToLexicalJSON({ text: 'text' }), + blockName: '', + blockType: 'blockInLexical', + }, + }, + ], + direction: null, + format: '', + indent: 0, + type: 'root', + version: 1, + }, + }, blocks: [ { blockType: 'lexicalInBlock2',