diff --git a/cypress/e2e/marks/Link.spec.js b/cypress/e2e/marks/Link.spec.js index f2ccb7dd2d7..59bb3402013 100644 --- a/cypress/e2e/marks/Link.spec.js +++ b/cypress/e2e/marks/Link.spec.js @@ -1,24 +1,6 @@ -/* eslint-disable no-unused-expressions */ /** - * @copyright Copyright (c) 2024 Max - * - * @author Max - * - * @license AGPL-3.0-or-later - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - * + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ import Markdown from './../../../src/extensions/Markdown.js' @@ -27,68 +9,37 @@ import { createCustomEditor } from './../../support/components.js' import { loadMarkdown, expectMarkdown } from '../nodes/helpers.js' describe('Link marks', { retries: 0 }, () => { - const editor = createCustomEditor({ content: '', - extensions: [ - Markdown, - Link, - Italic, - ], + extensions: [Markdown, Link, Italic], }) describe('insertOrSetLink command', { retries: 0 }, () => { - it('is available in commands', () => { expect(editor.commands).to.have.property('insertOrSetLink') }) it('can run on normal paragraph', () => { prepareEditor('hello\n', 3) - expect(editor.can().insertOrSetLink()).to.be.ok + expect(editor.can().insertOrSetLink()).toBe(true) }) it('will insert a link in a normal paragraph', () => { prepareEditor('hello\n', 3) - editor.commands.insertOrSetLink('https://nextcloud.com', { href: 'https://nextcloud.com' }) + editor.commands.insertOrSetLink('https://nextcloud.com', { + href: 'https://nextcloud.com', + }) expectMarkdown(editor, 'he\n\n\n\nllo') }) - }) - /** - * Expect a link in the editor. - */ - function expectLink() { - expect(getParentNode().type.name).to.equal('paragraph') - expect(getParentNode().attrs.href).to.equal('https://nextcloud.com') - expect(getMark().attrs.href).to.equal('https://nextcloud.com') - } - - /** - * - */ - function getParentNode() { - const { state: { selection } } = editor - return selection.$head.parent - } - /** * - */ - function getMark() { - const { state: { selection } } = editor - console.info(selection.$head) - return selection.$head.nodeAfter.marks[0] - } - - /** - * - * @param input + * @param {*} input markdown content + * @param {*} position cursor pos */ function prepareEditor(input, position = 1) { loadMarkdown(editor, input) - editor.commands.setTextSelection( position ) + editor.commands.setTextSelection(position) } - }) diff --git a/src/marks/Link.js b/src/marks/Link.js index 97e57b631c4..e70bc1e65d2 100644 --- a/src/marks/Link.js +++ b/src/marks/Link.js @@ -3,11 +3,10 @@ * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { markInputRule } from '@tiptap/core' import TipTapLink from '@tiptap/extension-link' import { domHref, parseHref } from './../helpers/links.js' import { linkClicking } from '../plugins/links.js' -import { isMarkActive } from '@tiptap/core' +import { markInputRule, getMarkRange, isMarkActive } from '@tiptap/core' const PROTOCOLS_TO_LINK_TO = ['http:', 'https:', 'mailto:', 'tel:'] @@ -90,28 +89,28 @@ const Link = TipTapLink.extend({ }, addCommands() { return { - /** - * Update the target of existing links. - * Insert a link if there currently is none. - * - */ insertOrSetLink: (text, attrs) => ({ state, chain, commands }) => { // Check if any text is selected, // if not insert the link using the given text property if (state.selection.empty) { if (isMarkActive(state, this.name)) { - commands.deleteNode('paragraph') + + // get current href to check what to replace, assumes there's only one link mark on the anchor + let href = '' + state.selection.$anchor.marks().forEach(item => { + if (item.attrs.href && item.type.name === "link") { + href = item.attrs.href + } + }) + commands.deleteRange(getMarkRange(state.selection.$anchor, state.schema.marks.link, { "href": href })) } return chain().insertContent({ - type: 'paragraph', - content: [{ - type: 'text', - marks: [{ - type: 'link', - attrs, - }], - text, + type: 'text', + marks: [{ + type: 'link', + attrs, }], + text, }) } else { return commands.setLink(attrs) diff --git a/src/tests/marks/Link.spec.js b/src/tests/marks/Link.spec.js new file mode 100644 index 00000000000..c721c3eb563 --- /dev/null +++ b/src/tests/marks/Link.spec.js @@ -0,0 +1,106 @@ +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +import Link from './../../marks/Link.js' +import Underline from '../../marks/Underline.js' +import createCustomEditor from '../testHelpers/createCustomEditor.ts' + +describe('Link extension integrated in the editor', () => { + it('should have link available in commands', () => { + const editor = createCustomEditor('

Test HELLO WORLD

', [Link]) + expect(editor.commands).toHaveProperty('insertOrSetLink') + }) + + it('should update link if anchor has mark', () => { + const editor = createCustomEditor( + '

Test HELLO WORLD

', + [Link, Underline], + ) + editor.commands.setTextSelection(3) + editor.commands.insertOrSetLink('updated.de', { href: 'updated.de' }) + expect(editor.getJSON()).toEqual({ + content: [ + { + content: [ + { + marks: [ + { attrs: { href: 'updated.de', title: null }, type: 'link' }, + ], + text: 'updated.de', + type: 'text', + }, + { text: ' HELLO WORLD', type: 'text' }, + ], + type: 'paragraph', + }, + ], + type: 'doc', + }) + }) + + it('Should only update link the anchor is on', () => { + const editor = createCustomEditor( + '

Testsecond link

', + [Link], + ) + editor.commands.setTextSelection(3) + editor.commands.insertOrSetLink('updated.de', { href: 'updated.de' }) + expect(editor.getJSON()).toEqual({ + content: [ + { + content: [ + { + marks: [ + { attrs: { href: 'updated.de', title: null }, type: 'link' }, + ], + text: 'updated.de', + type: 'text', + }, + { + "marks": [ + { + attrs: { + href: "not-nextcloud.com", + "title": null, + }, + type: "link", + } + ], + text: "second link", + type: "text", + } + ], + type: 'paragraph', + }, + ], + type: 'doc', + }) + }) + + it('should insert new link if none at anchor', () => { + const editor = createCustomEditor( + '

Test HELLO WORLD

', + [Link], + ) + editor.commands.setTextSelection(10) + expect(editor.getJSON()).toEqual({ + content: [ + { + content: [ + { + marks: [ + { attrs: { href: 'nextcloud.com', title: null }, type: 'link' }, + ], + text: 'Test', + type: 'text', + }, + { text: ' HELLO WORLD', type: 'text' }, + ], + type: 'paragraph', + }, + ], + type: 'doc', + }) + }) +})