mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-18 12:05:05 +00:00
fix: External links in widget not opening in new tab (#11608)
This commit is contained in:
@@ -38,16 +38,9 @@ export const openExternalLinksInNewTab = () => {
|
|||||||
document.addEventListener('click', event => {
|
document.addEventListener('click', event => {
|
||||||
if (!isOnArticlePage) return;
|
if (!isOnArticlePage) return;
|
||||||
|
|
||||||
// Some of the links come wrapped in strong tag through prosemirror
|
const link = event.target.closest('a');
|
||||||
|
|
||||||
const isTagAnchor = event.target.tagName === 'A';
|
|
||||||
const isParentTagAnchor =
|
|
||||||
event.target.tagName === 'STRONG' &&
|
|
||||||
event.target.parentNode.tagName === 'A';
|
|
||||||
|
|
||||||
if (isTagAnchor || isParentTagAnchor) {
|
|
||||||
const link = isTagAnchor ? event.target : event.target.parentNode;
|
|
||||||
|
|
||||||
|
if (link) {
|
||||||
const isInternalLink =
|
const isInternalLink =
|
||||||
link.hostname === window.location.hostname ||
|
link.hostname === window.location.hostname ||
|
||||||
link.href.includes(customDomain) ||
|
link.href.includes(customDomain) ||
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||||
import { JSDOM } from 'jsdom';
|
import { JSDOM } from 'jsdom';
|
||||||
import { InitializationHelpers } from '../portalHelpers';
|
import {
|
||||||
|
InitializationHelpers,
|
||||||
|
openExternalLinksInNewTab,
|
||||||
|
} from '../portalHelpers';
|
||||||
|
|
||||||
describe('InitializationHelpers.navigateToLocalePage', () => {
|
describe('InitializationHelpers.navigateToLocalePage', () => {
|
||||||
let dom;
|
let dom;
|
||||||
@@ -44,3 +47,96 @@ describe('InitializationHelpers.navigateToLocalePage', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('openExternalLinksInNewTab', () => {
|
||||||
|
let dom;
|
||||||
|
let document;
|
||||||
|
let window;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
dom = new JSDOM(
|
||||||
|
`<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<div id="cw-article-content">
|
||||||
|
<a href="https://external.com" id="external">External</a>
|
||||||
|
<a href="https://app.chatwoot.com/page" id="internal">Internal</a>
|
||||||
|
<a href="https://custom.domain.com/page" id="custom">Custom</a>
|
||||||
|
<a href="https://example.com" id="nested"><code>Code</code><strong>Bold</strong></a>
|
||||||
|
<ul>
|
||||||
|
<li>Visit the preferences centre here > <a href="https://external.com" id="list-link"><strong>https://external.com</strong></a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>`,
|
||||||
|
{ url: 'https://app.chatwoot.com/hc/article' }
|
||||||
|
);
|
||||||
|
|
||||||
|
document = dom.window.document;
|
||||||
|
window = dom.window;
|
||||||
|
|
||||||
|
window.portalConfig = {
|
||||||
|
customDomain: 'custom.domain.com',
|
||||||
|
hostURL: 'app.chatwoot.com',
|
||||||
|
};
|
||||||
|
|
||||||
|
global.document = document;
|
||||||
|
global.window = window;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
dom = null;
|
||||||
|
document = null;
|
||||||
|
window = null;
|
||||||
|
delete global.document;
|
||||||
|
delete global.window;
|
||||||
|
});
|
||||||
|
|
||||||
|
const simulateClick = selector => {
|
||||||
|
const element = document.querySelector(selector);
|
||||||
|
const event = new window.MouseEvent('click', { bubbles: true });
|
||||||
|
element.dispatchEvent(event);
|
||||||
|
return element.closest('a') || element;
|
||||||
|
};
|
||||||
|
|
||||||
|
it('opens external links in new tab', () => {
|
||||||
|
openExternalLinksInNewTab();
|
||||||
|
|
||||||
|
const link = simulateClick('#external');
|
||||||
|
|
||||||
|
expect(link.target).toBe('_blank');
|
||||||
|
expect(link.rel).toBe('noopener noreferrer');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('preserves internal links', () => {
|
||||||
|
openExternalLinksInNewTab();
|
||||||
|
|
||||||
|
const internal = simulateClick('#internal');
|
||||||
|
const custom = simulateClick('#custom');
|
||||||
|
|
||||||
|
expect(internal.target).not.toBe('_blank');
|
||||||
|
expect(custom.target).not.toBe('_blank');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles clicks on nested elements', () => {
|
||||||
|
openExternalLinksInNewTab();
|
||||||
|
|
||||||
|
simulateClick('#nested code');
|
||||||
|
simulateClick('#nested strong');
|
||||||
|
|
||||||
|
const link = document.getElementById('nested');
|
||||||
|
expect(link.target).toBe('_blank');
|
||||||
|
expect(link.rel).toBe('noopener noreferrer');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles links inside list items with strong tags', () => {
|
||||||
|
openExternalLinksInNewTab();
|
||||||
|
|
||||||
|
// Click on the strong element inside the link in the list
|
||||||
|
simulateClick('#list-link strong');
|
||||||
|
|
||||||
|
const link = document.getElementById('list-link');
|
||||||
|
expect(link.target).toBe('_blank');
|
||||||
|
expect(link.rel).toBe('noopener noreferrer');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user