diff --git a/devenv/dev-dashboards/feature-templating/templating-dashboard-links-and-variables.json b/devenv/dev-dashboards/feature-templating/templating-dashboard-links-and-variables.json index f5751ec42f5..ab94db3f055 100644 --- a/devenv/dev-dashboards/feature-templating/templating-dashboard-links-and-variables.json +++ b/devenv/dev-dashboards/feature-templating/templating-dashboard-links-and-variables.json @@ -16,7 +16,7 @@ "gnetId": null, "graphTooltip": 0, "id": null, - "iteration": 1601526910610, + "iteration": 1646409057541, "links": [ { "icon": "external link", @@ -73,6 +73,61 @@ "timeShift": null, "title": "${custom.text}", "type": "text" + }, + { + "id": 4, + "gridPos": { + "h": 9, + "w": 9, + "x": 12, + "y": 0 + }, + "type": "stat", + "title": "Panel Title", + "fieldConfig": { + "defaults": { + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "color": { + "mode": "thresholds" + }, + "links": [ + { + "title": "Var Link", + "url": "/d/vmie2cmWz/bar-gauge-demo?var-custom=$custom" + } + ] + }, + "overrides": [] + }, + "options": { + "reduceOptions": { + "values": false, + "calcs": [ + "lastNotNull" + ], + "fields": "" + }, + "orientation": "auto", + "textMode": "auto", + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto" + }, + "pluginVersion": "8.5.0-pre", + "datasource": null } ], "schemaVersion": 26, @@ -112,6 +167,11 @@ "selected": false, "text": "p3", "value": "p3" + }, + { + "selected": false, + "text": "p4", + "value": "test%25value" } ], "query": "p1,p2,p3", diff --git a/e2e/dashboards-suite/templating-dashboard-links-and-variables.spec.ts b/e2e/dashboards-suite/templating-dashboard-links-and-variables.spec.ts index c2eb79f780c..2c4fe236baf 100644 --- a/e2e/dashboards-suite/templating-dashboard-links-and-variables.spec.ts +++ b/e2e/dashboards-suite/templating-dashboard-links-and-variables.spec.ts @@ -34,7 +34,7 @@ e2e.scenario({ expect(links).to.have.length.greaterThan(13); for (let index = 0; index < links.length; index++) { - expect(Cypress.$(links[index]).attr('href')).contains(`var-custom=${variableValue}`); + expect(Cypress.$(links[index]).attr('href')).contains(`var-custom=${encodeURI(variableValue)}`); } }); }; @@ -44,6 +44,14 @@ e2e.scenario({ // verify all links, should have All value verifyLinks('All'); + // Data links should percent encode var values + e2e() + .get('[aria-label="Data link"]') + .should('exist') + .and((link) => { + expect(link.attr('href')).contains(encodeURI('test%25value')); + }); + e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('All').should('be.visible').click(); e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('p2').should('be.visible').click(); diff --git a/packages/grafana-data/src/field/fieldOverrides.ts b/packages/grafana-data/src/field/fieldOverrides.ts index a2292847c2f..d6358395d80 100644 --- a/packages/grafana-data/src/field/fieldOverrides.ts +++ b/packages/grafana-data/src/field/fieldOverrides.ts @@ -1,7 +1,6 @@ import { ApplyFieldOverrideOptions, DataFrame, - DataLink, DisplayProcessor, DisplayValue, DynamicConfigValue, @@ -353,7 +352,7 @@ export const getLinksSupplier = const timeRangeUrl = locationUtil.getTimeRangeUrlParams(); const { timeField } = getTimeField(frame); - return field.config.links.map((link: DataLink) => { + return field.config.links.map((link) => { const variablesQuery = locationUtil.getVariablesUrlParams(); let dataFrameVars = {}; let valueVars = {}; @@ -439,7 +438,7 @@ export const getLinksSupplier = } let href = locationUtil.assureBaseUrl(link.url.replace(/\n/g, '')); - href = replaceVariables(href, variables); + href = replaceVariables(href, variables, encodeURIComponent); href = locationUtil.processUrl(href); const info: LinkModel = {