Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[lexical-playground] Feat: Cannot insert table into table #6509

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 14 additions & 45 deletions packages/lexical-playground/__tests__/e2e/Tables.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
copyToClipboard,
deleteTableColumns,
deleteTableRows,
expect,
focusEditor,
html,
initialize,
Expand Down Expand Up @@ -246,62 +247,30 @@ test.describe.parallel('Tables', () => {
});
});

test(`Can exit the first cell of a nested table into the parent table cell`, async ({
test('Can not table node insert table cell', async ({
page,
isPlainText,
isCollab,
}) => {
await initialize({isCollab, page});
test.skip(isPlainText);

await focusEditor(page);
await insertTable(page, 2, 2);
await insertTable(page, 2, 2);

await assertSelection(page, {
anchorOffset: 0,
anchorPath: [1, 0, 0, 1, 0, 0, 0],
focusOffset: 0,
focusPath: [1, 0, 0, 1, 0, 0, 0],
});

await moveLeft(page, 1);
await assertSelection(page, {
anchorOffset: 0,
anchorPath: [1, 0, 0, 0],
focusOffset: 0,
focusPath: [1, 0, 0, 0],
});
});
});

test(`Can exit the last cell of a nested table into the parent table cell`, async ({
page,
isPlainText,
isCollab,
}) => {
await initialize({isCollab, page});
test.skip(isPlainText);

await focusEditor(page);
await insertTable(page, 2, 2);
await insertTable(page, 2, 2);

await moveRight(page, 3);
await assertSelection(page, {
anchorOffset: 0,
anchorPath: [1, 0, 0, 1, 1, 1, 0],
focusOffset: 0,
focusPath: [1, 0, 0, 1, 1, 1, 0],
});
test('After the cursor is positioned on the table, the toolbar does not display the inserted table', async ({
page,
isPlainText,
isCollab,
}) => {
await initialize({isCollab, page});
test.skip(isPlainText);
await focusEditor(page);
await insertTable(page, 2, 2);
const element = await page.$('.toolbar-item[aria-label="Insert table"]');

await moveRight(page, 1);
await assertSelection(page, {
anchorOffset: 0,
anchorPath: [1, 0, 0, 2],
focusOffset: 0,
focusPath: [1, 0, 0, 2],
});
});
expect(element).toEqual(null);
});

test(`Can insert a paragraph after a table, that is the last node, with the "Enter" key`, async ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -367,13 +367,18 @@ export default function ComponentPickerMenuPlugin(): JSX.Element {
options={options}
menuRenderFn={(
anchorElementRef,
{selectedIndex, selectOptionAndCleanUp, setHighlightedIndex},
{
selectedIndex,
selectOptionAndCleanUp,
setHighlightedIndex,
options: menuOptions,
},
) =>
anchorElementRef.current && options.length
anchorElementRef.current && menuOptions.length
? ReactDOM.createPortal(
<div className="typeahead-popover component-picker-menu">
<ul>
{options.map((option, i: number) => (
{menuOptions.map((option, i: number) => (
<ComponentPickerMenuItem
index={i}
isSelected={selectedIndex === i}
Expand Down
31 changes: 18 additions & 13 deletions packages/lexical-playground/src/plugins/ToolbarPlugin/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,7 @@ export default function ToolbarPlugin({
const [codeLanguage, setCodeLanguage] = useState<string>('');
const [isEditable, setIsEditable] = useState(() => editor.isEditable());
const [isImageCaption, setIsImageCaption] = useState(false);
const [isLocatedTable, setIsLocatedTable] = useState<boolean>(false);

const $updateToolbar = useCallback(() => {
const selection = $getSelection();
Expand Down Expand Up @@ -599,8 +600,10 @@ export default function ToolbarPlugin({

const tableNode = $findMatchingParent(node, $isTableNode);
if ($isTableNode(tableNode)) {
setIsLocatedTable(true);
setRootType('table');
} else {
setIsLocatedTable(false);
setRootType('root');
}

Expand Down Expand Up @@ -1137,19 +1140,21 @@ export default function ToolbarPlugin({
<i className="icon diagram-2" />
<span className="text">Excalidraw</span>
</DropDownItem>
<DropDownItem
onClick={() => {
showModal('Insert Table', (onClose) => (
<InsertTableDialog
activeEditor={activeEditor}
onClose={onClose}
/>
));
}}
className="item">
<i className="icon table" />
<span className="text">Table</span>
</DropDownItem>
{!isLocatedTable && (
<DropDownItem
onClick={() => {
showModal('Insert Table', (onClose) => (
<InsertTableDialog
activeEditor={activeEditor}
onClose={onClose}
/>
));
}}
className="item">
<i className="icon table" />
<span className="text">Table</span>
</DropDownItem>
)}
<DropDownItem
onClick={() => {
showModal('Insert Poll', (onClose) => (
Expand Down
18 changes: 18 additions & 0 deletions packages/lexical-react/src/LexicalTablePlugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ import {
} from '@lexical/utils';
import {
$createParagraphNode,
$findMatchingParent,
$getNodeByKey,
$getPreviousSelection,
$getSelection,
$isRangeSelection,
$isTextNode,
COMMAND_PRIORITY_EDITOR,
} from 'lexical';
Expand Down Expand Up @@ -71,6 +75,20 @@ export function TablePlugin({
Number(columns),
includeHeaders,
);

const selection = $getSelection() || $getPreviousSelection();

if ($isRangeSelection(selection)) {
const {anchor, focus} = selection;
const focusNode = focus.getNode();
const anchorNode = anchor.getNode();
if (
$findMatchingParent(focusNode, $isTableRowNode) ||
$findMatchingParent(anchorNode, $isTableRowNode)
) {
return false;
}
}
$insertNodeToNearestRoot(tableNode);

const firstDescendant = tableNode.getFirstDescendant();
Expand Down
25 changes: 23 additions & 2 deletions packages/lexical-react/src/LexicalTypeaheadMenuPlugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import type {
} from './shared/LexicalMenu';

import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
import {$isTableNode} from '@lexical/table';
import {
$findMatchingParent,
$getSelection,
$isRangeSelection,
$isTextNode,
Expand All @@ -26,7 +28,7 @@ import {
RangeSelection,
TextNode,
} from 'lexical';
import {useCallback, useEffect, useState} from 'react';
import {useCallback, useEffect, useMemo, useState} from 'react';
import * as React from 'react';

import {LexicalMenu, MenuOption, useMenuAnchorRef} from './shared/LexicalMenu';
Expand Down Expand Up @@ -220,6 +222,7 @@ export function LexicalTypeaheadMenuPlugin<TOption extends MenuOption>({
}: TypeaheadMenuPluginProps<TOption>): JSX.Element | null {
const [editor] = useLexicalComposerContext();
const [resolution, setResolution] = useState<MenuResolution | null>(null);
const [isLocatedTable, setIsLocatedTable] = useState<boolean>(false);
const anchorElementRef = useMenuAnchorRef(
resolution,
setResolution,
Expand Down Expand Up @@ -274,6 +277,17 @@ export function LexicalTypeaheadMenuPlugin<TOption extends MenuOption>({
range,
editorWindow,
);

const tableNode = $findMatchingParent(
selection.anchor.getNode(),
$isTableNode,
);
if (tableNode) {
setIsLocatedTable(true);
} else {
setIsLocatedTable(false);
}

if (isRangePositioned !== null) {
startTransition(() =>
openTypeahead({
Expand Down Expand Up @@ -302,13 +316,20 @@ export function LexicalTypeaheadMenuPlugin<TOption extends MenuOption>({
openTypeahead,
]);

const internalOptions = useMemo(() => {
if (isLocatedTable) {
return options.filter((option) => option.key !== 'Table');
}
return options;
}, [isLocatedTable, options]);

return resolution === null || editor === null ? null : (
<LexicalMenu
close={closeTypeahead}
resolution={resolution}
editor={editor}
anchorElementRef={anchorElementRef}
options={options}
options={internalOptions}
menuRenderFn={menuRenderFn}
shouldSplitNodeWithQuery={true}
onSelectOption={onSelectOption}
Expand Down
1 change: 1 addition & 0 deletions packages/lexical/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ export {
$applyNodeReplacement,
$cloneWithProperties,
$copyNode,
$findMatchingParent,
$getAdjacentNode,
$getEditor,
$getNearestNodeFromDOMNode,
Expand Down
Loading