diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index bca6f9236..0adb2fa2b 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +### 2.31.7 + +- `Fix` - Inability to unlink text using the inline toolbar unlink action + ### 2.31.6 - `Fix` - Widen `sanitize` type on `BlockTool` and `BaseToolConstructable` to accept per-field `SanitizerConfig` diff --git a/package.json b/package.json index 3d964610b..2bc48489f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@editorjs/editorjs", - "version": "2.31.6", + "version": "2.31.7", "description": "Editor.js — open source block-style WYSIWYG editor with JSON output", "main": "dist/editorjs.umd.js", "module": "dist/editorjs.mjs", diff --git a/src/components/inline-tools/inline-tool-link.ts b/src/components/inline-tools/inline-tool-link.ts index 0bef25c73..b881c962e 100644 --- a/src/components/inline-tools/inline-tool-link.ts +++ b/src/components/inline-tools/inline-tool-link.ts @@ -74,6 +74,11 @@ export default class LinkInlineTool implements InlineTool { input: null, }; + /** + * Indicates whether the button has been clicked + */ + private BUTTON_CLICKED = false; + /** * SelectionUtils instance */ @@ -125,6 +130,10 @@ export default class LinkInlineTool implements InlineTool { this.nodes.button.innerHTML = IconLink; + this.nodes.button.addEventListener('click', () => { + this.BUTTON_CLICKED = true; + }); + return this.nodes.button; } @@ -151,6 +160,13 @@ export default class LinkInlineTool implements InlineTool { * @param {Range} range - range to wrap with link */ public surround(range: Range): void { + /** + * Preserve the current click state + */ + const buttonClicked = this.BUTTON_CLICKED; + + this.BUTTON_CLICKED = false; + /** * Range will be null when user makes second click on the 'link icon' to close opened input */ @@ -171,22 +187,12 @@ export default class LinkInlineTool implements InlineTool { /** * Unlink icon pressed */ - if (parentAnchor) { - /** - * If input is not opened, treat click as explicit unlink action. - * If input is opened (e.g., programmatic close when switching tools), avoid unlinking. - */ - if (!this.inputOpened) { - this.selection.expandToTag(parentAnchor); - this.unlink(); - this.closeActions(); - this.checkState(); - this.toolbar.close(); - } else { - /** Only close actions without clearing saved selection to preserve user state */ - this.closeActions(false); - this.checkState(); - } + if (parentAnchor && buttonClicked) { + this.selection.expandToTag(parentAnchor); + this.unlink(); + this.closeActions(); + this.checkState(); + this.toolbar.close(); return; } diff --git a/test/cypress/tests/inline-tools/link.cy.ts b/test/cypress/tests/inline-tools/link.cy.ts index 7d3fa121a..6cd761d82 100644 --- a/test/cypress/tests/inline-tools/link.cy.ts +++ b/test/cypress/tests/inline-tools/link.cy.ts @@ -238,4 +238,115 @@ describe('Inline Tool Link', () => { cy.get('@windowOpen').should('be.calledWith', 'https://test.io/'); }); + + it('should unlink on popover item block click', () => { + 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=link]') + .click(); + + cy.get('[data-cy=editorjs]') + .find('div.ce-block') + .find('a') + .should('not.exist'); + }); + + it('should hide popover on selection change', () => { + 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-paragraph') + .click(); + + cy.get('[data-cy=editorjs]') + .find('[data-item-name=link]') + .should('not.exist'); + + cy.get('[data-cy=editorjs]') + .find('.ce-paragraph') + .find('span') + .should('not.exist'); + }); + + it('should restore selection and apply formatting on other popover item block click', () => { + 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('[data-item-name=bold]') + .click(); + + cy.get('[data-cy=editorjs]') + .find('div.ce-block') + .find('b') + .should('exist') + .and('have.text', 'Link text'); + }); });