diff --git a/packages/lexical-link/src/index.ts b/packages/lexical-link/src/index.ts index cc9591fc1ac..70720c6f2dc 100644 --- a/packages/lexical-link/src/index.ts +++ b/packages/lexical-link/src/index.ts @@ -10,9 +10,11 @@ import type { DOMConversionMap, DOMConversionOutput, EditorConfig, + GridSelection, LexicalCommand, LexicalNode, NodeKey, + NodeSelection, RangeSelection, SerializedElementNode, } from 'lexical'; @@ -21,6 +23,7 @@ import {addClassNamesToElement} from '@lexical/utils'; import { $getSelection, $isElementNode, + $isRangeSelection, $setSelection, createCommand, ElementNode, @@ -133,6 +136,25 @@ export class LinkNode extends ElementNode { isInline(): true { return true; } + + extractWithChild( + child: LexicalNode, + selection: RangeSelection | NodeSelection | GridSelection, + destination: 'clone' | 'html', + ): boolean { + if (!$isRangeSelection(selection)) { + return false; + } + + const anchorNode = selection.anchor.getNode(); + const focusNode = selection.focus.getNode(); + + return ( + this.isParentOf(anchorNode) && + this.isParentOf(focusNode) && + selection.getTextContent().length > 0 + ); + } } function convertAnchorElement(domNode: Node): DOMConversionOutput { diff --git a/packages/lexical-playground/__tests__/e2e/CopyAndPaste.spec.mjs b/packages/lexical-playground/__tests__/e2e/CopyAndPaste.spec.mjs index 7222cbb454e..f14547580f9 100644 --- a/packages/lexical-playground/__tests__/e2e/CopyAndPaste.spec.mjs +++ b/packages/lexical-playground/__tests__/e2e/CopyAndPaste.spec.mjs @@ -1809,4 +1809,82 @@ test.describe('CopyAndPaste', () => { `, ); }); + + test('HTML Copy + paste in front of or after a link', async ({ + page, + isPlainText, + }) => { + test.skip(isPlainText); + await focusEditor(page); + await pasteFromClipboard(page, { + 'text/html': `textlinktext`, + }); + await moveToEditorBeginning(page); + await pasteFromClipboard(page, { + 'text/html': 'before', + }); + await moveToEditorEnd(page); + await pasteFromClipboard(page, { + 'text/html': 'after', + }); + await assertHTML( + page, + html` +
+ beforetext + + link + + textafter +
+ `, + ); + }); + + test('HTML Copy + paste link by selecting its (partial) content', async ({ + page, + isPlainText, + }) => { + test.skip(isPlainText); + await focusEditor(page); + await pasteFromClipboard(page, { + 'text/html': `textlinktext`, + }); + await moveLeft(page, 5); + await page.keyboard.down('Shift'); + await moveLeft(page, 2); + await page.keyboard.up('Shift'); + const clipboard = await copyToClipboard(page); + await moveToEditorEnd(page); + await pasteFromClipboard(page, clipboard); + + await assertHTML( + page, + html` ++ text + + link + + text + + in + +
+ `, + ); + }); }); diff --git a/packages/lexical-playground/__tests__/e2e/Selection.spec.mjs b/packages/lexical-playground/__tests__/e2e/Selection.spec.mjs index c5543f25fa7..ad3ba70a7da 100644 --- a/packages/lexical-playground/__tests__/e2e/Selection.spec.mjs +++ b/packages/lexical-playground/__tests__/e2e/Selection.spec.mjs @@ -7,13 +7,20 @@ */ import { + moveToLineBeginning, + pressShiftEnter, +} from '../keyboardShortcuts/index.mjs'; +import { + assertHTML, click, evaluate, expect, focusEditor, + html, initialize, insertImageCaption, insertSampleImage, + selectFromFormatDropdown, sleep, test, } from '../utils/index.mjs'; @@ -94,4 +101,38 @@ test.describe('Selection', () => { expect(await hasSelection('.image-caption-container')).toBe(true); expect(await hasSelection('.editor-shell')).toBe(false); }); + + test('can wrap post-linebreak nodes into new element', async ({ + page, + isPlainText, + }) => { + test.skip(isPlainText); + await focusEditor(page); + await page.keyboard.type('Line1'); + await pressShiftEnter(page); + await page.keyboard.type('Line2'); + await page.keyboard.down('Shift'); + await moveToLineBeginning(page); + await page.keyboard.up('Shift'); + await selectFromFormatDropdown(page, '.code'); + await assertHTML( + page, + html` +
+ Line1
+
+
+ Line2
+
+