mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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.
This commit is contained in:
@@ -59,8 +59,7 @@ e2e.scenario({
|
||||
e2e.components.DashboardLinks.dropDown()
|
||||
.should('be.visible')
|
||||
.click()
|
||||
.wait('@tagsTemplatingSearch')
|
||||
.wait('@tagsDemoSearch');
|
||||
.wait('@tagsTemplatingSearch');
|
||||
|
||||
// verify all links, should have p2 value
|
||||
verifyLinks('p2');
|
||||
|
||||
@@ -27,7 +27,7 @@ describe('searchForTags', () => {
|
||||
it('then tags from link should be used in search and limit should be 100', async () => {
|
||||
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(backendSrv.search).toHaveBeenCalledWith({ tag: ['A', 'B'], limit: 100 });
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { createRef, PureComponent } from 'react';
|
||||
import React, { useRef, useState } from 'react';
|
||||
import { Icon, Tooltip } from '@grafana/ui';
|
||||
import { sanitize, sanitizeUrl } from '@grafana/data/src/text/sanitize';
|
||||
import { getBackendSrv } from 'app/core/services/backend_srv';
|
||||
@@ -6,6 +6,7 @@ import { getLinkSrv } from '../../../panel/panellinks/link_srv';
|
||||
import { DashboardLink } from '../../state/DashboardModel';
|
||||
import { DashboardSearchHit } from 'app/features/search/types';
|
||||
import { selectors } from '@grafana/e2e-selectors';
|
||||
import { useAsync } from 'react-use';
|
||||
|
||||
interface Props {
|
||||
link: DashboardLink;
|
||||
@@ -13,53 +14,56 @@ interface Props {
|
||||
dashboardId: any;
|
||||
}
|
||||
|
||||
interface State {
|
||||
resolvedLinks: ResolvedLinkDTO[];
|
||||
}
|
||||
|
||||
export class DashboardLinksDashboard extends PureComponent<Props, State> {
|
||||
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;
|
||||
export const DashboardLinksDashboard: React.FC<Props> = props => {
|
||||
const { link, linkInfo } = props;
|
||||
const listRef = useRef<HTMLUListElement>(null);
|
||||
const [opened, setOpened] = useState(0);
|
||||
const resolvedLinks = useResolvedLinks(props, opened);
|
||||
|
||||
if (link.asDropdown) {
|
||||
return (
|
||||
<div className="gf-form" key={key} aria-label={selector}>
|
||||
{link.tooltip && <Tooltip content={link.tooltip}>{linkElement}</Tooltip>}
|
||||
{!link.tooltip && <>{linkElement}</>}
|
||||
</div>
|
||||
<LinkElement link={link} key="dashlinks-dropdown" aria-label={selectors.components.DashboardLinks.dropDown}>
|
||||
<>
|
||||
<a
|
||||
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 (
|
||||
<>
|
||||
{resolvedLinks.length > 0 &&
|
||||
resolvedLinks.map((resolvedLink, index) => {
|
||||
const linkElement = (
|
||||
return (
|
||||
<>
|
||||
{resolvedLinks.length > 0 &&
|
||||
resolvedLinks.map((resolvedLink, index) => {
|
||||
return (
|
||||
<LinkElement
|
||||
link={link}
|
||||
key={`dashlinks-list-item-${resolvedLink.id}-${index}`}
|
||||
aria-label={selectors.components.DashboardLinks.container}
|
||||
>
|
||||
<a
|
||||
className="gf-form-label gf-form-label--dashlink"
|
||||
href={resolvedLink.url}
|
||||
@@ -69,67 +73,40 @@ export class DashboardLinksDashboard extends PureComponent<Props, State> {
|
||||
<Icon name="apps" style={{ marginRight: '4px' }} />
|
||||
<span>{resolvedLink.title}</span>
|
||||
</a>
|
||||
);
|
||||
return this.renderElement(
|
||||
linkElement,
|
||||
`dashlinks-list-item-${resolvedLink.id}-${index}`,
|
||||
selectors.components.DashboardLinks.container
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
</LinkElement>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
renderDropdown() {
|
||||
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();
|
||||
}
|
||||
interface LinkElementProps {
|
||||
link: DashboardLink;
|
||||
'aria-label': string;
|
||||
key: string;
|
||||
children: JSX.Element;
|
||||
}
|
||||
|
||||
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 {
|
||||
id: any;
|
||||
url: string;
|
||||
@@ -137,11 +114,11 @@ interface ResolvedLinkDTO {
|
||||
}
|
||||
|
||||
export async function searchForTags(
|
||||
link: DashboardLink,
|
||||
tags: any[],
|
||||
dependencies: { getBackendSrv: typeof getBackendSrv } = { getBackendSrv }
|
||||
): Promise<DashboardSearchHit[]> {
|
||||
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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user