diff --git a/packages/grafana-data/src/field/fieldOverrides.ts b/packages/grafana-data/src/field/fieldOverrides.ts index 0924a417231..7e9aa1052aa 100644 --- a/packages/grafana-data/src/field/fieldOverrides.ts +++ b/packages/grafana-data/src/field/fieldOverrides.ts @@ -501,6 +501,7 @@ export const getLinksSupplier = }); }, origin: field, + oneClick: link.oneClick ?? false, }; } else { linkModel = { @@ -508,6 +509,7 @@ export const getLinksSupplier = title: replaceVariables(link.title || '', dataLinkScopedVars), target: link.targetBlank ? '_blank' : undefined, origin: field, + oneClick: link.oneClick ?? false, }; } diff --git a/packages/grafana-data/src/types/dataLink.ts b/packages/grafana-data/src/types/dataLink.ts index 476f2441ed7..14ee3ad4301 100644 --- a/packages/grafana-data/src/types/dataLink.ts +++ b/packages/grafana-data/src/types/dataLink.ts @@ -55,6 +55,8 @@ export interface DataLink { correlationData?: ExploreCorrelationHelperData; transformations?: DataLinkTransformationConfig[]; }; + + oneClick?: boolean; } /** @@ -98,6 +100,7 @@ export interface LinkModel { // When a click callback exists, this is passed the raw mouse|react event onClick?: (e: any, origin?: any) => void; + oneClick?: boolean; } /** diff --git a/packages/grafana-ui/src/components/DataLinks/DataLinkEditor.tsx b/packages/grafana-ui/src/components/DataLinks/DataLinkEditor.tsx index 98f5d67227f..cdd5e64eddf 100644 --- a/packages/grafana-ui/src/components/DataLinks/DataLinkEditor.tsx +++ b/packages/grafana-ui/src/components/DataLinks/DataLinkEditor.tsx @@ -5,7 +5,7 @@ import { VariableSuggestion, GrafanaTheme2, DataLink } from '@grafana/data'; import { useStyles2 } from '../../themes/index'; import { isCompactUrl } from '../../utils/dataLinks'; -import { Trans } from '../../utils/i18n'; +import { t, Trans } from '../../utils/i18n'; import { Field } from '../Forms/Field'; import { Input } from '../Input/Input'; import { Switch } from '../Switch/Switch'; @@ -45,6 +45,10 @@ export const DataLinkEditor = memo(({ index, value, onChange, suggestions, isLas onChange(index, { ...value, targetBlank: !value.targetBlank }); }; + const onOneClickChanged = () => { + onChange(index, { ...value, oneClick: !value.oneClick }); + }; + return (
@@ -63,6 +67,16 @@ export const DataLinkEditor = memo(({ index, value, onChange, suggestions, isLas + + + + {isLast && (
diff --git a/packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksInlineEditor.tsx b/packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksInlineEditor.tsx index a431e241479..fa4e61589f3 100644 --- a/packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksInlineEditor.tsx +++ b/packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksInlineEditor.tsx @@ -51,6 +51,15 @@ export const DataLinksInlineEditor = ({ setIsNew(false); } } + + if (link.oneClick === true) { + linksSafe.forEach((link) => { + if (link.oneClick) { + link.oneClick = false; + } + }); + } + const update = cloneDeep(linksSafe); update[index] = link; onChange(update); diff --git a/packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksListItem.tsx b/packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksListItem.tsx index 939a4ec15b7..1c12a41dd2a 100644 --- a/packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksListItem.tsx +++ b/packages/grafana-ui/src/components/DataLinks/DataLinksInlineEditor/DataLinksListItem.tsx @@ -5,6 +5,8 @@ import { DataFrame, DataLink, GrafanaTheme2 } from '@grafana/data'; import { useStyles2 } from '../../../themes'; import { isCompactUrl } from '../../../utils'; +import { t } from '../../../utils/i18n'; +import { Badge } from '../../Badge/Badge'; import { Icon } from '../../Icon/Icon'; import { IconButton } from '../../IconButton/IconButton'; import { Tooltip } from '../../Tooltip/Tooltip'; @@ -22,7 +24,7 @@ export interface DataLinksListItemProps { export const DataLinksListItem = ({ link, onEdit, onRemove, index, itemKey }: DataLinksListItemProps) => { const styles = useStyles2(getDataLinkListItemStyles); - const { title = '', url = '' } = link; + const { title = '', url = '', oneClick = false } = link; const hasTitle = title.trim() !== ''; const hasUrl = url.trim() !== ''; @@ -52,6 +54,13 @@ export const DataLinksListItem = ({ link, onEdit, onRemove, index, itemKey }: Da
+ {oneClick && ( + + )}
diff --git a/packages/grafana-ui/src/components/VizTooltip/VizTooltipFooter.tsx b/packages/grafana-ui/src/components/VizTooltip/VizTooltipFooter.tsx index 05b05f7518d..c3c4a34eb64 100644 --- a/packages/grafana-ui/src/components/VizTooltip/VizTooltipFooter.tsx +++ b/packages/grafana-ui/src/components/VizTooltip/VizTooltipFooter.tsx @@ -2,7 +2,7 @@ import { css } from '@emotion/css'; import { ActionModel, Field, GrafanaTheme2, LinkModel } from '@grafana/data'; -import { Button, DataLinkButton, Stack } from '..'; +import { Button, DataLinkButton, Icon, Stack } from '..'; import { useStyles2 } from '../../themes'; import { Trans } from '../../utils/i18n'; import { ActionButton } from '../Actions/ActionButton'; @@ -16,6 +16,21 @@ interface VizTooltipFooterProps { export const ADD_ANNOTATION_ID = 'add-annotation-button'; const renderDataLinks = (dataLinks: LinkModel[], styles: ReturnType) => { + const oneClickLink = dataLinks.find((link) => link.oneClick === true); + + if (oneClickLink != null) { + return ( + + + + + Click to open {{ linkTitle: oneClickLink.title }} + + + + ); + } + return ( {dataLinks.map((link, i) => ( @@ -35,14 +50,15 @@ const renderActions = (actions: ActionModel[]) => { ); }; -export const VizTooltipFooter = ({ dataLinks, actions, annotate }: VizTooltipFooterProps) => { +export const VizTooltipFooter = ({ dataLinks, actions = [], annotate }: VizTooltipFooterProps) => { const styles = useStyles2(getStyles); + const hasOneClickLink = dataLinks.some((link) => link.oneClick === true); return (
- {dataLinks?.length > 0 &&
{renderDataLinks(dataLinks, styles)}
} - {actions && actions.length > 0 &&
{renderActions(actions)}
} - {annotate != null && ( + {dataLinks.length > 0 &&
{renderDataLinks(dataLinks, styles)}
} + {!hasOneClickLink && actions.length > 0 &&
{renderActions(actions)}
} + {!hasOneClickLink && annotate != null && (