diff --git a/demos/src/Marks/Link/React/index.html b/demos/src/Marks/Link/React/index.html new file mode 100644 index 00000000..28ecdafe --- /dev/null +++ b/demos/src/Marks/Link/React/index.html @@ -0,0 +1,15 @@ + + + + + + + +
+ + + diff --git a/demos/src/Marks/Link/React/index.jsx b/demos/src/Marks/Link/React/index.jsx new file mode 100644 index 00000000..04f989ad --- /dev/null +++ b/demos/src/Marks/Link/React/index.jsx @@ -0,0 +1,71 @@ +import React, { useCallback } from 'react' +import { useEditor, EditorContent } from '@tiptap/react' +import Document from '@tiptap/extension-document' +import Paragraph from '@tiptap/extension-paragraph' +import Text from '@tiptap/extension-text' +import Link from '@tiptap/extension-link' +import Code from '@tiptap/extension-code' +import './styles.scss' + +export default () => { + const editor = useEditor({ + extensions: [ + Document, + Paragraph, + Text, + Link.configure({ + openOnClick: false, + }), + Code, + ], + content: ` +

+ Wow, this editor has support for links to the whole world wide web. We tested a lot of URLs and I think you can add *every URL* you want. Isn’t that cool? Let’s try another one! Yep, seems to work. +

+

+ By default every link will get a rel="noopener noreferrer nofollow" attribute. It’s configurable though. +

+ `, + }) + + const setLink = useCallback(() => { + const previousUrl = editor.getAttributes('link').href + const url = window.prompt('URL', previousUrl) + + // cancelled + if (url === null) { + return + } + + // empty + if (url === '') { + editor.chain().focus().extendMarkRange('link').unsetLink() + .run() + + return + } + + // update link + editor.chain().focus().extendMarkRange('link').setLink({ href: url }) + .run() + }, [editor]) + + if (!editor) { + return null + } + + return ( + <> + + + + + ) +} diff --git a/demos/src/Marks/Link/React/index.spec.js b/demos/src/Marks/Link/React/index.spec.js new file mode 100644 index 00000000..db43732b --- /dev/null +++ b/demos/src/Marks/Link/React/index.spec.js @@ -0,0 +1,93 @@ +context('/src/Marks/Link/React/', () => { + before(() => { + cy.visit('/src/Marks/Link/React/') + }) + + beforeEach(() => { + cy.get('.ProseMirror').then(([{ editor }]) => { + editor.commands.setContent('

Example Text

') + cy.get('.ProseMirror').type('{selectall}') + }) + }) + + it('should parse a tags correctly', () => { + cy.get('.ProseMirror').then(([{ editor }]) => { + editor.commands.setContent('

Example Text

') + expect(editor.getHTML()).to.eq( + '

Example Text

', + ) + }) + }) + + it('should parse a tags with target attribute correctly', () => { + cy.get('.ProseMirror').then(([{ editor }]) => { + editor.commands.setContent('

Example Text

') + expect(editor.getHTML()).to.eq( + '

Example Text

', + ) + }) + }) + + it('should parse a tags with rel attribute correctly', () => { + cy.get('.ProseMirror').then(([{ editor }]) => { + editor.commands.setContent('

Example Text

') + expect(editor.getHTML()).to.eq( + '

Example Text

', + ) + }) + }) + + it('the button should add a link to the selected text', () => { + cy.window().then(win => { + cy.stub(win, 'prompt').returns('https://tiptap.dev') + + cy.get('button:first').click() + + cy.window().its('prompt').should('be.called') + + cy.get('.ProseMirror') + .find('a') + .should('contain', 'Example Text') + .should('have.attr', 'href', 'https://tiptap.dev') + }) + }) + + it('detects a pasted URL within a text', () => { + cy.get('.ProseMirror') + .paste({ + pastePayload: 'some text https://example.com around an url', + pasteType: 'text/plain', + }) + .find('a') + .should('contain', 'https://example.com') + .should('have.attr', 'href', 'https://example.com') + }) + + it('detects a pasted URL', () => { + cy.get('.ProseMirror') + .paste({ pastePayload: 'https://example.com', pasteType: 'text/plain' }) + .find('a') + .should('contain', 'Example Text') + .should('have.attr', 'href', 'https://example.com') + }) + + it('correctly detects multiple pasted URLs', () => { + cy.get('.ProseMirror').paste({ + pastePayload: + 'https://example1.com, https://example2.com/foobar, (http://example3.com/foobar)', + pasteType: 'text/plain', + }) + + cy.get('.ProseMirror') + .find('a[href="https://example1.com"]') + .should('contain', 'https://example1.com') + + cy.get('.ProseMirror') + .find('a[href="https://example2.com/foobar"]') + .should('contain', 'https://example2.com/foobar') + + cy.get('.ProseMirror') + .find('a[href="http://example3.com/foobar"]') + .should('contain', 'http://example3.com/foobar') + }) +}) diff --git a/demos/src/Marks/Link/React/styles.scss b/demos/src/Marks/Link/React/styles.scss new file mode 100644 index 00000000..d2e5a346 --- /dev/null +++ b/demos/src/Marks/Link/React/styles.scss @@ -0,0 +1,19 @@ +/* Basic editor styles */ +.ProseMirror { + > * + * { + margin-top: 0.75em; + } + + a { + color: #68cef8; + } + + code { + background-color: rgba(#616161, 0.1); + border-radius: 0.25em; + box-decoration-break: clone; + color: #616161; + font-size: 0.9rem; + padding: 0.25em; + } +}