Skip to content

Commit

Permalink
fix: layout deletion behavior
Browse files Browse the repository at this point in the history
- Added  function in LayoutPlugin to handle layout deletion
  for all delete operations (character, word, line)
- Added command registrations for DELETE_CHARACTER_COMMAND, DELETE_WORD_COMMAND,
  and DELETE_LINE_COMMAND
- Added new e2e tests in Layout.spec.mjs:
  - Layout deletion when it's the first node
  - Layout deletion with surrounding content
  - Testing different delete operations (character, word, line)

Fixes #6938
  • Loading branch information
kirandash committed Mar 9, 2025
1 parent 196c1ce commit 4f0e5a1
Show file tree
Hide file tree
Showing 3 changed files with 448 additions and 1 deletion.
352 changes: 352 additions & 0 deletions packages/lexical-playground/__tests__/e2e/Layout.spec.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,352 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import {selectAll} from '../keyboardShortcuts/index.mjs';
import {
assertHTML,
click,
focusEditor,
html,
initialize,
insertLayoutInFirstCell,
test,
} from '../utils/index.mjs';

test.describe('Layout', () => {
test.beforeEach(({isCollab, page}) => initialize({isCollab, page}));

test('Can delete layout with Backspace', async ({page, isPlainText}) => {
test.skip(isPlainText);

await focusEditor(page);
await insertLayoutInFirstCell(page, '1fr 1fr');
await page.keyboard.type('Left column');
await click(page, '.PlaygroundEditorTheme__layoutItem:nth-child(2)');
await page.keyboard.type('Right column');
await assertHTML(
page,
html`
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
<div
class="PlaygroundEditorTheme__layoutContainer"
style="grid-template-columns: 1fr 1fr;">
<div
class="PlaygroundEditorTheme__layoutItem"
data-lexical-layout-item="true">
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">Left column</span>
</p>
</div>
<div
class="PlaygroundEditorTheme__layoutItem"
data-lexical-layout-item="true">
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">Right column</span>
</p>
</div>
</div>
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
`,
);

await selectAll(page);
await page.keyboard.press('Backspace');
await assertHTML(
page,
html`
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
`,
);
});

test('Can delete layout with Delete', async ({page, isPlainText}) => {
test.skip(isPlainText);

await focusEditor(page);
await insertLayoutInFirstCell(page, '1fr 1fr');
await page.keyboard.type('Left column');
await click(page, '.PlaygroundEditorTheme__layoutItem:nth-child(2)');
await page.keyboard.type('Right column');
await assertHTML(
page,
html`
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
<div
class="PlaygroundEditorTheme__layoutContainer"
style="grid-template-columns: 1fr 1fr;">
<div
class="PlaygroundEditorTheme__layoutItem"
data-lexical-layout-item="true">
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">Left column</span>
</p>
</div>
<div
class="PlaygroundEditorTheme__layoutItem"
data-lexical-layout-item="true">
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">Right column</span>
</p>
</div>
</div>
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
`,
);

await selectAll(page);
await page.keyboard.press('Delete');
await assertHTML(
page,
html`
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
`,
);
});

test('Can delete layout with word delete', async ({page, isPlainText}) => {
test.skip(isPlainText);

await focusEditor(page);
await insertLayoutInFirstCell(page, '1fr 1fr');
await page.keyboard.type('Left column');
await click(page, '.PlaygroundEditorTheme__layoutItem:nth-child(2)');
await page.keyboard.type('Right column');
await assertHTML(
page,
html`
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
<div
class="PlaygroundEditorTheme__layoutContainer"
style="grid-template-columns: 1fr 1fr;">
<div
class="PlaygroundEditorTheme__layoutItem"
data-lexical-layout-item="true">
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">Left column</span>
</p>
</div>
<div
class="PlaygroundEditorTheme__layoutItem"
data-lexical-layout-item="true">
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">Right column</span>
</p>
</div>
</div>
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
`,
);

await selectAll(page);
// Ctrl+Backspace for word delete
await page.keyboard.down('Control');
await page.keyboard.press('Backspace');
await page.keyboard.up('Control');
await assertHTML(
page,
html`
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
`,
);
});

test('Can delete layout with line delete', async ({page, isPlainText}) => {
test.skip(isPlainText);

await focusEditor(page);
await insertLayoutInFirstCell(page, '1fr 1fr');
await page.keyboard.type('Left column');
await click(page, '.PlaygroundEditorTheme__layoutItem:nth-child(2)');
await page.keyboard.type('Right column');
await assertHTML(
page,
html`
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
<div
class="PlaygroundEditorTheme__layoutContainer"
style="grid-template-columns: 1fr 1fr;">
<div
class="PlaygroundEditorTheme__layoutItem"
data-lexical-layout-item="true">
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">Left column</span>
</p>
</div>
<div
class="PlaygroundEditorTheme__layoutItem"
data-lexical-layout-item="true">
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">Right column</span>
</p>
</div>
</div>
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
`,
);

await selectAll(page);
// Cmd+Backspace for line delete
await page.keyboard.down('Meta');
await page.keyboard.press('Backspace');
await page.keyboard.up('Meta');
await assertHTML(
page,
html`
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
`,
);
});

// Reference: https://github.com/facebook/lexical/issues/6938
test('Can delete layout when it is the first node', async ({
page,
isPlainText,
}) => {
test.skip(isPlainText);

await focusEditor(page);
await insertLayoutInFirstCell(page, '1fr 1fr');

// Remove the paragraph before the layout to make layout the first node
await page.keyboard.press('ArrowUp');
await page.keyboard.press('Backspace');

await page.keyboard.type('Left column');
await click(page, '.PlaygroundEditorTheme__layoutItem:nth-child(2)');
await page.keyboard.type('Right column');

await assertHTML(
page,
html`
<div
class="PlaygroundEditorTheme__layoutContainer"
style="grid-template-columns: 1fr 1fr;">
<div
class="PlaygroundEditorTheme__layoutItem"
data-lexical-layout-item="true">
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">Left column</span>
</p>
</div>
<div
class="PlaygroundEditorTheme__layoutItem"
data-lexical-layout-item="true">
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">Right column</span>
</p>
</div>
</div>
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
`,
);

await selectAll(page);
await page.keyboard.press('Delete');

await assertHTML(
page,
html`
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
`,
);
});

test('Can delete layout with surrounding content', async ({
page,
isPlainText,
}) => {
test.skip(isPlainText);

await focusEditor(page);
// Add content before layout
await page.keyboard.type('Content before');
await page.keyboard.press('Enter');

// Insert and fill layout
await insertLayoutInFirstCell(page, '1fr 1fr');
await page.keyboard.type('Left column');
await click(page, '.PlaygroundEditorTheme__layoutItem:nth-child(2)');
await page.keyboard.type('Right column');

// Add content after layout
await page.keyboard.press('ArrowDown');
await page.keyboard.type('Content after');

await assertHTML(
page,
html`
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">Content before</span>
</p>
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
<div
class="PlaygroundEditorTheme__layoutContainer"
style="grid-template-columns: 1fr 1fr;">
<div
class="PlaygroundEditorTheme__layoutItem"
data-lexical-layout-item="true">
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">Left column</span>
</p>
</div>
<div
class="PlaygroundEditorTheme__layoutItem"
data-lexical-layout-item="true">
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">Right column</span>
</p>
</div>
</div>
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">Content after</span>
</p>
`,
);

// Click second column before select all
await click(page, '.PlaygroundEditorTheme__layoutItem:nth-child(2)');
await selectAll(page);
await page.keyboard.press('Delete');

await assertHTML(
page,
html`
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
`,
);
});
});
15 changes: 15 additions & 0 deletions packages/lexical-playground/__tests__/utils/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1104,3 +1104,18 @@ export function createHumanReadableSelection(_overview, dto) {
focusPath: dto.focusPath.map((p) => p.value),
};
}

export async function insertLayoutInFirstCell(page, template) {
await selectFromInsertDropdown(page, '.item .columns');
await click(page, '.toolbar-item.dialog-dropdown');
const layoutLabels = {
'1fr 1fr': '2 columns (equal width)',
'1fr 1fr 1fr': '3 columns (equal width)',
'1fr 1fr 1fr 1fr': '4 columns (equal width)',
'1fr 2fr 1fr': '3 columns (25% - 50% - 25%)',
'1fr 3fr': '2 columns (25% - 75%)',
};
const label = layoutLabels[template];
await click(page, `.item span.text:has-text("${label}")`);
await click(page, '.Button__root:has-text("Insert")');
}
Loading

0 comments on commit 4f0e5a1

Please sign in to comment.