mirror of
https://github.com/grafana/grafana.git
synced 2024-12-01 21:19:28 -06:00
DataLinks: Implement javascript callback (#19851)
This commit is contained in:
parent
040801256f
commit
9e004b9211
@ -1,11 +1,32 @@
|
||||
import { ScopedVars } from './ScopedVars';
|
||||
|
||||
/**
|
||||
* Callback info for DataLink click events
|
||||
*/
|
||||
export interface DataLinkClickEvent<T = any> {
|
||||
origin: T;
|
||||
scopedVars: ScopedVars;
|
||||
e?: any; // mouse|react event
|
||||
}
|
||||
|
||||
/**
|
||||
* Link configuration. The values may contain variables that need to be
|
||||
* processed before running
|
||||
*/
|
||||
export interface DataLink {
|
||||
url: string;
|
||||
title: string;
|
||||
targetBlank?: boolean;
|
||||
|
||||
// 3: The URL if others did not set it first
|
||||
url: string;
|
||||
|
||||
// 2: If exists, use this to construct the URL
|
||||
// Not saved in JSON/DTO
|
||||
onBuildUrl?: (event: DataLinkClickEvent) => string;
|
||||
|
||||
// 1: If exists, handle click directly
|
||||
// Not saved in JSON/DTO
|
||||
onClick?: (event: DataLinkClickEvent) => void;
|
||||
}
|
||||
|
||||
export type LinkTarget = '_blank' | '_self';
|
||||
@ -18,6 +39,9 @@ export interface LinkModel<T> {
|
||||
title: string;
|
||||
target: LinkTarget;
|
||||
origin: T;
|
||||
|
||||
// When a click callback exists, this is passed the raw mouse|react event
|
||||
onClick?: (e: any) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -189,7 +189,7 @@ export const ContextMenu: React.FC<ContextMenuProps> = React.memo(({ x, y, onClo
|
||||
renderItem={(item, index) => {
|
||||
return (
|
||||
<>
|
||||
<ContextMenuGroup group={item} onItemClick={onClose} />
|
||||
<ContextMenuGroup group={item} onClick={onClose} />
|
||||
</>
|
||||
);
|
||||
}}
|
||||
@ -215,7 +215,7 @@ const ContextMenuItem: React.FC<ContextMenuItemProps> = React.memo(
|
||||
return (
|
||||
<div className={styles.item}>
|
||||
<a
|
||||
href={url}
|
||||
href={url ? url : undefined}
|
||||
target={target || '_self'}
|
||||
className={cx(className, styles.link)}
|
||||
onClick={e => {
|
||||
@ -233,10 +233,10 @@ const ContextMenuItem: React.FC<ContextMenuItemProps> = React.memo(
|
||||
|
||||
interface ContextMenuGroupProps {
|
||||
group: ContextMenuGroup;
|
||||
onItemClick?: () => void;
|
||||
onClick?: () => void; // Used with 'onClose'
|
||||
}
|
||||
|
||||
const ContextMenuGroup: React.FC<ContextMenuGroupProps> = ({ group, onItemClick }) => {
|
||||
const ContextMenuGroup: React.FC<ContextMenuGroupProps> = ({ group, onClick }) => {
|
||||
const theme = useContext(ThemeContext);
|
||||
const styles = getContextMenuStyles(theme);
|
||||
|
||||
@ -261,8 +261,9 @@ const ContextMenuGroup: React.FC<ContextMenuGroupProps> = ({ group, onItemClick
|
||||
item.onClick(e);
|
||||
}
|
||||
|
||||
if (onItemClick) {
|
||||
onItemClick();
|
||||
// Typically closes the context menu
|
||||
if (onClick) {
|
||||
onClick();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
@ -27,6 +27,7 @@ export const linkModelToContextMenuItems: (links: LinkModelSupplier<any>) => Con
|
||||
url: link.href,
|
||||
target: link.target,
|
||||
icon: `fa ${link.target === '_self' ? 'fa-link' : 'fa-external-link'}`,
|
||||
onClick: link.onClick,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
@ -156,11 +156,31 @@ export class LinkSrv implements LinkService {
|
||||
const params: KeyValue = {};
|
||||
const timeRangeUrl = toUrlParams(this.timeSrv.timeRangeForUrl());
|
||||
|
||||
let href = link.url;
|
||||
if (link.onBuildUrl) {
|
||||
href = link.onBuildUrl({
|
||||
origin,
|
||||
scopedVars,
|
||||
});
|
||||
}
|
||||
|
||||
let onClick: (e: any) => void = undefined;
|
||||
if (link.onClick) {
|
||||
onClick = (e: any) => {
|
||||
link.onClick({
|
||||
origin,
|
||||
scopedVars,
|
||||
e,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
const info: LinkModel<T> = {
|
||||
href: link.url.replace(/\s|\n/g, ''),
|
||||
href: href.replace(/\s|\n/g, ''),
|
||||
title: this.templateSrv.replace(link.title || '', scopedVars),
|
||||
target: link.targetBlank ? '_blank' : '_self',
|
||||
origin,
|
||||
onClick,
|
||||
};
|
||||
this.templateSrv.fillVariableValuesForUrl(params, scopedVars);
|
||||
|
||||
|
@ -214,6 +214,7 @@ class GraphElement {
|
||||
url: link.href,
|
||||
target: link.target,
|
||||
icon: `fa ${link.target === '_self' ? 'fa-link' : 'fa-external-link'}`,
|
||||
onClick: link.onClick,
|
||||
};
|
||||
}),
|
||||
},
|
||||
@ -248,19 +249,23 @@ class GraphElement {
|
||||
if (item) {
|
||||
// pickup y-axis index to know which field's config to apply
|
||||
const yAxisConfig = this.panel.yaxes[item.series.yaxis.n === 2 ? 1 : 0];
|
||||
const fieldConfig = {
|
||||
decimals: yAxisConfig.decimals,
|
||||
links: this.panel.options.dataLinks || [],
|
||||
};
|
||||
const dataFrame = this.ctrl.dataList[item.series.dataFrameIndex];
|
||||
const field = dataFrame.fields[item.series.fieldIndex];
|
||||
|
||||
let links = this.panel.options.dataLinks || [];
|
||||
if (field.config.links && field.config.links.length) {
|
||||
// Append the configured links to the panel datalinks
|
||||
links = [...links, ...field.config.links];
|
||||
}
|
||||
const fieldConfig = {
|
||||
decimals: yAxisConfig.decimals,
|
||||
links,
|
||||
};
|
||||
const fieldDisplay = getDisplayProcessor({
|
||||
config: fieldConfig,
|
||||
theme: getCurrentTheme(),
|
||||
})(field.values.get(item.dataIndex));
|
||||
|
||||
linksSupplier = this.panel.options.dataLinks
|
||||
linksSupplier = links.length
|
||||
? getFieldLinksSupplier({
|
||||
display: fieldDisplay,
|
||||
name: field.name,
|
||||
|
Loading…
Reference in New Issue
Block a user