From f7720562968413d8e1d569b3544803c340629661 Mon Sep 17 00:00:00 2001 From: gng0 <157993762+gng0@users.noreply.github.com> Date: Tue, 28 May 2024 18:16:55 +1200 Subject: [PATCH] DataLinks: Fixes datalinks with onClick and variables in url not being interpolated (#86253) * Use replaceVariables method on link url, fix and add unit tests * Refactor href processing for links * Prettier --- .../src/field/fieldOverrides.test.ts | 49 +++++++++++++++++-- .../grafana-data/src/field/fieldOverrides.ts | 29 +++++------ 2 files changed, 61 insertions(+), 17 deletions(-) diff --git a/packages/grafana-data/src/field/fieldOverrides.test.ts b/packages/grafana-data/src/field/fieldOverrides.test.ts index 67426e6f538..95d8f9fd9c1 100644 --- a/packages/grafana-data/src/field/fieldOverrides.test.ts +++ b/packages/grafana-data/src/field/fieldOverrides.test.ts @@ -971,7 +971,7 @@ describe('getLinksSupplier', () => { }); it('handles link click handlers', () => { const onClickSpy = jest.fn(); - const replaceSpy = jest.fn(); + const replaceSpy = jest.fn().mockImplementation((value, vars, format) => value); const f0 = createDataFrame({ name: 'A', fields: [ @@ -1008,8 +1008,8 @@ describe('getLinksSupplier', () => { links[0].onClick!({}); - expect(onClickSpy).toBeCalledTimes(1); - expect(replaceSpy).toBeCalledTimes(4); + expect(onClickSpy).toHaveBeenCalledTimes(1); + expect(replaceSpy).toHaveBeenCalledTimes(5); // check that onClick variable replacer has scoped vars bound to it expect(replaceSpy.mock.calls[1][1]).toHaveProperty('foo', { text: 'bar', value: 'bar' }); }); @@ -1057,6 +1057,49 @@ describe('getLinksSupplier', () => { expect(replaceSpy.mock.calls[1][1]).toHaveProperty('foo', { text: 'bar', value: 'bar' }); }); }); + + it('handles dynamic links with onclick handler', () => { + const replaceSpy = jest.fn().mockReturnValue('url interpolated 10'); + const onClickUrlSpy = jest.fn(); + const scopedVars = { foo: { text: 'bar', value: 'bar' } }; + const f0 = createDataFrame({ + name: 'A', + fields: [ + { + name: 'message', + type: FieldType.string, + config: { + links: [ + { + url: 'should be ignored', + onClick: (evt) => { + onClickUrlSpy(); + evt.replaceVariables?.('${foo}'); + }, + title: 'title to be interpolated', + }, + { + url: 'should not be ignored', + title: 'title to be interpolated', + }, + ], + }, + }, + ], + }); + + const supplier = getLinksSupplier(f0, f0.fields[0], scopedVars, replaceSpy); + const links = supplier({}); + links[0].onClick!({}); + + expect(onClickUrlSpy).toHaveBeenCalledTimes(1); + expect(links.length).toBe(2); + expect(links[0].href).toEqual('url interpolated 10'); + expect(links[0].onClick).toBeDefined(); + expect(replaceSpy).toHaveBeenCalledTimes(5); + // check that onClick variable replacer has scoped vars bound to it + expect(replaceSpy.mock.calls[1][1]).toHaveProperty('foo', { text: 'bar', value: 'bar' }); + }); }); describe('applyRawFieldOverrides', () => { diff --git a/packages/grafana-data/src/field/fieldOverrides.ts b/packages/grafana-data/src/field/fieldOverrides.ts index 4047a589d96..e0cdf966f06 100644 --- a/packages/grafana-data/src/field/fieldOverrides.ts +++ b/packages/grafana-data/src/field/fieldOverrides.ts @@ -468,9 +468,23 @@ export const getLinksSupplier = let linkModel: LinkModel; + let href = + link.onClick || !link.onBuildUrl + ? link.url + : link.onBuildUrl({ + origin: field, + replaceVariables: boundReplaceVariables, + }); + + if (href) { + href = locationUtil.assureBaseUrl(href.replace(/\n/g, '')); + href = replaceVariables(href, dataLinkScopedVars, VariableFormatID.UriEncode); + href = locationUtil.processUrl(href); + } + if (link.onClick) { linkModel = { - href: link.url, + href, title: replaceVariables(link.title || '', dataLinkScopedVars), target: link.targetBlank ? '_blank' : undefined, onClick: (evt: MouseEvent, origin: Field) => { @@ -483,19 +497,6 @@ export const getLinksSupplier = origin: field, }; } else { - let href = link.onBuildUrl - ? link.onBuildUrl({ - origin: field, - replaceVariables: boundReplaceVariables, - }) - : link.url; - - if (href) { - href = locationUtil.assureBaseUrl(href.replace(/\n/g, '')); - href = replaceVariables(href, dataLinkScopedVars, VariableFormatID.UriEncode); - href = locationUtil.processUrl(href); - } - linkModel = { href, title: replaceVariables(link.title || '', dataLinkScopedVars),