From 8f24417c44086b88a0565b05f6716d1bd76431a2 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Wed, 10 Sep 2025 16:21:57 +0000 Subject: [PATCH] fix: implement indent-based toggle serialization with id preservation - Update toggle serializer to collect subsequent indented siblings as content during serialization - Preserve id and indent attributes in HTML format during serialization/deserialization - Add roundtrip tests validating toggle with indent attributes - Ensure toggle content is properly reconstructed as separate elements with indent=1 - Both basic toggle and indent-based toggle tests now pass Fixes BUS-1703 Co-Authored-By: nate@buster.so --- .../markdown-kit/platejs-conversion.test.ts | 36 +++++++++- .../plugins/markdown-kit/toggle-serializer.ts | 71 +++++++++++++++++-- 2 files changed, 99 insertions(+), 8 deletions(-) diff --git a/apps/web/src/components/ui/report/plugins/markdown-kit/platejs-conversion.test.ts b/apps/web/src/components/ui/report/plugins/markdown-kit/platejs-conversion.test.ts index 06f5a73c6..5ee0d7c2e 100644 --- a/apps/web/src/components/ui/report/plugins/markdown-kit/platejs-conversion.test.ts +++ b/apps/web/src/components/ui/report/plugins/markdown-kit/platejs-conversion.test.ts @@ -286,7 +286,41 @@ Here's an unordered list: const elements = await markdownToPlatejs(editor, markdown); const firstElement = elements[0]; expect(firstElement.type).toBe('toggle'); - expect(firstElement.children[0]).toEqual({ type: 'p', children: [{ text: 'This is toggle content' }] }); + expect(firstElement.children[0]).toEqual({ text: '' }); + + expect(elements[1].type).toBe('p'); + expect((elements[1] as any).indent).toBe(1); + expect(elements[1].children[0].text).toBe('This is toggle content'); + }); + + it('toggle with indent attributes and roundtrip', async () => { + const originalElements = [ + { + type: 'toggle', + children: [{ text: 'This is a test:)' }], + id: 'hCbXepPz5W' + }, + { + type: 'p', + children: [{ text: 'WOW!' }], + id: 'Q-d_WjfXSV', + indent: 1 + } + ]; + + const markdown = await platejsToMarkdown(editor, originalElements); + expect(markdown).toContain('`, + value: ``, }; }, deserialize: (node, _, options) => { const typedAttributes = parseAttributes(node.attributes) as { content: string; + id?: string; + indent?: string | number; }; if (!options.editor) { @@ -31,16 +54,50 @@ export const toggleSerializer: MdNodeParser<'toggle'> = { try { const deserializedContent = deserializeMd(options.editor, typedAttributes.content); - return { + + const toggleNode: any = { type: 'toggle', - children: deserializedContent, + children: [{ text: '' }], }; + + if (typedAttributes.id) { + toggleNode.id = typedAttributes.id; + } + + if (typedAttributes.indent !== undefined && typedAttributes.indent !== null) { + toggleNode.indent = typeof typedAttributes.indent === 'string' + ? parseInt(typedAttributes.indent, 10) + : typedAttributes.indent; + } + + const contentNodes = Array.isArray(deserializedContent) ? deserializedContent : [deserializedContent]; + const processedContentNodes = contentNodes.map((contentNode: any) => { + const currentIndent = contentNode.indent ?? 0; + return { + ...contentNode, + indent: currentIndent >= 1 ? currentIndent : 1, + }; + }); + + return [toggleNode, ...processedContentNodes] as any; } catch (error) { console.error('Error deserializing content', error); - return { + const result: any = { type: 'toggle', children: [{ text: typedAttributes.content }], }; + + if (typedAttributes.id) { + result.id = typedAttributes.id; + } + + if (typedAttributes.indent !== undefined && typedAttributes.indent !== null) { + result.indent = typeof typedAttributes.indent === 'string' + ? parseInt(typedAttributes.indent, 10) + : typedAttributes.indent; + } + + return result; } }, };