Commit fa278139 by Hugo Häggmark Committed by GitHub

DashboardLinks: values in links are updated when variables change (#27926)

* DashboardLinks: values in links are updated when variable changes

* Tests: adds e2e test that verifies links
parent 4e4242d4
{
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": "-- Grafana --",
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"gnetId": null,
"graphTooltip": 0,
"id": null,
"iteration": 1601526910610,
"links": [
{
"icon": "external link",
"includeVars": true,
"tags": [],
"title": "Grafana",
"tooltip": "",
"type": "link",
"url": "http://www.grafana.com"
},
{
"asDropdown": true,
"icon": "external link",
"includeVars": true,
"tags": ["templating"],
"title": "Link as DropDown",
"type": "dashboards"
},
{
"icon": "external link",
"includeVars": true,
"tags": ["demo"],
"type": "dashboards"
}
],
"panels": [
{
"description": "",
"fieldConfig": {
"defaults": {
"custom": {}
},
"overrides": []
},
"gridPos": {
"h": 9,
"w": 12,
"x": 0,
"y": 0
},
"id": 2,
"options": {
"content": "# ${custom.text}\n ",
"mode": "markdown"
},
"pluginVersion": "7.3.0-pre",
"targets": [
{
"refId": "A",
"scenarioId": "random_walk"
}
],
"timeFrom": null,
"timeShift": null,
"title": "${custom.text}",
"type": "text"
}
],
"schemaVersion": 26,
"style": "dark",
"tags": ["gdev", "templating"],
"templating": {
"list": [
{
"allValue": null,
"current": {
"selected": false,
"text": "All",
"value": "$__all"
},
"hide": 0,
"includeAll": true,
"label": null,
"multi": true,
"name": "custom",
"options": [
{
"selected": true,
"text": "All",
"value": "$__all"
},
{
"selected": false,
"text": "p1",
"value": "p1"
},
{
"selected": false,
"text": "p2",
"value": "p2"
},
{
"selected": false,
"text": "p3",
"value": "p3"
}
],
"query": "p1,p2,p3",
"skipUrlSync": false,
"type": "custom"
}
]
},
"time": {
"from": "now-6h",
"to": "now"
},
"timepicker": {},
"timezone": "",
"title": "Templating - Dashboard Links and Variables",
"uid": "yBCC3aKGk",
"version": 7
}
import { e2e } from '@grafana/e2e';
e2e.scenario({
describeName: 'Templating',
itName: 'Tests dashboard links and variables in links',
addScenarioDataSource: false,
addScenarioDashBoard: false,
skipScenario: false,
scenario: () => {
e2e.flows.openDashboard({ uid: 'yBCC3aKGk' });
e2e().server();
e2e()
.route({
method: 'GET',
url: '/api/search?tag=templating&limit=100',
})
.as('tagsTemplatingSearch');
e2e()
.route({
method: 'GET',
url: '/api/search?tag=demo&limit=100',
})
.as('tagsDemoSearch');
// waiting for links to render, couldn't find a better way using routes for instance
e2e().wait(1000);
const verifyLinks = (variableValue: string) => {
e2e.components.DashboardLinks.link()
.should('be.visible')
.and(links => {
expect(links).to.have.length(13);
for (let index = 0; index < links.length; index++) {
expect(Cypress.$(links[index]).attr('href')).contains(`var-custom=${variableValue}`);
}
});
};
e2e.components.DashboardLinks.dropDown()
.should('be.visible')
.click()
.wait('@tagsTemplatingSearch')
.wait('@tagsDemoSearch');
// verify all links, should have All value
verifyLinks('All');
e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownValueLinkTexts('All')
.should('be.visible')
.click();
e2e.pages.Dashboard.SubMenu.submenuItemValueDropDownOptionTexts('p2')
.should('be.visible')
.click();
e2e.pages.Dashboard.Toolbar.navBar().click();
e2e.components.DashboardLinks.dropDown()
.should('be.visible')
.click()
.wait('@tagsTemplatingSearch')
.wait('@tagsDemoSearch');
// verify all links, should have p2 value
verifyLinks('p2');
},
});
...@@ -153,4 +153,9 @@ export const Components = { ...@@ -153,4 +153,9 @@ export const Components = {
section: 'Search section', section: 'Search section',
items: 'Search items', items: 'Search items',
}, },
DashboardLinks: {
container: 'Dashboard link container',
dropDown: 'Dashboard link dropdown',
link: 'Dashboard link',
},
}; };
...@@ -9,6 +9,7 @@ import { DashboardLink } from '../../state/DashboardModel'; ...@@ -9,6 +9,7 @@ import { DashboardLink } from '../../state/DashboardModel';
import { iconMap } from '../DashLinks/DashLinksEditorCtrl'; import { iconMap } from '../DashLinks/DashLinksEditorCtrl';
import { useEffectOnce } from 'react-use'; import { useEffectOnce } from 'react-use';
import { CoreEvents } from 'app/types'; import { CoreEvents } from 'app/types';
import { selectors } from '@grafana/e2e-selectors';
export interface Props { export interface Props {
dashboard: DashboardModel; dashboard: DashboardModel;
...@@ -42,14 +43,19 @@ export const DashboardLinks: FC<Props> = ({ dashboard, links }) => { ...@@ -42,14 +43,19 @@ export const DashboardLinks: FC<Props> = ({ dashboard, links }) => {
} }
const linkElement = ( const linkElement = (
<a className="gf-form-label" href={sanitizeUrl(linkInfo.href)} target={link.targetBlank ? '_blank' : '_self'}> <a
className="gf-form-label"
href={sanitizeUrl(linkInfo.href)}
target={link.targetBlank ? '_blank' : '_self'}
aria-label={selectors.components.DashboardLinks.link}
>
<Icon name={iconMap[link.icon] as IconName} style={{ marginRight: '4px' }} /> <Icon name={iconMap[link.icon] as IconName} style={{ marginRight: '4px' }} />
<span>{sanitize(linkInfo.title)}</span> <span>{sanitize(linkInfo.title)}</span>
</a> </a>
); );
return ( return (
<div key={key} className="gf-form"> <div key={key} className="gf-form" aria-label={selectors.components.DashboardLinks.container}>
{link.tooltip ? <Tooltip content={link.tooltip}>{linkElement}</Tooltip> : linkElement} {link.tooltip ? <Tooltip content={link.tooltip}>{linkElement}</Tooltip> : linkElement}
</div> </div>
); );
......
...@@ -5,6 +5,7 @@ import { getBackendSrv } from 'app/core/services/backend_srv'; ...@@ -5,6 +5,7 @@ import { getBackendSrv } from 'app/core/services/backend_srv';
import { getLinkSrv } from '../../../panel/panellinks/link_srv'; import { getLinkSrv } from '../../../panel/panellinks/link_srv';
import { DashboardLink } from '../../state/DashboardModel'; import { DashboardLink } from '../../state/DashboardModel';
import { DashboardSearchHit } from 'app/features/search/types'; import { DashboardSearchHit } from 'app/features/search/types';
import { selectors } from '@grafana/e2e-selectors';
interface Props { interface Props {
link: DashboardLink; link: DashboardLink;
...@@ -24,7 +25,7 @@ export class DashboardLinksDashboard extends PureComponent<Props, State> { ...@@ -24,7 +25,7 @@ export class DashboardLinksDashboard extends PureComponent<Props, State> {
} }
componentDidUpdate(prevProps: Readonly<Props>) { componentDidUpdate(prevProps: Readonly<Props>) {
if (this.props.link !== prevProps.link) { if (this.props.link !== prevProps.link || this.props.linkInfo !== prevProps.linkInfo) {
this.searchForDashboards(); this.searchForDashboards();
} }
} }
...@@ -38,11 +39,11 @@ export class DashboardLinksDashboard extends PureComponent<Props, State> { ...@@ -38,11 +39,11 @@ export class DashboardLinksDashboard extends PureComponent<Props, State> {
this.setState({ resolvedLinks }); this.setState({ resolvedLinks });
}; };
renderElement = (linkElement: JSX.Element, key: string) => { renderElement = (linkElement: JSX.Element, key: string, selector: string) => {
const { link } = this.props; const { link } = this.props;
return ( return (
<div className="gf-form" key={key}> <div className="gf-form" key={key} aria-label={selector}>
{link.tooltip && <Tooltip content={link.tooltip}>{linkElement}</Tooltip>} {link.tooltip && <Tooltip content={link.tooltip}>{linkElement}</Tooltip>}
{!link.tooltip && <>{linkElement}</>} {!link.tooltip && <>{linkElement}</>}
</div> </div>
...@@ -58,12 +59,21 @@ export class DashboardLinksDashboard extends PureComponent<Props, State> { ...@@ -58,12 +59,21 @@ export class DashboardLinksDashboard extends PureComponent<Props, State> {
{resolvedLinks.length > 0 && {resolvedLinks.length > 0 &&
resolvedLinks.map((resolvedLink, index) => { resolvedLinks.map((resolvedLink, index) => {
const linkElement = ( const linkElement = (
<a className="gf-form-label" href={resolvedLink.url} target={link.targetBlank ? '_blank' : '_self'}> <a
className="gf-form-label"
href={resolvedLink.url}
target={link.targetBlank ? '_blank' : '_self'}
aria-label={selectors.components.DashboardLinks.link}
>
<Icon name="apps" style={{ marginRight: '4px' }} /> <Icon name="apps" style={{ marginRight: '4px' }} />
<span>{resolvedLink.title}</span> <span>{resolvedLink.title}</span>
</a> </a>
); );
return this.renderElement(linkElement, `dashlinks-list-item-${resolvedLink.id}-${index}`); return this.renderElement(
linkElement,
`dashlinks-list-item-${resolvedLink.id}-${index}`,
selectors.components.DashboardLinks.container
);
})} })}
</> </>
); );
...@@ -89,7 +99,11 @@ export class DashboardLinksDashboard extends PureComponent<Props, State> { ...@@ -89,7 +99,11 @@ export class DashboardLinksDashboard extends PureComponent<Props, State> {
resolvedLinks.map((resolvedLink, index) => { resolvedLinks.map((resolvedLink, index) => {
return ( return (
<li key={`dashlinks-dropdown-item-${resolvedLink.id}-${index}`}> <li key={`dashlinks-dropdown-item-${resolvedLink.id}-${index}`}>
<a href={resolvedLink.url} target={link.targetBlank ? '_blank' : '_self'}> <a
href={resolvedLink.url}
target={link.targetBlank ? '_blank' : '_self'}
aria-label={selectors.components.DashboardLinks.link}
>
{resolvedLink.title} {resolvedLink.title}
</a> </a>
</li> </li>
...@@ -99,7 +113,7 @@ export class DashboardLinksDashboard extends PureComponent<Props, State> { ...@@ -99,7 +113,7 @@ export class DashboardLinksDashboard extends PureComponent<Props, State> {
</> </>
); );
return this.renderElement(linkElement, 'dashlinks-dropdown'); return this.renderElement(linkElement, 'dashlinks-dropdown', selectors.components.DashboardLinks.dropDown);
}; };
render() { render() {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment