fix: repair malformed nested editor blocks
This commit is contained in:
@@ -500,11 +500,12 @@ describe('SingleEditorView', () => {
|
||||
expect(editor.replaceBlocks.mock.calls[0][1]).toEqual([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
children: [
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
}),
|
||||
],
|
||||
children: [],
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
content: [{ type: 'text', text: 'Recovered child', styles: {} }],
|
||||
children: [],
|
||||
}),
|
||||
])
|
||||
} finally {
|
||||
|
||||
@@ -15,7 +15,7 @@ function block(type: string, text: string, children: unknown[] = []) {
|
||||
}
|
||||
|
||||
describe('repairMalformedEditorBlocks', () => {
|
||||
it('promotes numbered-list children out of paragraph blocks', () => {
|
||||
it('promotes all children out of paragraph blocks', () => {
|
||||
const nestedParagraph = block('paragraph', 'Nested paragraph')
|
||||
const nestedList = block('numberedListItem', 'Step one', [
|
||||
block('numberedListItem', 'Nested step'),
|
||||
@@ -26,8 +26,9 @@ describe('repairMalformedEditorBlocks', () => {
|
||||
expect(repairMalformedEditorBlocks([paragraph, tail])).toEqual([
|
||||
{
|
||||
...paragraph,
|
||||
children: [nestedParagraph],
|
||||
children: [],
|
||||
},
|
||||
nestedParagraph,
|
||||
nestedList,
|
||||
tail,
|
||||
])
|
||||
@@ -39,4 +40,11 @@ describe('repairMalformedEditorBlocks', () => {
|
||||
|
||||
expect(repairMalformedEditorBlocks([parent])).toEqual([parent])
|
||||
})
|
||||
|
||||
it('keeps nested toggle-list children under toggle-list items', () => {
|
||||
const nested = block('paragraph', 'Toggle detail')
|
||||
const parent = block('toggleListItem', 'Toggle summary', [nested])
|
||||
|
||||
expect(repairMalformedEditorBlocks([parent])).toEqual([parent])
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
let fallbackBlockIdSequence = 0
|
||||
const LIST_ITEM_TYPES = new Set(['bulletListItem', 'numberedListItem', 'checkListItem'])
|
||||
const NESTABLE_LIST_ITEM_TYPES = new Set([
|
||||
'bulletListItem',
|
||||
'numberedListItem',
|
||||
'checkListItem',
|
||||
'toggleListItem',
|
||||
])
|
||||
|
||||
type RepairResult = {
|
||||
blocks: unknown[]
|
||||
@@ -25,8 +30,8 @@ function isEditorBlockRecord(value: unknown): value is Record<string, unknown> {
|
||||
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
||||
}
|
||||
|
||||
function isListItemBlock(block: Record<string, unknown>): boolean {
|
||||
return typeof block.type === 'string' && LIST_ITEM_TYPES.has(block.type)
|
||||
function canNestChildBlocks(block: Record<string, unknown>): boolean {
|
||||
return typeof block.type === 'string' && NESTABLE_LIST_ITEM_TYPES.has(block.type)
|
||||
}
|
||||
|
||||
function hasUsableBlockId(block: Record<string, unknown>): boolean {
|
||||
@@ -46,20 +51,11 @@ function splitChildrenForBlock(
|
||||
block: Record<string, unknown>,
|
||||
children: unknown[],
|
||||
): { safeChildren: unknown[], promotedChildren: unknown[] } {
|
||||
if (isListItemBlock(block)) {
|
||||
if (canNestChildBlocks(block)) {
|
||||
return { safeChildren: children, promotedChildren: [] }
|
||||
}
|
||||
|
||||
const safeChildren: unknown[] = []
|
||||
const promotedChildren: unknown[] = []
|
||||
for (const child of children) {
|
||||
if (isEditorBlockRecord(child) && isListItemBlock(child)) {
|
||||
promotedChildren.push(child)
|
||||
} else {
|
||||
safeChildren.push(child)
|
||||
}
|
||||
}
|
||||
return { safeChildren, promotedChildren }
|
||||
return { safeChildren: [], promotedChildren: children }
|
||||
}
|
||||
|
||||
function repairBlockList(blocks: unknown[]): RepairResult {
|
||||
|
||||
@@ -558,13 +558,14 @@ describe('useEditorTabSwap raw mode sync', () => {
|
||||
expect.objectContaining({ id: expect.any(String), type: 'paragraph', content: [], children: [] }),
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
children: [
|
||||
expect.objectContaining({ id: expect.any(String), type: 'paragraph', content: [], children: [] }),
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
content: [{ type: 'text', text: 'Child', styles: {} }],
|
||||
}),
|
||||
],
|
||||
content: [{ type: 'text', text: 'Parent', styles: {} }],
|
||||
children: [],
|
||||
}),
|
||||
expect.objectContaining({ id: expect.any(String), type: 'paragraph', content: [], children: [] }),
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
content: [{ type: 'text', text: 'Child', styles: {} }],
|
||||
children: [],
|
||||
}),
|
||||
])
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user