From 3a6d4834495ff4df98648d8b5fa66713d6573bf8 Mon Sep 17 00:00:00 2001 From: "Md. Yakub Hossain" <1830968@iub.edu.bd> Date: Fri, 7 Aug 2026 23:37:07 +0600 Subject: [PATCH 1/2] fix(ui): strip lastRenderedPath from copied form state so pasted rows re-render --- .../elements/ClipboardAction/mergeFormStateFromClipboard.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/elements/ClipboardAction/mergeFormStateFromClipboard.ts b/packages/ui/src/elements/ClipboardAction/mergeFormStateFromClipboard.ts index 04c492cb1ff..76b5c93a54a 100644 --- a/packages/ui/src/elements/ClipboardAction/mergeFormStateFromClipboard.ts +++ b/packages/ui/src/elements/ClipboardAction/mergeFormStateFromClipboard.ts @@ -25,14 +25,14 @@ export function reduceFormStateByPath({ continue } - const { customComponents: _, validate: __, ...field } = formState[key] + const { customComponents: _, lastRenderedPath: __, validate: ___, ...field } = formState[key] if (Array.isArray(field.rows)) { field.rows = field.rows.map((row) => { if (!row || typeof row !== 'object') { return row } - const { customComponents: _, ...serializableRow } = row + const { customComponents: _, lastRenderedPath: __, ...serializableRow } = row return serializableRow }) } From 14b6236bafa2001f1c0df70d3b208ea189cafcb7 Mon Sep 17 00:00:00 2001 From: "Md. Yakub Hossain" <1830968@iub.edu.bd> Date: Fri, 7 Aug 2026 23:37:08 +0600 Subject: [PATCH 2/2] fix(ui): add tests for lastRenderedPath stripping in reduceFormStateByPath --- .../mergeFormStateFromClipboard.spec.ts | 79 ++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/packages/ui/src/elements/ClipboardAction/mergeFormStateFromClipboard.spec.ts b/packages/ui/src/elements/ClipboardAction/mergeFormStateFromClipboard.spec.ts index bef4746ea19..e3e0aeeaa70 100644 --- a/packages/ui/src/elements/ClipboardAction/mergeFormStateFromClipboard.spec.ts +++ b/packages/ui/src/elements/ClipboardAction/mergeFormStateFromClipboard.spec.ts @@ -3,7 +3,7 @@ import type { FormState } from 'payload' import ObjectIdImport from 'bson-objectid' import { describe, expect, it } from 'vitest' -import { mergeFormStateFromClipboard } from './mergeFormStateFromClipboard.js' +import { mergeFormStateFromClipboard, reduceFormStateByPath } from './mergeFormStateFromClipboard.js' import type { ClipboardPasteData } from './types.js' const ObjectId = ( @@ -550,3 +550,80 @@ describe('mergeFormStateFromClipboard', () => { }) }) }) + +describe('reduceFormStateByPath', () => { + it('should strip lastRenderedPath from fields and rows so pasted content re-renders', () => { + const formState: FormState = { + layout: { + valid: true, + value: 1, + initialValue: 1, + rows: [ + { + id: 'row-1', + blockType: 'content', + isLoading: false, + lastRenderedPath: 'layout.0', + }, + ], + }, + 'layout.0.id': { + value: 'row-1', + valid: true, + }, + 'layout.0.richText': { + value: 'test content', + valid: true, + lastRenderedPath: 'layout.0.richText', + }, + } + + const result = reduceFormStateByPath({ + formState, + path: 'layout', + }) + + // lastRenderedPath must not survive the copy, otherwise pasting back onto the + // same path makes the server skip re-rendering and the block body renders blank + expect(result.layout.rows![0].lastRenderedPath).toBeUndefined() + expect(result['layout.0.richText'].lastRenderedPath).toBeUndefined() + + // The rest of the state is preserved + expect(result.layout.rows![0].id).toEqual('row-1') + expect(result.layout.rows![0].blockType).toEqual('content') + expect(result['layout.0.richText'].value).toEqual('test content') + expect(result['layout.0.id'].value).toEqual('row-1') + }) + + it('should strip lastRenderedPath when copying a single row', () => { + const formState: FormState = { + 'layout.0.nested': { + valid: true, + value: 1, + rows: [ + { + id: 'nested-row-1', + isLoading: false, + lastRenderedPath: 'layout.0.nested.0', + }, + ], + }, + 'layout.0.richText': { + value: 'row content', + valid: true, + lastRenderedPath: 'layout.0.richText', + }, + } + + const result = reduceFormStateByPath({ + formState, + path: 'layout', + rowIndex: 0, + }) + + expect(result['layout.0.richText'].lastRenderedPath).toBeUndefined() + expect(result['layout.0.nested'].rows![0].lastRenderedPath).toBeUndefined() + expect(result['layout.0.richText'].value).toEqual('row content') + expect(result['layout.0.nested'].rows![0].id).toEqual('nested-row-1') + }) +})