diff --git a/packages/lexical-playground/__tests__/e2e/Headings/HeadingsBackspaceAtStart.spec.mjs b/packages/lexical-playground/__tests__/e2e/Headings/HeadingsBackspaceAtStart.spec.mjs index 703bf182034..f7960953855 100644 --- a/packages/lexical-playground/__tests__/e2e/Headings/HeadingsBackspaceAtStart.spec.mjs +++ b/packages/lexical-playground/__tests__/e2e/Headings/HeadingsBackspaceAtStart.spec.mjs @@ -56,3 +56,86 @@ test('Headings - stays as a heading when you backspace at the start of a heading `, ); }); + +test('Headings - converts to paragraph when backspace at start of empty heading', async ({ + page, + isPlainText, + isCollab, +}) => { + test.skip(isPlainText); + await initialize({isCollab, page}); + await focusEditor(page); + + await click(page, '.block-controls'); + await click(page, '.dropdown .icon.h1'); + + await assertHTML( + page, + html` + <h1 class="PlaygroundEditorTheme__h1"> + <br /> + </h1> + `, + ); + + await page.keyboard.press('Backspace'); + + await assertHTML( + page, + html` + <p class="PlaygroundEditorTheme__paragraph"> + <br /> + </p> + `, + ); +}); + +test('Headings - does not create unnecessary history entries when backspace at start of heading with content', async ({ + page, + isPlainText, + isCollab, +}) => { + // Note: This test works in regular collaborative mode but fails specifically in split view + test.skip(isPlainText || isCollab); + await initialize({isCollab, page}); + await focusEditor(page); + + // Create an empty heading + await click(page, '.block-controls'); + await click(page, '.dropdown .icon.h1'); + + // This is our meaningful change that should be undoable + await page.keyboard.type('Welcome to the playground'); + + await moveToEditorBeginning(page); + + // These backspace operations should not create history entries + await page.keyboard.press('Backspace'); + await page.keyboard.press('Backspace'); + await page.keyboard.press('Backspace'); + + // Verify content is unchanged after backspace attempts + await assertHTML( + page, + html` + <h1 + class="PlaygroundEditorTheme__h1 PlaygroundEditorTheme__ltr" + dir="ltr"> + <span data-lexical-text="true">Welcome to the playground</span> + </h1> + `, + ); + + // Press undo - should undo the typing since backspace didn't create history entries + await page.keyboard.press('Meta+z'); + + // Should revert to empty heading since that was the state before typing + await assertHTML( + page, + html` + <h1 class="PlaygroundEditorTheme__h1"> + <br /> + </h1> + `, + ); +}); diff --git a/packages/lexical-rich-text/src/index.ts b/packages/lexical-rich-text/src/index.ts index 3bc75ca6580..ffe1a38b1d8 100644 --- a/packages/lexical-rich-text/src/index.ts +++ b/packages/lexical-rich-text/src/index.ts @@ -379,9 +379,10 @@ export class HeadingNode extends ElementNode { } collapseAtStart(): true { - const newElement = !this.isEmpty() - ? $createHeadingNode(this.getTag()) - : $createParagraphNode(); + if (!this.isEmpty()) { + return true; + } + const newElement = $createParagraphNode(); const children = this.getChildren(); children.forEach((child) => newElement.append(child)); this.replace(newElement);