diff --git a/public/services/shared-link.ts b/public/services/shared-link.ts index 4a078735e..defb60c12 100644 --- a/public/services/shared-link.ts +++ b/public/services/shared-link.ts @@ -12,7 +12,6 @@ * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ -import { parse } from 'url'; import { CoreStart } from 'opensearch-dashboards/public'; import { API_ENDPOINT_MULTITENANCY } from '../apps/configuration/constants'; @@ -72,45 +71,38 @@ export function updateClipboard( originalValue: string | undefined, tenant: string ) { - const shareButton = document.querySelector('[data-share-url]') as any; const target = document.querySelector('body > span'); if (!originalValue) { originalValue = urlPart; } - const { host, pathname, search } = parse(urlPart); - const queryDelimiter = !search ? '?' : '&'; - - // The url parser returns null if the search is empty. Change that to an empty - // string so that we can use it to build the values later - if (search && search.toLowerCase().indexOf('security_tenant') > -1) { - // If we for some reason already have a tenant in the URL we skip any updates + const originalUrl = new URL(urlPart); + if (originalUrl.searchParams?.has('security_tenant')) { return originalValue; } + originalUrl.searchParams.append('security_tenant', tenant); - // A helper for finding the part in the string that we want to extend/replace - const valueToReplace = host! + pathname! + (search || ''); - const replaceWith = - valueToReplace + queryDelimiter + 'security_tenant=' + encodeURIComponent(tenant); + const originalValueWithTenant = originalValue.replace(urlPart, originalUrl.toString()); - setClipboardAndTarget(shareButton, target, replaceWith, originalValue); + setClipboardAndTarget(target, originalValueWithTenant, originalValue); } -export function setClipboardAndTarget( - shareButton: any, - target: any, - newValue: string, - originalValue: string -) { - const range = document.createRange() as any; - const referenceNode = document.getElementsByTagName('span').item(0); +export function setClipboardAndTarget(target: any, newValue: string, originalValue: string) { + let range = document.createRange() as any; + const referenceNode = target; + + const selection = document.getSelection(); + if (selection?.rangeCount === 1) { + range = selection.getRangeAt(0); + } range.selectNode(referenceNode); - shareButton.removeAllRanges(); - shareButton.addRange(range); + selection?.removeAllRanges(); if (newValue !== originalValue) { target.textContent = newValue; } + + selection?.addRange(range); } diff --git a/public/services/test/shared-link.test.ts b/public/services/test/shared-link.test.ts index 8849b2f76..92ec52f26 100644 --- a/public/services/test/shared-link.test.ts +++ b/public/services/test/shared-link.test.ts @@ -73,6 +73,14 @@ describe('updateClipboard function', () => { addRange: jest.fn(), }; + document.createRange = jest.fn().mockReturnValue({ + selectNode: jest.fn(), + }); + document.getSelection = jest.fn().mockReturnValue({ + removeAllRanges: jest.fn(), + addRange: jest.fn(), + }); + const targetMock: any = { textContent: 'mocked-text-content', }; @@ -84,7 +92,26 @@ describe('updateClipboard function', () => { return targetMock; } }); - updateClipboard('mocked-url-part', 'mocked-original-value', 'mocked-tenant'); + + updateClipboard( + 'http://hostname.com/route?param1=value1#hash', + 'http://hostname.com/route?param1=value1#hash', + 'mocked-tenant' + ); + + expect(targetMock.textContent).toBe( + 'http://hostname.com/route?param1=value1&security_tenant=mocked-tenant#hash' + ); + + updateClipboard( + 'http://hostname.com/route?param1=value1#hash', + '', + 'mocked-tenant' + ); + + expect(targetMock.textContent).toBe( + '' + ); }); }); describe('setClipboardAndTarget function', () => { @@ -94,9 +121,19 @@ describe('setClipboardAndTarget function', () => { addRange: jest.fn(), }; + document.createRange = jest.fn().mockReturnValue({ + selectNode: jest.fn(), + }); + document.getSelection = jest.fn().mockReturnValue({ + removeAllRanges: jest.fn(), + addRange: jest.fn(), + }); + const targetMock: any = { textContent: 'mocked-text-content', }; - setClipboardAndTarget(shareButtonMock, targetMock, 'mocked-new-value', 'mocked-original-value'); + setClipboardAndTarget(targetMock, 'mocked-new-value', 'mocked-original-value'); + + expect(targetMock.textContent).toBe('mocked-new-value'); }); });