Skip to content

Commit

Permalink
Support pasting a url onto text to create a link (#7766)
Browse files Browse the repository at this point in the history
  • Loading branch information
emmatown authored Aug 3, 2022
1 parent 6012390 commit 99d8f06
Show file tree
Hide file tree
Showing 3 changed files with 164 additions and 2 deletions.
5 changes: 5 additions & 0 deletions .changeset/quiet-months-argue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@keystone-6/fields-document': patch
---

Adds support for pasting a url onto text to create a link
29 changes: 27 additions & 2 deletions packages/fields-document/src/DocumentEditor/pasting/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { Descendant, Editor, Transforms } from 'slate';
import { Descendant, Editor, Transforms, Range } from 'slate';
import { isValidURL } from '../isValidURL';
import { insertNodesButReplaceIfSelectionIsAtEmptyParagraphOrHeading } from '../utils';
import { deserializeHTML } from './html';
import { deserializeMarkdown } from './markdown';

const urlPattern = /https?:\/\//;

function insertFragmentButDifferent(editor: Editor, nodes: Descendant[]) {
if (Editor.isBlock(editor, nodes[0])) {
insertNodesButReplaceIfSelectionIsAtEmptyParagraphOrHeading(editor, nodes);
Expand Down Expand Up @@ -63,14 +66,36 @@ export function withPasting(editor: Editor): Editor {
}
}

const plain = data.getData('text/plain');

if (
// isValidURL is a bit more permissive than a user might expect
// so for pasting, we'll constrain it to starting with https:// or http://
urlPattern.test(plain) &&
isValidURL(plain) &&
editor.selection &&
!Range.isCollapsed(editor.selection) &&
// we only want to turn the selected text into a link if the selection is within the same block
Editor.above(editor, {
match: node => Editor.isBlock(editor, node) && !Editor.isBlock(editor, node.children[0]),
}) &&
// and there is only text(potentially with marks) in the selection
// no other links or inline relationships
Editor.nodes(editor, {
match: node => Editor.isInline(editor, node),
}).next().done
) {
Transforms.wrapNodes(editor, { type: 'link', href: plain, children: [] }, { split: true });
return;
}

const html = data.getData('text/html');
if (html) {
const fragment = deserializeHTML(html);
insertFragmentButDifferent(editor, fragment);
return;
}

const plain = data.getData('text/plain');
if (plain) {
const fragment = deserializeMarkdown(plain);
insertFragmentButDifferent(editor, fragment);
Expand Down
132 changes: 132 additions & 0 deletions packages/fields-document/src/DocumentEditor/pasting/links.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/** @jest-environment jsdom */
/** @jsxRuntime classic */
/** @jsx jsx */
import { Editor } from 'slate';
import { makeEditor, jsx } from '../tests/utils';
import { MyDataTransfer } from './data-transfer';

function pasteText(editor: Editor, text: string) {
const data = new MyDataTransfer();
data.setData('text/plain', text);
editor.insertData(data);
}

test('pasting a url on some text wraps the text with a link', () => {
const editor = makeEditor(
<editor>
<paragraph>
<text>
blah <anchor />
blah
<focus /> blah
</text>
</paragraph>
</editor>
);
pasteText(editor, 'https://keystonejs.com');
expect(editor).toMatchInlineSnapshot(`
<editor>
<paragraph>
<text>
blah
</text>
<link
@@isInline={true}
href="https://keystonejs.com"
>
<text>
<anchor />
blah
<focus />
</text>
</link>
<text>
blah
</text>
</paragraph>
</editor>
`);
});

test('pasting a url on a selection spanning multiple blocks replaces the selection with the url', () => {
const editor = makeEditor(
<editor>
<paragraph>
<text>
start should still exist <anchor />
blah blah
</text>
</paragraph>
<paragraph>
<text>
blah blah
<focus /> end should still exist
</text>
</paragraph>
</editor>
);
pasteText(editor, 'https://keystonejs.com');
expect(editor).toMatchInlineSnapshot(`
<editor>
<paragraph>
<text>
start should still exist
</text>
<link
@@isInline={true}
href="https://keystonejs.com"
>
<text>
https://keystonejs.com
<cursor />
</text>
</link>
<text>
end should still exist
</text>
</paragraph>
</editor>
`);
});

test('pasting a url on a selection with a link inside replaces the selection with the url', () => {
const editor = makeEditor(
<editor>
<paragraph>
<text>
start should still exist <anchor />
should{' '}
</text>
<link href="https://keystonejs.com/docs">
<text>be</text>
</link>
<text>
replaced
<focus /> end should still exist
</text>
</paragraph>
</editor>
);
pasteText(editor, 'https://keystonejs.com');
expect(editor).toMatchInlineSnapshot(`
<editor>
<paragraph>
<text>
start should still exist
</text>
<link
@@isInline={true}
href="https://keystonejs.com"
>
<text>
https://keystonejs.com
<cursor />
</text>
</link>
<text>
end should still exist
</text>
</paragraph>
</editor>
`);
});

0 comments on commit 99d8f06

Please sign in to comment.