Compare commits

...

1 commit

Author SHA1 Message Date
KoshaevEugeny
530ec56bb8
fix(link-tool): open new window with url when formatted link clicked via ctrl key (#2996)
* fix(link-tool): open new window with url when formatted link clicked via ctrl key

* add test

* fix lint

* bump version and add changelog

* Apply suggestions from code review

Co-authored-by: Peter <specc.dev@gmail.com>
Co-authored-by: KoshaevEugeny <103786108+akulistus@users.noreply.github.com>

* Update test/cypress/tests/inline-tools/link.cy.ts

Co-authored-by: Peter <specc.dev@gmail.com>

---------

Co-authored-by: Peter <specc.dev@gmail.com>
2026-03-11 20:58:30 +03:00
5 changed files with 65 additions and 4 deletions

View file

@ -1,5 +1,9 @@
# Changelog # Changelog
### 2.31.5
- `Fix` - Handle __Ctrl + click__ on links with inline styles applied (e.g., bold, italic)
### 2.31.4 ### 2.31.4
- `Fix` - Prevent inline-toolbar re-renders when linked text is selected - `Fix` - Prevent inline-toolbar re-renders when linked text is selected

View file

@ -1,6 +1,6 @@
{ {
"name": "@editorjs/editorjs", "name": "@editorjs/editorjs",
"version": "2.31.4", "version": "2.31.5",
"description": "Editor.js — open source block-style WYSIWYG editor with JSON output", "description": "Editor.js — open source block-style WYSIWYG editor with JSON output",
"main": "dist/editorjs.umd.js", "main": "dist/editorjs.umd.js",
"module": "dist/editorjs.mjs", "module": "dist/editorjs.mjs",

View file

@ -566,6 +566,16 @@ export default class Dom {
return element.tagName.toLowerCase() === 'a'; 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 getClosestAnchor(element: Element): HTMLAnchorElement | null {
return element.closest("a");
}
/** /**
* Return element's offset related to the document * Return element's offset related to the document
* *

View file

@ -773,12 +773,13 @@ export default class UI extends Module<UINodes> {
*/ */
const element = event.target as Element; const element = event.target as Element;
const ctrlKey = event.metaKey || event.ctrlKey; const ctrlKey = event.metaKey || event.ctrlKey;
const anchor = $.getClosestAnchor(element);
if ($.isAnchor(element) && ctrlKey) {
if (anchor && ctrlKey) {
event.stopImmediatePropagation(); event.stopImmediatePropagation();
event.stopPropagation(); event.stopPropagation();
const href = element.getAttribute('href'); const href = anchor.getAttribute('href');
const validUrl = _.getValidUrl(href); const validUrl = _.getValidUrl(href);
_.openTab(validUrl); _.openTab(validUrl);

View file

@ -192,4 +192,50 @@ describe('Inline Tool Link', () => {
.should('have.attr', 'href', 'https://editorjs.io') .should('have.attr', 'href', 'https://editorjs.io')
.should('contain', 'Bold and italic text'); .should('contain', 'Bold and italic text');
}); });
it('should open a link if it is wrapped in another formatting', () => {
cy.createEditor({
data: {
blocks: [
{
type: 'paragraph',
data: {
text: 'Link text',
},
},
],
},
});
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('[data-cy=editorjs]')
.find('div.ce-block')
.find('a')
.selectText('Link text');
cy.get('[data-cy=editorjs]')
.find('[data-item-name=italic]')
.click();
cy.window().then((win) => {
cy.stub(win, 'open').as('windowOpen');
});
cy.contains('[data-cy=editorjs] div.ce-block i', 'Link text')
.click({ ctrlKey: true });
cy.get('@windowOpen').should('be.calledWith', 'https://test.io/');
});
}); });