Commit 87b4bb69 authored by Enrique Alcantara's avatar Enrique Alcantara Committed by Enrique Alcántara

Add input rules for creating links

Allow to create a link in the Content Editor
using Markdown syntax or by typing a URL

Changelog: changed
parent 295b2ad2
...@@ -2,22 +2,35 @@ import { markInputRule } from '@tiptap/core'; ...@@ -2,22 +2,35 @@ import { markInputRule } from '@tiptap/core';
import { Link } from '@tiptap/extension-link'; import { Link } from '@tiptap/extension-link';
import { defaultMarkdownSerializer } from 'prosemirror-markdown/src/to_markdown'; import { defaultMarkdownSerializer } from 'prosemirror-markdown/src/to_markdown';
const insertLinkInputRuleRegExp = /(?:^|\s)\[([\w|\s|-]+)\]\(.+?\)$/gm; export const markdownLinkSyntaxInputRuleRegExp = /(?:^|\s)\[([\w|\s|-]+)\]\((?<href>.+?)\)$/gm;
export const tiptapExtension = Link export const urlSyntaxRegExp = /(?:^|\s)(?<href>(?:https?:\/\/|www\.)[\S]+)(?:\s|\n)$/gim;
.extend({
addInputRules() {
return [
markInputRule(insertLinkInputRuleRegExp, this.type, (match) => {
const extractHrefRegExp = /\[([\w|\s|-]+)\]\((?<href>.+?)\)$/gm;
return extractHrefRegExp.exec(match[0]).groups; const extractHrefFromMatch = (match) => {
}), return { href: match.groups.href };
]; };
}
}) export const extractHrefFromMarkdownLink = (match) => {
.configure({ /**
openOnClick: false, * Removes the last capture group from the match to satisfy
}); * tiptap markInputRule expectation of having the content as
* the last capture group in the match.
*
* https://github.com/ueberdosis/tiptap/blob/main/packages/core/src/inputRules/markInputRule.ts#L11
*/
match.pop();
return extractHrefFromMatch(match);
};
export const tiptapExtension = Link.extend({
addInputRules() {
return [
markInputRule(markdownLinkSyntaxInputRuleRegExp, this.type, extractHrefFromMarkdownLink),
markInputRule(urlSyntaxRegExp, this.type, extractHrefFromMatch),
];
},
}).configure({
openOnClick: false,
});
export const serializer = defaultMarkdownSerializer.marks.link; export const serializer = defaultMarkdownSerializer.marks.link;
import { tiptapExtension as Link } from '~/content_editor/extensions/link'; import {
import { createTestEditor } from '../test_utils'; markdownLinkSyntaxInputRuleRegExp,
urlSyntaxRegExp,
extractHrefFromMarkdownLink,
} from '~/content_editor/extensions/link';
describe('content_editor/extensions/link', () => { describe('content_editor/extensions/link', () => {
let tiptapEditor; describe.each`
input | matches
${'[gitlab](https://gitlab.com)'} | ${true}
${'[documentation](readme.md)'} | ${true}
${'[link 123](readme.md)'} | ${true}
${'[link 123](read me.md)'} | ${true}
${'text'} | ${false}
${'documentation](readme.md'} | ${false}
${'https://www.google.com'} | ${false}
`('markdownLinkSyntaxInputRuleRegExp', ({ input, matches }) => {
it(`${matches ? 'matches' : 'does not match'} ${input}`, () => {
const match = new RegExp(markdownLinkSyntaxInputRuleRegExp).exec(input);
beforeEach(() => { expect(Boolean(match?.groups.href)).toBe(matches);
tiptapEditor = createTestEditor({ extensions: [Link] }); });
}); });
it.each` describe.each`
input input | matches
${'[gitlab](https://gitlab.com)'} ${'http://example.com '} | ${true}
`('creates a link when the input rule matches $input', ({ input }) => { ${'https://example.com '} | ${true}
const { view } = tiptapEditor; ${'www.example.com '} | ${true}
const { selection } = view.state; ${'example.com/ab.html '} | ${false}
${'text'} | ${false}
${' http://example.com '} | ${true}
${'https://www.google.com '} | ${true}
`('urlSyntaxRegExp', ({ input, matches }) => {
it(`${matches ? 'matches' : 'does not match'} ${input}`, () => {
const match = new RegExp(urlSyntaxRegExp).exec(input);
tiptapEditor.chain().insertContent(input).run(); expect(Boolean(match?.groups.href)).toBe(matches);
});
});
describe('extractHrefFromMarkdownLink', () => {
const input = '[gitlab](https://gitlab.com)';
const href = 'https://gitlab.com';
let match;
let result;
/** beforeEach(() => {
* Calls the event handler that executes the input rule match = new RegExp(markdownLinkSyntaxInputRuleRegExp).exec(input);
* https://github.com/ProseMirror/prosemirror-inputrules/blob/master/src/inputrules.js#L65 result = extractHrefFromMarkdownLink(match);
* */ });
view.someProp('handleTextInput', (f) => f(view, selection.from, selection.to, input));
const serializedDoc = tiptapEditor.getJSON(); it('extracts the url from a markdown link captured by markdownLinkSyntaxInputRuleRegExp', () => {
expect(result).toEqual({ href });
});
console.log(serializedDoc.content[0].content[0]) it('makes sure that url text is the last capture group', () => {
expect(match[match.length - 1]).toEqual('gitlab');
});
}); });
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment