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 <nate@buster.so>
This commit is contained in:
Devin AI 2025-09-10 16:21:57 +00:00
parent 19ae8ca40d
commit 8f24417c44
2 changed files with 99 additions and 8 deletions

View File

@ -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('<toggle ');
expect(markdown).toContain('content="');
expect(markdown).toContain('WOW!');
const elements = await markdownToPlatejs(editor, markdown);
expect(elements[0].type).toBe('toggle');
expect(elements[0].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('WOW!');
});
});

View File

@ -6,23 +6,46 @@ export const toggleSerializer: MdNodeParser<'toggle'> = {
throw new Error('Editor is required');
}
console.log(node);
const doc = (options as any).value ?? options.editor.children;
const nodeIndex = doc.indexOf(node as any);
const contentNodes: any[] = [];
for (let i = nodeIndex + 1; i < doc.length; i++) {
const sibling = doc[i] as any;
const siblingIndent = sibling?.indent ?? 0;
if (siblingIndent >= 1) {
contentNodes.push(sibling);
} else {
break;
}
}
const content = serializeMd(options.editor, {
...options,
value: node.children,
value: contentNodes.length ? contentNodes : [{ type: 'p', children: [{ text: '' }] }],
});
console.log('content', content);
const attrs: string[] = [`content="${content}"`];
if ((node as any).id) {
attrs.push(`id="${(node as any).id}"`);
}
if ((node as any).indent !== undefined && (node as any).indent !== null) {
attrs.push(`indent="${(node as any).indent}"`);
}
return {
type: 'html',
value: `<toggle content="${content}"></toggle>`,
value: `<toggle ${attrs.join(' ')}></toggle>`,
};
},
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;
}
},
};