diff --git a/packages/lexical-playground/__tests__/e2e/ClearFormatting.spec.mjs b/packages/lexical-playground/__tests__/e2e/ClearFormatting.spec.mjs index 391aa097440..e1f04d08967 100644 --- a/packages/lexical-playground/__tests__/e2e/ClearFormatting.spec.mjs +++ b/packages/lexical-playground/__tests__/e2e/ClearFormatting.spec.mjs @@ -9,6 +9,7 @@ import { centerAlign, indent, + moveToPrevWord, outdent, rightAlign, selectAll, @@ -274,4 +275,43 @@ test.describe('Clear All Formatting', () => { `, ); }); + + test(`Should clear formatting of selected text which spans over 1 paragraph`, async ({ + page, + }) => { + await focusEditor(page); + + await page.keyboard.type('Foo bar'); + await page.keyboard.press('Enter'); + await page.keyboard.type('baz qux'); + await selectAll(page); + await toggleBold(page); + await page.keyboard.press('ArrowRight'); + await moveToPrevWord(page); + await page.keyboard.down('Shift'); + await page.keyboard.press('ArrowUp'); + await page.keyboard.up('Shift'); + await selectFromAdditionalStylesDropdown(page, '.clear'); + await assertHTML( + page, + html` +
+ + Foo + + bar +
++ baz + + qux + +
+ `, + ); + }); }); diff --git a/packages/lexical-playground/src/plugins/ToolbarPlugin/utils.ts b/packages/lexical-playground/src/plugins/ToolbarPlugin/utils.ts index 05878e260b5..c5dfaed5224 100644 --- a/packages/lexical-playground/src/plugins/ToolbarPlugin/utils.ts +++ b/packages/lexical-playground/src/plugins/ToolbarPlugin/utils.ts @@ -341,53 +341,28 @@ export const clearFormatting = ( if ($isRangeSelection(selection) || $isTableSelection(selection)) { const anchor = selection.anchor; const focus = selection.focus; - const nodes = selection.getNodes(); const extractedNodes = selection.extract(); if (anchor.key === focus.key && anchor.offset === focus.offset) { return; } - nodes.forEach((node, idx) => { - // We split the first and last node by the selection - // So that we don't format unselected text inside those nodes + extractedNodes.forEach((node) => { if ($isTextNode(node)) { - // Use a separate variable to ensure TS does not lose the refinement - let textNode = node; - if (idx === 0 && anchor.offset !== 0) { - textNode = textNode.splitText(anchor.offset)[1] || textNode; + if (node.getStyle() !== '') { + node.setStyle(''); } - if (idx === nodes.length - 1) { - textNode = textNode.splitText(focus.offset)[0] || textNode; - } - /** - * If the selected text has one format applied - * selecting a portion of the text, could - * clear the format to the wrong portion of the text. - * - * The cleared text is based on the length of the selected text. - */ - // We need this in case the selected text only has one format - const extractedTextNode = extractedNodes[0]; - if (nodes.length === 1 && $isTextNode(extractedTextNode)) { - textNode = extractedTextNode; - } - - if (textNode.__style !== '') { - textNode.setStyle(''); - } - if (textNode.__format !== 0) { - textNode.setFormat(0); + if (node.getFormat() !== 0) { + node.setFormat(0); } const nearestBlockElement = - $getNearestBlockElementAncestorOrThrow(textNode); - if (nearestBlockElement.__format !== 0) { + $getNearestBlockElementAncestorOrThrow(node); + if (nearestBlockElement.getFormat() !== 0) { nearestBlockElement.setFormat(''); } - if (nearestBlockElement.__indent !== 0) { + if (nearestBlockElement.getIndent() !== 0) { nearestBlockElement.setIndent(0); } - node = textNode; } else if ($isHeadingNode(node) || $isQuoteNode(node)) { node.replace($createParagraphNode(), true); } else if ($isDecoratorBlockNode(node)) {