Skip to content

Commit

Permalink
[Breaking Change][lexical][lexical-link] Bug Fix: Collapse through in…
Browse files Browse the repository at this point in the history
…line elements in deleteCharacter (#7180)
  • Loading branch information
etrepum authored Feb 15, 2025
1 parent ad2511c commit f505ffe
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 9 deletions.
93 changes: 93 additions & 0 deletions packages/lexical-playground/__tests__/e2e/List.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import {
moveRight,
moveToEditorBeginning,
moveToEditorEnd,
moveToLineBeginning,
moveToParagraphEnd,
pressBackspace,
redo,
selectAll,
selectCharacters,
Expand Down Expand Up @@ -1946,4 +1948,95 @@ test.describe.parallel('Nested List', () => {
`,
);
});
test('collapseAtStart for trivial bullet list', async ({page}) => {
await focusEditor(page);
await toggleBulletList(page);
await assertHTML(
page,
html`
<ul class="PlaygroundEditorTheme__ul">
<li class="PlaygroundEditorTheme__listItem" value="1">
<br />
</li>
</ul>
`,
);
await pressBackspace(page);
await assertHTML(
page,
html`
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
`,
);
});
test('collapseAtStart for bullet list with text', async ({page}) => {
await focusEditor(page);
await toggleBulletList(page);
await page.keyboard.type('Hello World');
await moveToLineBeginning(page);
await assertHTML(
page,
html`
<ul class="PlaygroundEditorTheme__ul">
<li
class="PlaygroundEditorTheme__listItem PlaygroundEditorTheme__ltr"
dir="ltr"
value="1">
<span data-lexical-text="true">Hello World</span>
</li>
</ul>
`,
);
await pressBackspace(page);
await assertHTML(
page,
html`
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">Hello World</span>
</p>
`,
);
});
test('collapseAtStart for bullet list with text inside autolink', async ({
page,
}) => {
await focusEditor(page);
await toggleBulletList(page);
await page.keyboard.type('www.example.com');
await moveToLineBeginning(page);
await assertHTML(
page,
html`
<ul class="PlaygroundEditorTheme__ul">
<li
class="PlaygroundEditorTheme__listItem PlaygroundEditorTheme__ltr"
dir="ltr"
value="1">
<a
class="PlaygroundEditorTheme__link PlaygroundEditorTheme__ltr"
dir="ltr"
href="https://www.example.com">
<span data-lexical-text="true">www.example.com</span>
</a>
</li>
</ul>
`,
);
await pressBackspace(page);
await assertHTML(
page,
html`
<p class="PlaygroundEditorTheme__paragraph">
<a
class="PlaygroundEditorTheme__link PlaygroundEditorTheme__ltr"
dir="ltr"
href="https://www.example.com">
<span data-lexical-text="true">www.example.com</span>
</a>
</p>
`,
);
});
});
34 changes: 27 additions & 7 deletions packages/lexical/src/LexicalSelection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1842,11 +1842,7 @@ export class RangeSelection implements BaseSelection {
$updateCaretSelectionForUnicodeCharacter(this, isBackward);
} else if (isBackward && anchor.offset === 0) {
// Special handling around rich text nodes
const element =
anchor.type === 'element'
? anchor.getNode()
: anchor.getNode().getParentOrThrow();
if (element.collapseAtStart(this)) {
if ($collapseAtStart(this, anchor.getNode())) {
return;
}
}
Expand All @@ -1863,9 +1859,9 @@ export class RangeSelection implements BaseSelection {
if (
anchorNode.isEmpty() &&
$isRootNode(anchorNode.getParent()) &&
anchorNode.getIndexWithinParent() === 0
anchorNode.getPreviousSibling() === null
) {
anchorNode.collapseAtStart(this);
$collapseAtStart(this, anchorNode);
}
}
}
Expand Down Expand Up @@ -1972,6 +1968,30 @@ export function $getCharacterOffsets(
return [getCharacterOffset(anchor), getCharacterOffset(focus)];
}

function $collapseAtStart(
selection: RangeSelection,
startNode: LexicalNode,
): boolean {
for (
let node: null | LexicalNode = startNode;
node;
node = node.getParent()
) {
if ($isElementNode(node)) {
if (node.collapseAtStart(selection)) {
return true;
}
if (!node.isInline()) {
break;
}
}
if (node.getPreviousSibling()) {
break;
}
}
return false;
}

function $swapPoints(selection: RangeSelection): void {
const focus = selection.focus;
const anchor = selection.anchor;
Expand Down
10 changes: 8 additions & 2 deletions packages/lexical/src/nodes/LexicalElementNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -880,9 +880,15 @@ export class ElementNode extends LexicalNode {
return true;
}
/*
* This method controls the behavior of a the node during backwards
* This method controls the behavior of the node during backwards
* deletion (i.e., backspace) when selection is at the beginning of
* the node (offset 0)
* the node (offset 0). You may use this to have the node replace
* itself, change its state, or do nothing. When you do make such
* a change, you should return true.
*
* When true is returned, the collapse phase will stop.
* When false is returned, and isInline() is true, and getPreviousSibling() is null,
* then this function will be called on its parent.
*/
collapseAtStart(selection: RangeSelection): boolean {
return false;
Expand Down

0 comments on commit f505ffe

Please sign in to comment.