import { css, cx } from '@emotion/css'; import { forwardRef } from 'react'; import { useAsync } from 'react-use'; import { GrafanaTheme2, ScopedVars } from '@grafana/data'; import { sanitize, sanitizeUrl } from '@grafana/data/src/text/sanitize'; import { selectors } from '@grafana/e2e-selectors'; import { DashboardLink } from '@grafana/schema'; import { Dropdown, Icon, Button, Menu, ScrollContainer, useStyles2 } from '@grafana/ui'; import { ButtonLinkProps, LinkButton } from '@grafana/ui/src/components/Button'; import { getBackendSrv } from 'app/core/services/backend_srv'; import { DashboardSearchItem } from 'app/features/search/types'; import { getLinkSrv } from '../../../panel/panellinks/link_srv'; interface Props { link: DashboardLink; linkInfo: { title: string; href: string }; dashboardUID: string; scopedVars?: ScopedVars; } interface DashboardLinksMenuProps { link: DashboardLink; dashboardUID: string; } function DashboardLinksMenu({ dashboardUID, link }: DashboardLinksMenuProps) { const styles = useStyles2(getStyles); const resolvedLinks = useResolvedLinks({ dashboardUID, link }); if (!resolvedLinks || resolveLinks.length === 0) { return null; } return (
{resolvedLinks.map((resolvedLink, index) => { return ( ); })}
); } export const DashboardLinksDashboard = (props: Props) => { const { link, linkInfo, dashboardUID } = props; const resolvedLinks = useResolvedLinks(props); const styles = useStyles2(getStyles); if (link.asDropdown) { return ( }> {linkInfo.title} ); } return ( <> {resolvedLinks.length > 0 && resolvedLinks.map((resolvedLink, index) => { return ( {resolvedLink.title} ); })} ); }; const useResolvedLinks = ({ link, dashboardUID }: Pick): ResolvedLinkDTO[] => { const { tags } = link; const result = useAsync(() => searchForTags(tags), [tags]); if (!result.value) { return []; } return resolveLinks(dashboardUID, link, result.value); }; interface ResolvedLinkDTO { uid: string; url: string; title: string; } export async function searchForTags( tags: string[], dependencies: { getBackendSrv: typeof getBackendSrv } = { getBackendSrv } ): Promise { const limit = 100; const searchHits: DashboardSearchItem[] = await dependencies.getBackendSrv().search({ tag: tags, limit }); return searchHits; } export function resolveLinks( dashboardUID: string, link: DashboardLink, searchHits: DashboardSearchItem[], dependencies: { getLinkSrv: typeof getLinkSrv; sanitize: typeof sanitize; sanitizeUrl: typeof sanitizeUrl } = { getLinkSrv, sanitize, sanitizeUrl, } ): ResolvedLinkDTO[] { return searchHits .filter((searchHit) => searchHit.uid !== dashboardUID) .map((searchHit) => { const uid = searchHit.uid; const title = dependencies.sanitize(searchHit.title); const resolvedLink = dependencies.getLinkSrv().getLinkUrl({ ...link, url: searchHit.url }); const url = dependencies.sanitizeUrl(resolvedLink); return { uid, title, url }; }); } function getStyles(theme: GrafanaTheme2) { return { iconMargin: css({ marginRight: theme.spacing(0.5), }), dropdown: css({ maxWidth: 'max(30vw, 300px)', maxHeight: '70vh', }), button: css({ color: theme.colors.text.primary, }), dashButton: css({ fontSize: theme.typography.bodySmall.fontSize, paddingLeft: theme.spacing(1), paddingRight: theme.spacing(1), }), }; } export const DashboardLinkButton = forwardRef(({ className, ...otherProps }, ref) => { const styles = useStyles2(getStyles); const Component = otherProps.href ? LinkButton : Button; return ( ); }); DashboardLinkButton.displayName = 'DashboardLinkButton';