Skip to content

Commit 0630dc7

Browse files
committed
fix(edit-content): stringify block editor object on submit to prevent Map.toString() persistence
After blockEditorResolutionFn parses the JSON string to an object so the editor can render translated content, the FormControl holds an object. If the user saves without interacting with the editor (which is what emits the JSON.stringify via onChange), the object was sent to the backend and persisted as Map.toString(), corrupting the field on subsequent loads. Stringify BLOCK_EDITOR object values in processFieldValue so the submit payload matches what the editor's own onChange emits. Closes #34025
1 parent f534adb commit 0630dc7

3 files changed

Lines changed: 54 additions & 2 deletions

File tree

core-web/libs/edit-content/src/lib/components/dot-edit-content-form/dot-edit-content-form.component.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,10 @@ export class DotEditContentFormComponent implements OnInit {
440440
* @returns {FormValues} The processed form value ready for submission
441441
*/
442442
private processFormValue(
443-
value: Record<string, string | string[] | Date | number | null | undefined>
443+
value: Record<
444+
string,
445+
string | string[] | Date | number | Record<string, unknown> | null | undefined
446+
>
444447
): FormValues {
445448
return Object.fromEntries(
446449
Object.entries(value).map(([key, fieldValue]) => {

core-web/libs/edit-content/src/lib/utils/functions.util.spec.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1751,6 +1751,41 @@ describe('Utils Functions', () => {
17511751
});
17521752
});
17531753

1754+
describe('Block Editor fields', () => {
1755+
it('should stringify object values so the backend does not store them as Map.toString()', () => {
1756+
const field = {
1757+
fieldType: FIELD_TYPES.BLOCK_EDITOR,
1758+
variable: 'blockEditor'
1759+
} as unknown as DotCMSContentTypeField;
1760+
const objectValue = {
1761+
type: 'doc',
1762+
content: [{ type: 'paragraph' }]
1763+
};
1764+
1765+
expect(processFieldValue(objectValue, field)).toBe(JSON.stringify(objectValue));
1766+
});
1767+
1768+
it('should pass through JSON string values unchanged', () => {
1769+
const field = {
1770+
fieldType: FIELD_TYPES.BLOCK_EDITOR,
1771+
variable: 'blockEditor'
1772+
} as unknown as DotCMSContentTypeField;
1773+
const stringValue = '{"type":"doc","content":[{"type":"paragraph"}]}';
1774+
1775+
expect(processFieldValue(stringValue, field)).toBe(stringValue);
1776+
});
1777+
1778+
it('should pass through null and undefined unchanged', () => {
1779+
const field = {
1780+
fieldType: FIELD_TYPES.BLOCK_EDITOR,
1781+
variable: 'blockEditor'
1782+
} as unknown as DotCMSContentTypeField;
1783+
1784+
expect(processFieldValue(null, field)).toBeNull();
1785+
expect(processFieldValue(undefined, field)).toBeUndefined();
1786+
});
1787+
});
1788+
17541789
describe('edge cases', () => {
17551790
it('should handle fields without specific processing', () => {
17561791
const field = {

core-web/libs/edit-content/src/lib/utils/functions.util.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -597,14 +597,15 @@ export const processCalendarFieldValue = (
597597
* Applies appropriate transformations for different field types:
598598
* - Flattened fields: Joins arrays with commas
599599
* - Calendar fields: Converts to numeric timestamps
600+
* - Block Editor: Stringifies object values (see details below)
600601
* - Other fields: Returns as-is
601602
*
602603
* @param fieldValue - The raw field value
603604
* @param field - The field configuration
604605
* @returns The processed field value
605606
*/
606607
export const processFieldValue = (
607-
fieldValue: string | string[] | Date | number | null | undefined,
608+
fieldValue: string | string[] | Date | number | Record<string, unknown> | null | undefined,
608609
field: DotCMSContentTypeField
609610
): string | number | null | undefined => {
610611
// Handle flattened fields (multi-select, etc.)
@@ -622,6 +623,19 @@ export const processFieldValue = (
622623
return processCalendarFieldValue(fieldValue, field.variable);
623624
}
624625

626+
// Handle Block Editor: the FormControl may hold an object when the form is
627+
// initialized from a translated contentlet (blockEditorResolutionFn parses
628+
// the JSON string to an object so the editor can render it). The backend
629+
// expects a JSON string — sending an object causes it to be stored as
630+
// Map.toString(), corrupting the field on save.
631+
if (
632+
field.fieldType === FIELD_TYPES.BLOCK_EDITOR &&
633+
fieldValue &&
634+
typeof fieldValue === 'object'
635+
) {
636+
return JSON.stringify(fieldValue);
637+
}
638+
625639
// For all other fields, return as-is
626640
return fieldValue as string | number | null | undefined;
627641
};

0 commit comments

Comments
 (0)