diff --git a/packages/ui/src/forms/Form/fieldReducer.ts b/packages/ui/src/forms/Form/fieldReducer.ts index 689123f46..cb655226d 100644 --- a/packages/ui/src/forms/Form/fieldReducer.ts +++ b/packages/ui/src/forms/Form/fieldReducer.ts @@ -198,6 +198,7 @@ export function fieldReducer(state: FormState, action: FieldAction): FormState { ...flattenRows(path, rows), [path]: { ...state[path], + requiresRender: true, rows: rowStateCopy, }, } diff --git a/test/fields/collections/Lexical/e2e/main/e2e.spec.ts b/test/fields/collections/Lexical/e2e/main/e2e.spec.ts index 75bcd895a..a5a96a29c 100644 --- a/test/fields/collections/Lexical/e2e/main/e2e.spec.ts +++ b/test/fields/collections/Lexical/e2e/main/e2e.spec.ts @@ -1044,6 +1044,30 @@ describe('lexicalMain', () => { await expect(htmlOutput).toBeVisible() }) + test('ensure lexical fields in blocks have correct value when moving blocks', async () => { + // Previously, we had the issue that the lexical field values did not update when moving blocks, as the MOVE_ROW form action did not request + // re-rendering of server components + 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('#blocks-row-0 .LexicalEditorTheme__paragraph')).toContainText('1') + await expect(page.locator('#blocks-row-0 .section-title__input')).toHaveValue('1') // block name + await expect(page.locator('#blocks-row-1 .LexicalEditorTheme__paragraph')).toContainText('2') + await expect(page.locator('#blocks-row-1 .section-title__input')).toHaveValue('2') // block name + + // Move block 1 to the end + await page.locator('#blocks-row-0 .array-actions__button').click() + await expect(page.locator('#blocks-row-0 .popup__content')).toBeVisible() + + await page.locator('#blocks-row-0 .popup__content').getByText('Move Down').click() + + await expect(page.locator('#blocks-row-0 .LexicalEditorTheme__paragraph')).toContainText('2') + await expect(page.locator('#blocks-row-0 .section-title__input')).toHaveValue('2') // block name + await expect(page.locator('#blocks-row-1 .LexicalEditorTheme__paragraph')).toContainText('1') + await expect(page.locator('#blocks-row-1 .section-title__input')).toHaveValue('1') // block name + }) + describe('localization', () => { test.skip('ensure simple localized lexical field works', async () => { await navigateToLexicalFields(true, true) diff --git a/test/fields/collections/LexicalInBlock/index.ts b/test/fields/collections/LexicalInBlock/index.ts new file mode 100644 index 000000000..b3e3c2e0f --- /dev/null +++ b/test/fields/collections/LexicalInBlock/index.ts @@ -0,0 +1,22 @@ +import type { CollectionConfig } from 'payload' + +export const LexicalInBlock: CollectionConfig = { + slug: 'LexicalInBlock', + fields: [ + { + name: 'blocks', + type: 'blocks', + blocks: [ + { + slug: 'lexicalInBlock2', + fields: [ + { + name: 'lexical', + type: 'richText', + }, + ], + }, + ], + }, + ], +} diff --git a/test/fields/config.ts b/test/fields/config.ts index 8bc8abec7..12ca0d125 100644 --- a/test/fields/config.ts +++ b/test/fields/config.ts @@ -19,6 +19,7 @@ import GroupFields from './collections/Group/index.js' import IndexedFields from './collections/Indexed/index.js' import JSONFields from './collections/JSON/index.js' import { LexicalFields } from './collections/Lexical/index.js' +import { LexicalInBlock } from './collections/LexicalInBlock/index.js' import { LexicalLocalizedFields } from './collections/LexicalLocalized/index.js' import { LexicalMigrateFields } from './collections/LexicalMigrate/index.js' import { LexicalObjectReferenceBugCollection } from './collections/LexicalObjectReferenceBug/index.js' @@ -63,6 +64,8 @@ export const collectionSlugs: CollectionConfig[] = [ }, ], }, + LexicalInBlock, + ArrayFields, BlockFields, CheckboxFields, diff --git a/test/fields/payload-types.ts b/test/fields/payload-types.ts index 9296fdd2e..8f02f35f1 100644 --- a/test/fields/payload-types.ts +++ b/test/fields/payload-types.ts @@ -33,6 +33,7 @@ export interface Config { 'lexical-localized-fields': LexicalLocalizedField; lexicalObjectReferenceBug: LexicalObjectReferenceBug; users: User; + LexicalInBlock: LexicalInBlock; 'array-fields': ArrayField; 'block-fields': BlockField; 'checkbox-fields': CheckboxField; @@ -75,6 +76,7 @@ export interface Config { 'lexical-localized-fields': LexicalLocalizedFieldsSelect | LexicalLocalizedFieldsSelect; lexicalObjectReferenceBug: LexicalObjectReferenceBugSelect | LexicalObjectReferenceBugSelect; users: UsersSelect | UsersSelect; + LexicalInBlock: LexicalInBlockSelect | LexicalInBlockSelect; 'array-fields': ArrayFieldsSelect | ArrayFieldsSelect; 'block-fields': BlockFieldsSelect | BlockFieldsSelect; 'checkbox-fields': CheckboxFieldsSelect | CheckboxFieldsSelect; @@ -123,9 +125,9 @@ export interface Config { user: User & { collection: 'users'; }; - jobs?: { + jobs: { tasks: unknown; - workflows?: unknown; + workflows: unknown; }; } export interface UserAuthOperations { @@ -394,6 +396,37 @@ export interface User { lockUntil?: string | null; password?: string | null; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "LexicalInBlock". + */ +export interface LexicalInBlock { + id: string; + blocks?: + | { + lexical?: { + 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; + id?: string | null; + blockName?: string | null; + blockType: 'lexicalInBlock2'; + }[] + | null; + updatedAt: string; + createdAt: string; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "array-fields". @@ -1783,6 +1816,10 @@ export interface PayloadLockedDocument { relationTo: 'users'; value: string | User; } | null) + | ({ + relationTo: 'LexicalInBlock'; + value: string | LexicalInBlock; + } | null) | ({ relationTo: 'array-fields'; value: string | ArrayField; @@ -2025,6 +2062,25 @@ export interface UsersSelect { loginAttempts?: T; lockUntil?: T; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "LexicalInBlock_select". + */ +export interface LexicalInBlockSelect { + blocks?: + | T + | { + lexicalInBlock2?: + | T + | { + lexical?: T; + id?: T; + blockName?: T; + }; + }; + updatedAt?: T; + createdAt?: T; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "array-fields_select". diff --git a/test/fields/seed.ts b/test/fields/seed.ts index 895a98dd5..3f1150917 100644 --- a/test/fields/seed.ts +++ b/test/fields/seed.ts @@ -367,7 +367,7 @@ export const seed = async (_payload: Payload) => { collection: lexicalLocalizedFieldsSlug, data: { title: 'Localized Lexical en', - lexicalBlocksLocalized: textToLexicalJSON({ text: 'English text' }) as any, + lexicalBlocksLocalized: textToLexicalJSON({ text: 'English text' }), lexicalBlocksSubLocalized: generateLexicalLocalizedRichText( 'Shared text', 'English text in block', @@ -381,7 +381,7 @@ export const seed = async (_payload: Payload) => { await _payload.create({ collection: lexicalRelationshipFieldsSlug, data: { - richText: textToLexicalJSON({ text: 'English text' }) as any, + richText: textToLexicalJSON({ text: 'English text' }), }, depth: 0, overrideAccess: true, @@ -392,7 +392,7 @@ export const seed = async (_payload: Payload) => { id: lexicalLocalizedDoc1.id, data: { title: 'Localized Lexical es', - lexicalBlocksLocalized: textToLexicalJSON({ text: 'Spanish text' }) as any, + lexicalBlocksLocalized: textToLexicalJSON({ text: 'Spanish text' }), lexicalBlocksSubLocalized: generateLexicalLocalizedRichText( 'Shared text', 'Spanish text in block', @@ -408,10 +408,7 @@ export const seed = async (_payload: Payload) => { collection: lexicalLocalizedFieldsSlug, data: { title: 'Localized Lexical en 2', - lexicalSimple: textToLexicalJSON({ - text: 'English text 2', - lexicalLocalizedRelID: lexicalLocalizedDoc1.id, - }), + lexicalBlocksLocalized: textToLexicalJSON({ text: 'English text 2', lexicalLocalizedRelID: lexicalLocalizedDoc1.id, @@ -431,10 +428,7 @@ export const seed = async (_payload: Payload) => { id: lexicalLocalizedDoc2.id, data: { title: 'Localized Lexical es 2', - lexicalSimple: textToLexicalJSON({ - text: 'Spanish text 2', - lexicalLocalizedRelID: lexicalLocalizedDoc1.id, - }), + lexicalBlocksLocalized: textToLexicalJSON({ text: 'Spanish text 2', lexicalLocalizedRelID: lexicalLocalizedDoc1.id, @@ -479,6 +473,24 @@ export const seed = async (_payload: Payload) => { text: 'text', }, }) + + await _payload.create({ + collection: 'LexicalInBlock', + data: { + blocks: [ + { + blockType: 'lexicalInBlock2', + blockName: '1', + lexical: textToLexicalJSON({ text: '1' }), + }, + { + blockType: 'lexicalInBlock2', + blockName: '2', + lexical: textToLexicalJSON({ text: '2' }), + }, + ], + }, + }) } export async function clearAndSeedEverything(_payload: Payload) {