From f3875aeb8a4e97bf3110a107c80df0079453404e Mon Sep 17 00:00:00 2001 From: Eugeny Date: Fri, 6 Mar 2026 01:33:16 +0300 Subject: [PATCH 1/6] fix(link-tool): open new window with url when formatted link clicked via ctrl key --- src/components/dom.ts | 10 ++++++++++ src/components/modules/ui.ts | 7 ++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/src/components/dom.ts b/src/components/dom.ts index 0cbfbeda5..573c2d98d 100644 --- a/src/components/dom.ts +++ b/src/components/dom.ts @@ -566,6 +566,16 @@ export default class Dom { return element.tagName.toLowerCase() === 'a'; } + /** + * Returns the closest ancestor anchor (A tag) of the given element (including itself) + * + * @param element - element to check + * @returns {HTMLAnchorElement | null} + */ + public static getAnchor(element: Element): HTMLAnchorElement | null { + return element.closest("a"); + } + /** * Return element's offset related to the document * diff --git a/src/components/modules/ui.ts b/src/components/modules/ui.ts index a4d3baad3..708592dfe 100644 --- a/src/components/modules/ui.ts +++ b/src/components/modules/ui.ts @@ -773,12 +773,13 @@ export default class UI extends Module { */ const element = event.target as Element; const ctrlKey = event.metaKey || event.ctrlKey; - - if ($.isAnchor(element) && ctrlKey) { + const anchor = $.getAnchor(element); + + if (anchor && ctrlKey) { event.stopImmediatePropagation(); event.stopPropagation(); - const href = element.getAttribute('href'); + const href = anchor.getAttribute('href'); const validUrl = _.getValidUrl(href); _.openTab(validUrl); From 029d37755300eb8c8de2c96ce4b62267026b24ba Mon Sep 17 00:00:00 2001 From: Eugeny Date: Fri, 6 Mar 2026 01:33:16 +0300 Subject: [PATCH 2/6] add test --- test/cypress/tests/inline-tools/link.cy.ts | 30 ++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/test/cypress/tests/inline-tools/link.cy.ts b/test/cypress/tests/inline-tools/link.cy.ts index 8ae7220d1..f81e5a4b2 100644 --- a/test/cypress/tests/inline-tools/link.cy.ts +++ b/test/cypress/tests/inline-tools/link.cy.ts @@ -192,4 +192,34 @@ describe('Inline Tool Link', () => { .should('have.attr', 'href', 'https://editorjs.io') .should('contain', 'Bold and italic text'); }); + + it('should open formatted link in the same tab', () => { + cy.createEditor({ + data: { + blocks: [ + { + type: 'paragraph', + data: { + text: 'Link text', + }, + }, + ], + }, + }); + + const editor = '[data-cy=editorjs]'; + + cy.get(`${editor} .ce-paragraph`).selectText('Link text'); + cy.get(`${editor} [data-item-name=link]`).click(); + cy.get(`${editor} .ce-inline-tool-input`).type('https://test.io{enter}'); + + cy.get(`${editor} a`).selectText('Link text'); + cy.get(`${editor} [data-item-name=italic]`).click(); + + cy.window().then((win) => cy.stub(win, 'open').as('open')); + + cy.contains(`${editor} i`, 'Link text').click({ ctrlKey: true }); + + cy.get('@open').should('have.been.calledWith', 'https://test.io/'); + }) }); From f7f0bbe6f7385d59334df6bffa0449ac50cbb69c Mon Sep 17 00:00:00 2001 From: Eugeny Date: Wed, 11 Mar 2026 02:40:46 +0300 Subject: [PATCH 3/6] fix lint --- test/cypress/tests/inline-tools/link.cy.ts | 36 ++++++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/test/cypress/tests/inline-tools/link.cy.ts b/test/cypress/tests/inline-tools/link.cy.ts index f81e5a4b2..b67c643d9 100644 --- a/test/cypress/tests/inline-tools/link.cy.ts +++ b/test/cypress/tests/inline-tools/link.cy.ts @@ -207,19 +207,35 @@ describe('Inline Tool Link', () => { }, }); - const editor = '[data-cy=editorjs]'; + cy.get('[data-cy=editorjs]') + .find('.ce-paragraph') + .selectText('Link text'); + + cy.get('[data-cy=editorjs]') + .find('[data-item-name=link]') + .click(); + + cy.get('[data-cy=editorjs]') + .find('.ce-inline-tool-input') + .type('https://test.io/') + .type('{enter}'); - cy.get(`${editor} .ce-paragraph`).selectText('Link text'); - cy.get(`${editor} [data-item-name=link]`).click(); - cy.get(`${editor} .ce-inline-tool-input`).type('https://test.io{enter}'); + cy.get('[data-cy=editorjs]') + .find('div.ce-block') + .find('a') + .selectText('Link text'); - cy.get(`${editor} a`).selectText('Link text'); - cy.get(`${editor} [data-item-name=italic]`).click(); + cy.get('[data-cy=editorjs]') + .find('[data-item-name=italic]') + .click(); - cy.window().then((win) => cy.stub(win, 'open').as('open')); + cy.window().then((win) => { + cy.stub(win, 'open').as('windowOpen'); + }); - cy.contains(`${editor} i`, 'Link text').click({ ctrlKey: true }); + cy.contains('[data-cy=editorjs] div.ce-block i', 'Link text') + .click({ ctrlKey: true }); - cy.get('@open').should('have.been.calledWith', 'https://test.io/'); - }) + cy.get('@windowOpen').should('be.calledWith', 'https://test.io/'); + }); }); From 5cfd9881c5138fceb7c7fbb0bdc6579edd44a7cb Mon Sep 17 00:00:00 2001 From: Eugeny Date: Wed, 11 Mar 2026 02:53:02 +0300 Subject: [PATCH 4/6] bump version and add changelog --- docs/CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index ce1e69970..3367bec6a 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +### 2.31.5 + +- `Fix` - Handle __Ctrl + click__ on links with inline styles applied (e.g., bold, italic) + ### 2.31.4 - `Fix` - Prevent inline-toolbar re-renders when linked text is selected diff --git a/package.json b/package.json index 49b61542d..ee5f1499e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@editorjs/editorjs", - "version": "2.31.4", + "version": "2.31.5", "description": "Editor.js — open source block-style WYSIWYG editor with JSON output", "main": "dist/editorjs.umd.js", "module": "dist/editorjs.mjs", From 714ee3e6ef618dfd1330d0560cd306e6aa21f956 Mon Sep 17 00:00:00 2001 From: KoshaevEugeny <103786108+akulistus@users.noreply.github.com> Date: Wed, 11 Mar 2026 18:35:46 +0300 Subject: [PATCH 5/6] Apply suggestions from code review Co-authored-by: Peter Co-authored-by: KoshaevEugeny <103786108+akulistus@users.noreply.github.com> --- src/components/dom.ts | 2 +- src/components/modules/ui.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/dom.ts b/src/components/dom.ts index 573c2d98d..2f67e49a7 100644 --- a/src/components/dom.ts +++ b/src/components/dom.ts @@ -572,7 +572,7 @@ export default class Dom { * @param element - element to check * @returns {HTMLAnchorElement | null} */ - public static getAnchor(element: Element): HTMLAnchorElement | null { + public static getClosestAnchor(element: Element): HTMLAnchorElement | null { return element.closest("a"); } diff --git a/src/components/modules/ui.ts b/src/components/modules/ui.ts index 708592dfe..d8dc37989 100644 --- a/src/components/modules/ui.ts +++ b/src/components/modules/ui.ts @@ -773,7 +773,7 @@ export default class UI extends Module { */ const element = event.target as Element; const ctrlKey = event.metaKey || event.ctrlKey; - const anchor = $.getAnchor(element); + const anchor = $.getClosestAnchor(element); if (anchor && ctrlKey) { event.stopImmediatePropagation(); From a64c83259235c3d7e5876b30c6428317e79d52aa Mon Sep 17 00:00:00 2001 From: KoshaevEugeny <103786108+akulistus@users.noreply.github.com> Date: Wed, 11 Mar 2026 20:43:32 +0300 Subject: [PATCH 6/6] Update test/cypress/tests/inline-tools/link.cy.ts Co-authored-by: Peter --- test/cypress/tests/inline-tools/link.cy.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cypress/tests/inline-tools/link.cy.ts b/test/cypress/tests/inline-tools/link.cy.ts index b67c643d9..7d3fa121a 100644 --- a/test/cypress/tests/inline-tools/link.cy.ts +++ b/test/cypress/tests/inline-tools/link.cy.ts @@ -193,7 +193,7 @@ describe('Inline Tool Link', () => { .should('contain', 'Bold and italic text'); }); - it('should open formatted link in the same tab', () => { + it('should open a link if it is wrapped in another formatting', () => { cy.createEditor({ data: { blocks: [