Skip to content

Commit

Permalink
[lexical-table] Bug Fix: Fix table tab navigation (#6880)
Browse files Browse the repository at this point in the history
  • Loading branch information
etrepum authored Nov 28, 2024
1 parent 0d1bb66 commit 16e4987
Show file tree
Hide file tree
Showing 4 changed files with 375 additions and 34 deletions.
298 changes: 298 additions & 0 deletions packages/lexical-playground/__tests__/e2e/Tables.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2437,6 +2437,304 @@ test.describe.parallel('Tables', () => {
);
});

test('Merged cell tab navigation forward', async ({
page,
isPlainText,
isCollab,
}) => {
await initialize({isCollab, page});
test.skip(isPlainText);
test.skip(isCollab);

await focusEditor(page);

await insertTable(page, 3, 3);

await click(page, '.PlaygroundEditorTheme__tableCell');
await selectCellsFromTableCords(
page,
{x: 0, y: 0},
{x: 0, y: 1},
true,
true,
);
await mergeTableCells(page);
await selectCellsFromTableCords(
page,
{x: 1, y: 0},
{x: 2, y: 0},
true,
true,
);
await mergeTableCells(page);
await assertHTML(
page,
html`
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
<table class="PlaygroundEditorTheme__table">
<colgroup>
<col style="width: 92px" />
<col style="width: 92px" />
<col style="width: 92px" />
</colgroup>
<tr>
<th
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader"
rowspan="2">
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
</th>
<th
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader"
colspan="2">
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
</th>
</tr>
<tr>
<td class="PlaygroundEditorTheme__tableCell">
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
</td>
<td class="PlaygroundEditorTheme__tableCell">
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
</td>
</tr>
<tr>
<th
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader">
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
</th>
<td class="PlaygroundEditorTheme__tableCell">
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
</td>
<td class="PlaygroundEditorTheme__tableCell">
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
</td>
</tr>
</table>
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
`,
);
await click(page, '.PlaygroundEditorTheme__tableCell');
for (const i of Array.from({length: 9 - 2}, (_v, idx) => idx)) {
await page.keyboard.type(String(i));
await page.keyboard.press('Tab');
}
await page.keyboard.type('Done!');
await assertHTML(
page,
html`
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
<table class="PlaygroundEditorTheme__table">
<colgroup>
<col style="width: 92px" />
<col style="width: 92px" />
<col style="width: 92px" />
</colgroup>
<tr>
<th
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader"
rowspan="2">
<p class="PlaygroundEditorTheme__paragraph">
<span data-lexical-text="true">0</span>
</p>
</th>
<th
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader"
colspan="2">
<p class="PlaygroundEditorTheme__paragraph">
<span data-lexical-text="true">1</span>
</p>
</th>
</tr>
<tr>
<td class="PlaygroundEditorTheme__tableCell">
<p class="PlaygroundEditorTheme__paragraph">
<span data-lexical-text="true">2</span>
</p>
</td>
<td class="PlaygroundEditorTheme__tableCell">
<p class="PlaygroundEditorTheme__paragraph">
<span data-lexical-text="true">3</span>
</p>
</td>
</tr>
<tr>
<th
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader">
<p class="PlaygroundEditorTheme__paragraph">
<span data-lexical-text="true">4</span>
</p>
</th>
<td class="PlaygroundEditorTheme__tableCell">
<p class="PlaygroundEditorTheme__paragraph">
<span data-lexical-text="true">5</span>
</p>
</td>
<td class="PlaygroundEditorTheme__tableCell">
<p class="PlaygroundEditorTheme__paragraph">
<span data-lexical-text="true">6</span>
</p>
</td>
</tr>
</table>
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">Done!</span>
</p>
`,
);
});

test('Merged cell tab navigation reverse', async ({
page,
isPlainText,
isCollab,
}) => {
await initialize({isCollab, page});
test.skip(isPlainText);
test.skip(isCollab);

await focusEditor(page);

await insertTable(page, 3, 3);

await click(page, '.PlaygroundEditorTheme__tableCell');
await selectCellsFromTableCords(
page,
{x: 0, y: 0},
{x: 0, y: 1},
true,
true,
);
await mergeTableCells(page);
await selectCellsFromTableCords(
page,
{x: 1, y: 0},
{x: 2, y: 0},
true,
true,
);
await mergeTableCells(page);
await assertHTML(
page,
html`
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
<table class="PlaygroundEditorTheme__table">
<colgroup>
<col style="width: 92px" />
<col style="width: 92px" />
<col style="width: 92px" />
</colgroup>
<tr>
<th
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader"
rowspan="2">
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
</th>
<th
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader"
colspan="2">
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
</th>
</tr>
<tr>
<td class="PlaygroundEditorTheme__tableCell">
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
</td>
<td class="PlaygroundEditorTheme__tableCell">
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
</td>
</tr>
<tr>
<th
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader">
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
</th>
<td class="PlaygroundEditorTheme__tableCell">
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
</td>
<td class="PlaygroundEditorTheme__tableCell">
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
</td>
</tr>
</table>
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
`,
);
await click(page, ':nth-match(.PlaygroundEditorTheme__tableCell, 7)');
for (const i of Array.from({length: 9 - 2}, (_v, idx) => idx)) {
await page.keyboard.type(String(i));
await page.keyboard.down('Shift');
await page.keyboard.press('Tab');
await page.keyboard.up('Shift');
}
await page.keyboard.type('Done!');
await assertHTML(
page,
html`
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">Done!</span>
</p>
<table class="PlaygroundEditorTheme__table">
<colgroup>
<col style="width: 92px" />
<col style="width: 92px" />
<col style="width: 92px" />
</colgroup>
<tr>
<th
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader"
rowspan="2">
<p class="PlaygroundEditorTheme__paragraph">
<span data-lexical-text="true">6</span>
</p>
</th>
<th
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader"
colspan="2">
<p class="PlaygroundEditorTheme__paragraph">
<span data-lexical-text="true">5</span>
</p>
</th>
</tr>
<tr>
<td class="PlaygroundEditorTheme__tableCell">
<p class="PlaygroundEditorTheme__paragraph">
<span data-lexical-text="true">4</span>
</p>
</td>
<td class="PlaygroundEditorTheme__tableCell">
<p class="PlaygroundEditorTheme__paragraph">
<span data-lexical-text="true">3</span>
</p>
</td>
</tr>
<tr>
<th
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader">
<p class="PlaygroundEditorTheme__paragraph">
<span data-lexical-text="true">2</span>
</p>
</th>
<td class="PlaygroundEditorTheme__tableCell">
<p class="PlaygroundEditorTheme__paragraph">
<span data-lexical-text="true">1</span>
</p>
</td>
<td class="PlaygroundEditorTheme__tableCell">
<p class="PlaygroundEditorTheme__paragraph">
<span data-lexical-text="true">0</span>
</p>
</td>
</tr>
</table>
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
`,
);
});

test('Merge with content', async ({page, isPlainText, isCollab}) => {
await initialize({isCollab, page});
test.skip(isPlainText);
Expand Down
22 changes: 12 additions & 10 deletions packages/lexical-table/src/LexicalTableNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ import {PIXEL_VALUE_REG_EXP} from './constants';
import {$isTableCellNode, TableCellNode} from './LexicalTableCellNode';
import {TableDOMCell, TableDOMTable} from './LexicalTableObserver';
import {TableRowNode} from './LexicalTableRowNode';
import {getTable} from './LexicalTableSelectionHelpers';
import {
$getNearestTableCellInTableFromDOMNode,
getTable,
} from './LexicalTableSelectionHelpers';

export type SerializedTableNode = Spread<
{
Expand Down Expand Up @@ -270,17 +273,16 @@ export class TableNode extends ElementNode {
continue;
}

const x = row.findIndex((cell) => {
if (!cell) {
return;
for (let x = 0; x < row.length; x++) {
const cell = row[x];
if (cell == null) {
continue;
}
const {elem} = cell;
const cellNode = $getNearestNodeFromDOMNode(elem);
return cellNode === tableCellNode;
});

if (x !== -1) {
return {x, y};
const cellNode = $getNearestTableCellInTableFromDOMNode(this, elem);
if (cellNode !== null && tableCellNode.is(cellNode)) {
return {x, y};
}
}
}

Expand Down
19 changes: 12 additions & 7 deletions packages/lexical-table/src/LexicalTableObserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {
$createRangeSelection,
$createTextNode,
$getEditor,
$getNearestNodeFromDOMNode,
$getNodeByKey,
$getRoot,
$getSelection,
Expand All @@ -38,7 +37,7 @@ import {
type TableSelection,
} from './LexicalTableSelection';
import {
$findTableNode,
$getNearestTableCellInTableFromDOMNode,
$updateDOMForSelection,
getTable,
getTableElement,
Expand Down Expand Up @@ -351,13 +350,15 @@ export class TableObserver {
this.focusY = cellY;

if (this.isHighlightingCells) {
const focusTableCellNode = $getNearestNodeFromDOMNode(cell.elem);
const focusTableCellNode = $getNearestTableCellInTableFromDOMNode(
tableNode,
cell.elem,
);

if (
this.tableSelection != null &&
this.anchorCellNodeKey != null &&
$isTableCellNode(focusTableCellNode) &&
tableNode.is($findTableNode(focusTableCellNode))
focusTableCellNode !== null
) {
this.focusCellNodeKey = focusTableCellNode.getKey();
this.tableSelection = $createTableSelectionFrom(
Expand Down Expand Up @@ -407,9 +408,13 @@ export class TableObserver {
this.anchorX = cell.x;
this.anchorY = cell.y;

const anchorTableCellNode = $getNearestNodeFromDOMNode(cell.elem);
const {tableNode} = this.$lookup();
const anchorTableCellNode = $getNearestTableCellInTableFromDOMNode(
tableNode,
cell.elem,
);

if ($isTableCellNode(anchorTableCellNode)) {
if (anchorTableCellNode !== null) {
const anchorNodeKey = anchorTableCellNode.getKey();
this.tableSelection =
this.tableSelection != null
Expand Down
Loading

0 comments on commit 16e4987

Please sign in to comment.