From bba84b912e98034f5117f5470a7ae6484568906e Mon Sep 17 00:00:00 2001 From: Katsiaryna <47710336+KatsiarynaDzibrova@users.noreply.github.com> Date: Mon, 22 Apr 2024 23:29:12 +0100 Subject: [PATCH 1/2] Fix selecting table selects an image after (#5917) --- packages/lexical-table/src/LexicalTableSelectionHelpers.ts | 4 +--- packages/lexical/src/LexicalSelection.ts | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/lexical-table/src/LexicalTableSelectionHelpers.ts b/packages/lexical-table/src/LexicalTableSelectionHelpers.ts index ed443fc3f17..cc4d9d681cf 100644 --- a/packages/lexical-table/src/LexicalTableSelectionHelpers.ts +++ b/packages/lexical-table/src/LexicalTableSelectionHelpers.ts @@ -707,9 +707,7 @@ export function applyTableHandlers( if (isFocusInside) { newSelection.focus.set( tableNode.getParentOrThrow().getKey(), - isBackward - ? tableNode.getIndexWithinParent() - : tableNode.getIndexWithinParent() + 1, + tableNode.getIndexWithinParent(), 'element', ); } else { diff --git a/packages/lexical/src/LexicalSelection.ts b/packages/lexical/src/LexicalSelection.ts index 520541f8e9b..959c9d4ac4a 100644 --- a/packages/lexical/src/LexicalSelection.ts +++ b/packages/lexical/src/LexicalSelection.ts @@ -1940,13 +1940,13 @@ function internalResolveSelectionPoint( : child.getFirstDescendant(); if (descendant === null) { resolvedElement = child; - resolvedOffset = 0; } else { child = descendant; resolvedElement = $isElementNode(child) ? child : child.getParentOrThrow(); } + resolvedOffset = 0; } if ($isTextNode(child)) { resolvedNode = child; From da06d8f8b0dce523bad6459ff2007ca747c88227 Mon Sep 17 00:00:00 2001 From: Serey Roth <88986106+serey-roth@users.noreply.github.com> Date: Mon, 22 Apr 2024 15:32:01 -0700 Subject: [PATCH 2/2] Preserve selection in tables with open typeahead menu (#5820) --- .../__tests__/e2e/Tables.spec.mjs | 205 ++++++++++++++---- .../src/LexicalTableSelectionHelpers.ts | 20 ++ 2 files changed, 183 insertions(+), 42 deletions(-) diff --git a/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs b/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs index 419b90fc364..55087f0a50d 100644 --- a/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs +++ b/packages/lexical-playground/__tests__/e2e/Tables.spec.mjs @@ -12,6 +12,7 @@ import { moveLeft, moveRight, moveToEditorBeginning, + moveUp, pressBackspace, selectAll, } from '../keyboardShortcuts/index.mjs'; @@ -44,6 +45,7 @@ import { test, toggleColumnHeader, unmergeTableCell, + waitForSelector, } from '../utils/index.mjs'; async function fillTablePartiallyWithText(page) { @@ -486,52 +488,171 @@ test.describe('Tables', () => { }); }); - test(`Can navigate table with keyboard`, async ({ - page, - isPlainText, - isCollab, - }) => { - await initialize({isCollab, page}); - test.skip(isPlainText); + test.describe(`Can navigate table with keyboard`, () => { + test(`Can navigate cells horizontally`, async ({ + page, + isPlainText, + isCollab, + }) => { + await initialize({isCollab, page}); + test.skip(isPlainText); - await focusEditor(page); - await insertTable(page, 2, 3); + await focusEditor(page); + await insertTable(page, 2, 2); - await fillTablePartiallyWithText(page); + await assertHTML( + page, + html` +


+ + + + + + + + + +
+


+
+


+
+


+
+


+
+


+ `, + undefined, + {ignoreClasses: true}, + ); - await assertHTML( + await assertSelection(page, { + anchorOffset: 0, + anchorPath: [1, 0, 0, 0], + focusOffset: 0, + focusPath: [1, 0, 0, 0], + }); + + await moveRight(page, 1); + await assertSelection(page, { + anchorOffset: 0, + anchorPath: [1, 0, 1, 0], + focusOffset: 0, + focusPath: [1, 0, 1, 0], + }); + + await moveRight(page, 1); + await assertSelection(page, { + anchorOffset: 0, + anchorPath: [1, 1, 0, 0], + focusOffset: 0, + focusPath: [1, 1, 0, 0], + }); + + await moveRight(page, 1); + await assertSelection(page, { + anchorOffset: 0, + anchorPath: [1, 1, 1, 0], + focusOffset: 0, + focusPath: [1, 1, 1, 0], + }); + + await moveLeft(page, 1); + await assertSelection(page, { + anchorOffset: 0, + anchorPath: [1, 1, 0, 0], + focusOffset: 0, + focusPath: [1, 1, 0, 0], + }); + + await moveLeft(page, 1); + await assertSelection(page, { + anchorOffset: 0, + anchorPath: [1, 0, 1, 0], + focusOffset: 0, + focusPath: [1, 0, 1, 0], + }); + + await moveLeft(page, 1); + await assertSelection(page, { + anchorOffset: 0, + anchorPath: [1, 0, 0, 0], + focusOffset: 0, + focusPath: [1, 0, 0, 0], + }); + }); + + test(`Can navigate cells vertically`, async ({ page, - html` -


- - - - - - - - - - - -
-

a

-
-

bb

-
-

cc

-
-

d

-
-

e

-
-

f

-
-


- `, - undefined, - {ignoreClasses: true}, - ); + isPlainText, + isCollab, + }) => { + await initialize({isCollab, page}); + test.skip(isPlainText); + + await focusEditor(page); + await insertTable(page, 2, 2); + + await assertSelection(page, { + anchorOffset: 0, + anchorPath: [1, 0, 0, 0], + focusOffset: 0, + focusPath: [1, 0, 0, 0], + }); + + await moveDown(page, 1); + await assertSelection(page, { + anchorOffset: 0, + anchorPath: [1, 1, 0, 0], + focusOffset: 0, + focusPath: [1, 1, 0, 0], + }); + + await moveUp(page, 1); + await assertSelection(page, { + anchorOffset: 0, + anchorPath: [1, 0, 0, 0], + focusOffset: 0, + focusPath: [1, 0, 0, 0], + }); + }); + + test('Should not navigate cells when typeahead menu is open and focused', async ({ + page, + isCollab, + isPlainText, + }) => { + await initialize({isCollab, page}); + test.skip(isPlainText); + + await focusEditor(page); + await insertTable(page, 2, 2); + + await page.keyboard.type('@A'); + await assertSelection(page, { + anchorOffset: 2, + anchorPath: [1, 0, 0, 0, 0, 0], + focusOffset: 2, + focusPath: [1, 0, 0, 0, 0, 0], + }); + + await waitForSelector(page, `#typeahead-menu ul li:first-child.selected`); + + await moveDown(page, 1); + await assertSelection(page, { + anchorOffset: 2, + anchorPath: [1, 0, 0, 0, 0, 0], + focusOffset: 2, + focusPath: [1, 0, 0, 0, 0, 0], + }); + + await waitForSelector( + page, + '#typeahead-menu ul li:nth-child(2).selected', + ); + }); }); test(`Can select cells using Table selection`, async ({ diff --git a/packages/lexical-table/src/LexicalTableSelectionHelpers.ts b/packages/lexical-table/src/LexicalTableSelectionHelpers.ts index cc4d9d681cf..e45c366faa3 100644 --- a/packages/lexical-table/src/LexicalTableSelectionHelpers.ts +++ b/packages/lexical-table/src/LexicalTableSelectionHelpers.ts @@ -1263,6 +1263,13 @@ function $handleArrowKey( tableNode: TableNode, tableObserver: TableObserver, ): boolean { + if ( + (direction === 'up' || direction === 'down') && + isTypeaheadMenuInView(editor) + ) { + return false; + } + const selection = $getSelection(); if (!$isSelectionInTable(selection, tableNode)) { @@ -1479,6 +1486,19 @@ function stopEvent(event: Event) { event.stopPropagation(); } +function isTypeaheadMenuInView(editor: LexicalEditor) { + // There is no inbuilt way to check if the component picker is in view + // but we can check if the root DOM element has the aria-controls attribute "typeahead-menu". + const root = editor.getRootElement(); + if (!root) { + return false; + } + return ( + root.hasAttribute('aria-controls') && + root.getAttribute('aria-controls') === 'typeahead-menu' + ); +} + function isExitingTableAnchor( type: string, offset: number,