Commit 4c0aa4ac by Marcus Andersson Committed by GitHub

DashboardLinks: will only refresh dashboard search when changing tags for link. (#29040)

* fixing so we dont run multiple dashboard links searches when changing variables.

* changed so we fetch the list when open the dashboard links dropdown.

* removed verification of unneccesary requests.
parent 0fc8b558
...@@ -59,8 +59,7 @@ e2e.scenario({ ...@@ -59,8 +59,7 @@ e2e.scenario({
e2e.components.DashboardLinks.dropDown() e2e.components.DashboardLinks.dropDown()
.should('be.visible') .should('be.visible')
.click() .click()
.wait('@tagsTemplatingSearch') .wait('@tagsTemplatingSearch');
.wait('@tagsDemoSearch');
// verify all links, should have p2 value // verify all links, should have p2 value
verifyLinks('p2'); verifyLinks('p2');
......
...@@ -27,7 +27,7 @@ describe('searchForTags', () => { ...@@ -27,7 +27,7 @@ describe('searchForTags', () => {
it('then tags from link should be used in search and limit should be 100', async () => { it('then tags from link should be used in search and limit should be 100', async () => {
const { link, backendSrv } = setupTestContext(); const { link, backendSrv } = setupTestContext();
const results = await searchForTags(link, { getBackendSrv: () => backendSrv }); const results = await searchForTags(link.tags, { getBackendSrv: () => backendSrv });
expect(results.length).toEqual(0); expect(results.length).toEqual(0);
expect(backendSrv.search).toHaveBeenCalledWith({ tag: ['A', 'B'], limit: 100 }); expect(backendSrv.search).toHaveBeenCalledWith({ tag: ['A', 'B'], limit: 100 });
......
import React, { createRef, PureComponent } from 'react'; import React, { useRef, useState } from 'react';
import { Icon, Tooltip } from '@grafana/ui'; import { Icon, Tooltip } from '@grafana/ui';
import { sanitize, sanitizeUrl } from '@grafana/data/src/text/sanitize'; import { sanitize, sanitizeUrl } from '@grafana/data/src/text/sanitize';
import { getBackendSrv } from 'app/core/services/backend_srv'; import { getBackendSrv } from 'app/core/services/backend_srv';
...@@ -6,6 +6,7 @@ import { getLinkSrv } from '../../../panel/panellinks/link_srv'; ...@@ -6,6 +6,7 @@ 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'; import { selectors } from '@grafana/e2e-selectors';
import { useAsync } from 'react-use';
interface Props { interface Props {
link: DashboardLink; link: DashboardLink;
...@@ -13,53 +14,56 @@ interface Props { ...@@ -13,53 +14,56 @@ interface Props {
dashboardId: any; dashboardId: any;
} }
interface State { export const DashboardLinksDashboard: React.FC<Props> = props => {
resolvedLinks: ResolvedLinkDTO[]; const { link, linkInfo } = props;
} const listRef = useRef<HTMLUListElement>(null);
const [opened, setOpened] = useState(0);
export class DashboardLinksDashboard extends PureComponent<Props, State> { const resolvedLinks = useResolvedLinks(props, opened);
state: State = { resolvedLinks: [] };
listItemRef = createRef<HTMLUListElement>();
componentDidMount() {
this.searchForDashboards();
}
componentDidUpdate(prevProps: Readonly<Props>) {
if (this.props.link !== prevProps.link || this.props.linkInfo !== prevProps.linkInfo) {
this.searchForDashboards();
}
}
searchForDashboards = async () => {
const { dashboardId, link } = this.props;
const searchHits = await searchForTags(link);
const resolvedLinks = resolveLinks(dashboardId, link, searchHits);
this.setState({ resolvedLinks });
};
renderElement = (linkElement: JSX.Element, key: string, selector: string) => {
const { link } = this.props;
if (link.asDropdown) {
return ( return (
<div className="gf-form" key={key} aria-label={selector}> <LinkElement link={link} key="dashlinks-dropdown" aria-label={selectors.components.DashboardLinks.dropDown}>
{link.tooltip && <Tooltip content={link.tooltip}>{linkElement}</Tooltip>} <>
{!link.tooltip && <>{linkElement}</>} <a
</div> onClick={() => setOpened(Date.now())}
className="gf-form-label gf-form-label--dashlink"
data-placement="bottom"
data-toggle="dropdown"
>
<Icon name="bars" style={{ marginRight: '4px' }} />
<span>{linkInfo.title}</span>
</a>
<ul className={`dropdown-menu ${getDropdownLocationCssClass(listRef.current)}`} role="menu" ref={listRef}>
{resolvedLinks.length > 0 &&
resolvedLinks.map((resolvedLink, index) => {
return (
<li key={`dashlinks-dropdown-item-${resolvedLink.id}-${index}`}>
<a
href={resolvedLink.url}
target={link.targetBlank ? '_blank' : '_self'}
aria-label={selectors.components.DashboardLinks.link}
>
{resolvedLink.title}
</a>
</li>
);
})}
</ul>
</>
</LinkElement>
); );
}; }
renderList = () => {
const { link } = this.props;
const { resolvedLinks } = this.state;
return ( return (
<> <>
{resolvedLinks.length > 0 && {resolvedLinks.length > 0 &&
resolvedLinks.map((resolvedLink, index) => { resolvedLinks.map((resolvedLink, index) => {
const linkElement = ( return (
<LinkElement
link={link}
key={`dashlinks-list-item-${resolvedLink.id}-${index}`}
aria-label={selectors.components.DashboardLinks.container}
>
<a <a
className="gf-form-label gf-form-label--dashlink" className="gf-form-label gf-form-label--dashlink"
href={resolvedLink.url} href={resolvedLink.url}
...@@ -69,66 +73,39 @@ export class DashboardLinksDashboard extends PureComponent<Props, State> { ...@@ -69,66 +73,39 @@ export class DashboardLinksDashboard extends PureComponent<Props, State> {
<Icon name="apps" style={{ marginRight: '4px' }} /> <Icon name="apps" style={{ marginRight: '4px' }} />
<span>{resolvedLink.title}</span> <span>{resolvedLink.title}</span>
</a> </a>
); </LinkElement>
return this.renderElement( );
linkElement, })}
`dashlinks-list-item-${resolvedLink.id}-${index}`, </>
selectors.components.DashboardLinks.container );
); };
})}
</> interface LinkElementProps {
); link: DashboardLink;
}; 'aria-label': string;
key: string;
renderDropdown() { children: JSX.Element;
const { link, linkInfo } = this.props; }
const { resolvedLinks } = this.state;
const linkElement = (
<>
<a
className="gf-form-label gf-form-label--dashlink"
onClick={this.searchForDashboards}
data-placement="bottom"
data-toggle="dropdown"
>
<Icon name="bars" style={{ marginRight: '4px' }} />
<span>{linkInfo.title}</span>
</a>
<ul
className={`dropdown-menu ${getDropdownLocationCssClass(this.listItemRef.current)}`}
role="menu"
ref={this.listItemRef}
>
{resolvedLinks.length > 0 &&
resolvedLinks.map((resolvedLink, index) => {
return (
<li key={`dashlinks-dropdown-item-${resolvedLink.id}-${index}`}>
<a
href={resolvedLink.url}
target={link.targetBlank ? '_blank' : '_self'}
aria-label={selectors.components.DashboardLinks.link}
>
{resolvedLink.title}
</a>
</li>
);
})}
</ul>
</>
);
return this.renderElement(linkElement, 'dashlinks-dropdown', selectors.components.DashboardLinks.dropDown);
}
render() {
if (this.props.link.asDropdown) {
return this.renderDropdown();
}
return this.renderList(); const LinkElement: React.FC<LinkElementProps> = props => {
const { link, children, ...rest } = props;
return (
<div {...rest} className="gf-form">
{link.tooltip && <Tooltip content={link.tooltip}>{children}</Tooltip>}
{!link.tooltip && <>{children}</>}
</div>
);
};
const useResolvedLinks = ({ link, dashboardId }: Props, opened: number): ResolvedLinkDTO[] => {
const { tags } = link;
const result = useAsync(() => searchForTags(tags), [tags, opened]);
if (!result.value) {
return [];
} }
} return resolveLinks(dashboardId, link, result.value);
};
interface ResolvedLinkDTO { interface ResolvedLinkDTO {
id: any; id: any;
...@@ -137,11 +114,11 @@ interface ResolvedLinkDTO { ...@@ -137,11 +114,11 @@ interface ResolvedLinkDTO {
} }
export async function searchForTags( export async function searchForTags(
link: DashboardLink, tags: any[],
dependencies: { getBackendSrv: typeof getBackendSrv } = { getBackendSrv } dependencies: { getBackendSrv: typeof getBackendSrv } = { getBackendSrv }
): Promise<DashboardSearchHit[]> { ): Promise<DashboardSearchHit[]> {
const limit = 100; const limit = 100;
const searchHits: DashboardSearchHit[] = await dependencies.getBackendSrv().search({ tag: link.tags, limit }); const searchHits: DashboardSearchHit[] = await dependencies.getBackendSrv().search({ tag: tags, limit });
return searchHits; return searchHits;
} }
......
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