From a62cdc89d887780b33568fde9ca45c047ffd489f Mon Sep 17 00:00:00 2001 From: Philipp Schneider <47689073+philipp-tailor@users.noreply.github.com> Date: Fri, 2 May 2025 16:18:11 +0200 Subject: [PATCH] fix(ui): blockType ignored when merging server form state (#12207) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In this case, the `blockType` property is created on the server, but - prior to this fix - was discarded on the client in [`fieldReducer.ts`](https://github.com/payloadcms/payload/blob/main/packages/ui/src/forms/Form/fieldReducer.ts#L186-L198) via [`mergerServerFormState.ts`](https://github.com/payloadcms/payload/blob/b9832f40e41fff0a5a5b7bf7b08032aa2d6e8b4e/packages/ui/src/forms/Form/mergeServerFormState.ts#L29-L31), because the field's path neither existed in the client's form state, nor was it marked as `addedByServer`. This caused later calls to POST requests to form state to send without the `blockType` key for block rows, which in turn caused `addFieldStatePromise.ts` to throw the following error: ``` Block with type "undefined" was found in block data, but no block with that type is defined in the config for field with schema path ${schemaPath}. ``` This prevented the client side form state update from completing, and if the form state was saved, broke the document. This is a follow-up to #12131, which treated the symptom, but not the cause. The original issue seems to have been introduced in https://github.com/payloadcms/payload/releases/tag/v3.34.0. It's unclear to me whether this issue is connected to block E2E tests having been disabled in the same release in https://github.com/payloadcms/payload/pull/11988. ## How to reproduce ### Collection configuration ```ts const RICH_TEXT_BLOCK_TYPE = 'richTextBlockType' const RichTextBlock: Block = { slug: RICH_TEXT_BLOCK_TYPE, interfaceName: 'RichTextBlock', fields: [ { name: 'richTextBlockField', label: 'Rich Text Field in Block Field', type: 'richText', editor: lexicalEditor({}), required: true, }, ], } const MyCollection: CollectionConfig = { slug: 'my-collection-slug, fields: [ { name: 'arrayField', label: 'Array Field', type: 'array', fields: [ { name: 'blockField', type: 'blocks', blocks: [RichTextBlock], required: true, }, ], }, ] } export default MyCollection ``` ### Steps - Press "Add Array Field" --> ✅ 1st block with rich text is added - Press "Add Array Field" a 2nd time ### Result - 🛑 2nd block is indefinitely in loading state (side-note: the form UI should preferably explicitly indicate the error). - 🛑 If saving the document, it is corrupted and will only show a blank page (also not indicating any error). Client side: Untitled API error: image Client side, when saving and re-opening document (API error of `GET /admin/collections/${myCollection}/${documentId}` is the same (arguably the HTTP response status code shouldn't be `200`)): image ### Result after fix - `blockType` is sent from the client to the server. - ✅ 2nd block with rich text is added. - ✅ Document does not break when saving & re-opening. Untitled --------- Co-authored-by: Jacob Fletcher --- .../forms/fieldSchemasToFormState/addFieldStatePromise.ts | 4 ++++ test/form-state/int.spec.ts | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/packages/ui/src/forms/fieldSchemasToFormState/addFieldStatePromise.ts b/packages/ui/src/forms/fieldSchemasToFormState/addFieldStatePromise.ts index b3896560dc..56035e4d1e 100644 --- a/packages/ui/src/forms/fieldSchemasToFormState/addFieldStatePromise.ts +++ b/packages/ui/src/forms/fieldSchemasToFormState/addFieldStatePromise.ts @@ -456,6 +456,10 @@ export const addFieldStatePromise = async (args: AddFieldStatePromiseArgs): Prom value: row.blockType, } + if (addedByServer) { + state[fieldKey].addedByServer = addedByServer + } + if (includeSchema) { state[fieldKey].fieldSchema = block.fields.find( (blockField) => 'name' in blockField && blockField.name === 'blockType', diff --git a/test/form-state/int.spec.ts b/test/form-state/int.spec.ts index 525e99e419..d81d4ae73f 100644 --- a/test/form-state/int.spec.ts +++ b/test/form-state/int.spec.ts @@ -228,6 +228,12 @@ describe('Form State', () => { collection: postsSlug, data: { title: 'Test Post', + blocks: [ + { + blockType: 'text', + text: 'Test block', + }, + ], }, }) @@ -248,6 +254,7 @@ describe('Form State', () => { }) expect(state.title?.addedByServer).toBe(true) + expect(state['blocks.0.blockType']?.addedByServer).toBe(true) // Ensure that `addedByServer` is removed after being received by the client const newState = mergeServerFormState({