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

$insertNodes: Selection-agnostic node insertion with Grid/Node selection support #2638

Merged
merged 17 commits into from
Sep 14, 2022
6 changes: 1 addition & 5 deletions examples/decorators.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,7 @@ function VideoPlugin(): ReactNode {
(payload) => {
// Adding custom command that will be handled by this plugin
editor.update(() => {
const selection = $getSelection();
if (selection !== null) {
const url: string = payload;
selection.insertNodes([$createVideoNode(url)]);
}
$insertNodes([$createVideoNode(url)]);
});

// Returning true indicates that command is handled and no further propagation is required
Expand Down
3 changes: 1 addition & 2 deletions packages/lexical-html/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,5 @@ const nodes = $generateNodesFromDOM(editor, dom);
const editor = createEditor({ ...config, nodes });

// Or insert them at a selection.
const selection = $getSelection();
selection.insertNodes(nodes);
$insertNodes(nodes);
```
124 changes: 124 additions & 0 deletions packages/lexical-playground/__tests__/e2e/Images.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
insertUrlImage,
IS_WINDOWS,
SAMPLE_IMAGE_URL,
SAMPLE_LANDSCAPE_IMAGE_URL,
selectorBoundingBox,
test,
waitForSelector,
Expand Down Expand Up @@ -606,4 +607,127 @@ test.describe('Images', () => {
expect(lexicalSelection.anchor).toBeTruthy();
expect(lexicalSelection.focus).toBeTruthy();
});

test('Node selection: can select multiple image nodes and replace them with a new image', async ({
page,
isPlainText,
}) => {
test.skip(isPlainText);

await focusEditor(page);

await page.keyboard.type('text1');
await page.keyboard.press('Enter');
await insertSampleImage(page);
await page.keyboard.press('Enter');
await page.keyboard.type('text2');
await page.keyboard.press('Enter');
await insertSampleImage(page, 'alt');
await page.keyboard.press('Enter');
await page.keyboard.type('text3');

await assertHTML(
page,
html`
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">text1</span>
</p>
<p class="PlaygroundEditorTheme__paragraph">
<span
class="editor-image"
contenteditable="false"
data-lexical-decorator="true">
<div draggable="false">
<img
alt="Yellow flower in tilt shift lens"
draggable="false"
src="${SAMPLE_IMAGE_URL}"
style="height: inherit; max-width: 500px; width: inherit" />
</div>
</span>
<br />
</p>
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">text2</span>
</p>
<p class="PlaygroundEditorTheme__paragraph">
<span
class="editor-image"
contenteditable="false"
data-lexical-decorator="true">
<div draggable="false">
<img
alt="Daylight fir trees forest glacier green high ice landscape"
draggable="false"
src="${SAMPLE_LANDSCAPE_IMAGE_URL}"
style="height: inherit; max-width: 500px; width: inherit" />
</div>
</span>
<br />
</p>
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">text3</span>
</p>
`,
);

await click(
page,
'.editor-image img[alt="Yellow flower in tilt shift lens"]',
);
await page.keyboard.down('Shift');
await click(
page,
'.editor-image img[alt="Daylight fir trees forest glacier green high ice landscape"]',
);
await page.keyboard.up('Shift');

await insertSampleImage(page);
await page.keyboard.type(' <- it works!');

await assertHTML(
page,
html`
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">text1</span>
</p>
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">text2</span>
</p>
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span
class="editor-image"
contenteditable="false"
data-lexical-decorator="true">
<div draggable="false">
<img
alt="Yellow flower in tilt shift lens"
draggable="false"
src="${SAMPLE_IMAGE_URL}"
style="height: inherit; max-width: 500px; width: inherit" />
</div>
</span>
<span data-lexical-text="true">&lt;- it works!</span>
</p>
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">text3</span>
</p>
`,
);
});
});
148 changes: 148 additions & 0 deletions packages/lexical-playground/__tests__/e2e/Tables.spec.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ import {
html,
initialize,
insertHorizontalRule,
insertSampleImage,
insertTable,
IS_COLLAB,
pasteFromClipboard,
SAMPLE_IMAGE_URL,
selectCellsFromTableCords,
selectFromAdditionalStylesDropdown,
test,
Expand Down Expand Up @@ -2019,4 +2021,150 @@ test.describe('Tables', () => {
{ignoreClasses: true},
);
});

test('Grid selection: can select multiple cells and insert an image', async ({
page,
isPlainText,
}) => {
test.skip(isPlainText);

await focusEditor(page);

await insertTable(page);

await click(page, '.PlaygroundEditorTheme__tableCell');
await page.keyboard.type('Hello');

await page.keyboard.down('Shift');
await page.keyboard.press('ArrowRight');
await page.keyboard.press('ArrowDown');
await page.keyboard.up('Shift');

await insertSampleImage(page);
await page.keyboard.type(' <- it works!');

await assertHTML(
page,
html`
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
<table class="PlaygroundEditorTheme__table">
<tr>
<th
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader">
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span data-lexical-text="true">Hello</span>
</p>
</th>
<th
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader">
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
</th>
<th
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader">
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
</th>
<th
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader">
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
</th>
<th
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader">
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
</th>
</tr>
<tr>
<th
class="PlaygroundEditorTheme__tableCell PlaygroundEditorTheme__tableCellHeader">
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
</th>
<td class="PlaygroundEditorTheme__tableCell">
<p
class="PlaygroundEditorTheme__paragraph PlaygroundEditorTheme__ltr"
dir="ltr">
<span
class="editor-image"
contenteditable="false"
data-lexical-decorator="true">
<div draggable="false">
<img
alt="Yellow flower in tilt shift lens"
draggable="false"
src="${SAMPLE_IMAGE_URL}"
style="height: inherit; max-width: 500px; width: inherit" />
</div>
</span>
<span data-lexical-text="true">&lt;- it works!</span>
</p>
</td>
<td class="PlaygroundEditorTheme__tableCell">
<p class="PlaygroundEditorTheme__paragraph"><br /></p>
</td>
<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>
<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>
<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>
<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>
`,
);
});
});
12 changes: 11 additions & 1 deletion packages/lexical-playground/__tests__/utils/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ export const SAMPLE_IMAGE_URL =
E2E_PORT === 3000
? '/src/images/yellow-flower.jpg'
: '/assets/yellow-flower.a2a7c7a2.jpg';
export const SAMPLE_LANDSCAPE_IMAGE_URL =
E2E_PORT === 3000
? '/src/images/landscape.jpg'
: '/assets/landscape.21352c66.jpg';

export async function initialize({
page,
Expand Down Expand Up @@ -481,9 +485,15 @@ export async function clearEditor(page) {
await page.keyboard.press('Backspace');
}

export async function insertSampleImage(page) {
export async function insertSampleImage(page, modifier) {
await selectFromInsertDropdown(page, '.image');
if (modifier === 'alt') {
await page.keyboard.down('Alt');
}
await click(page, 'button[data-test-id="image-modal-option-sample"]');
if (modifier === 'alt') {
await page.keyboard.up('Alt');
}
}

export async function insertUrlImage(page, url, altText) {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ export default function ComponentPickerMenuPlugin(): JSX.Element {
if (selection.isCollapsed()) {
$wrapLeafNodesInElements(selection, () => $createCodeNode());
} else {
// Will this ever happen?
const textContent = selection.getTextContent();
const codeNode = $createCodeNode();
selection.insertNodes([codeNode]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,17 @@
*
*/

import type {LexicalCommand} from 'lexical';

import 'katex/dist/katex.css';

import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext';
import {$wrapNodeInElement} from '@lexical/utils';
import {
$getSelection,
$isRangeSelection,
$createParagraphNode,
$insertNodes,
$isRootOrShadowRoot,
COMMAND_PRIORITY_EDITOR,
createCommand,
LexicalCommand,
} from 'lexical';
import {useEffect} from 'react';

Expand Down Expand Up @@ -43,11 +44,11 @@ export default function EquationsPlugin(): JSX.Element | null {
INSERT_EQUATION_COMMAND,
(payload) => {
const {equation, inline} = payload;
const selection = $getSelection();
const equationNode = $createEquationNode(equation, inline);

if ($isRangeSelection(selection)) {
const equationNode = $createEquationNode(equation, inline);
selection.insertNodes([equationNode]);
$insertNodes([equationNode]);
if ($isRootOrShadowRoot(equationNode.getParentOrThrow())) {
$wrapNodeInElement(equationNode, $createParagraphNode).selectEnd();
}

return true;
Expand Down
Loading