DashboardScene: Panel links support (#77295)

* DashboardScene: PanelLinks support

* Update public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx

* Remove code

* Use updated API

* Bump scenes version
This commit is contained in:
Dominik Prokop
2023-10-31 09:57:38 +01:00
committed by GitHub
parent 7ba3b52e65
commit bbe1767310
7 changed files with 110 additions and 11 deletions

View File

@@ -0,0 +1,48 @@
import React from 'react';
import { LinkModel } from '@grafana/data';
import { SceneComponentProps, SceneObjectBase, SceneObjectState } from '@grafana/scenes';
import { Dropdown, Menu, ToolbarButton } from '@grafana/ui';
interface VizPanelLinksState extends SceneObjectState {
links?: LinkModel[];
menu: VizPanelLinksMenu;
}
export class VizPanelLinks extends SceneObjectBase<VizPanelLinksState> {
static Component = VizPanelLinksRenderer;
}
function VizPanelLinksRenderer({ model }: SceneComponentProps<VizPanelLinks>) {
const { menu } = model.useState();
return (
<Dropdown
overlay={() => {
return <menu.Component model={menu} key={menu.state.key} />;
}}
>
<ToolbarButton icon="external-link-alt" iconSize="md" aria-label="panel links" />
</Dropdown>
);
}
export class VizPanelLinksMenu extends SceneObjectBase<Omit<VizPanelLinksState, 'menu'>> {
static Component = VizPanelLinksMenuRenderer;
}
function VizPanelLinksMenuRenderer({ model }: SceneComponentProps<VizPanelLinks>) {
const { links } = model.useState();
if (!links) {
return null;
}
return (
<Menu>
{links?.map((link, idx) => {
return <Menu.Item key={idx} label={link.title} url={link.href} target={link.target} onClick={link.onClick} />;
})}
</Menu>
);
}

View File

@@ -1,14 +1,17 @@
import { PanelMenuItem } from '@grafana/data';
import { InterpolateFunction, PanelMenuItem } from '@grafana/data';
import { locationService, reportInteraction } from '@grafana/runtime';
import { VizPanel, VizPanelMenu } from '@grafana/scenes';
import { sceneGraph, VizPanel, VizPanelMenu } from '@grafana/scenes';
import { t } from 'app/core/internationalization';
import { PanelModel } from 'app/features/dashboard/state';
import { InspectTab } from 'app/features/inspector/types';
import { getPanelLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers';
import { ShareModal } from '../sharing/ShareModal';
import { getDashboardUrl, getInspectUrl, getViewPanelUrl, tryGetExploreUrlForPanel } from '../utils/urlBuilders';
import { getPanelIdForVizPanel } from '../utils/utils';
import { DashboardScene } from './DashboardScene';
import { VizPanelLinks } from './PanelLinks';
/**
* Behavior is called when VizPanelMenu is activated (ie when it's opened).
@@ -81,3 +84,31 @@ export function panelMenuBehavior(menu: VizPanelMenu) {
asyncFunc();
}
/**
* Behavior is called when VizPanelLinksMenu is activated (when it's opened).
*/
export function getPanelLinksBehavior(panel: PanelModel) {
return (panelLinksMenu: VizPanelLinks) => {
const interpolate: InterpolateFunction = (v, scopedVars) => {
return sceneGraph.interpolate(panelLinksMenu, v, scopedVars);
};
const linkSupplier = getPanelLinksSupplier(panel, interpolate);
if (!linkSupplier) {
return;
}
const panelLinks = linkSupplier && linkSupplier.getLinks(interpolate);
const links = panelLinks.map((panelLink) => ({
...panelLink,
onClick: (e: any, origin: any) => {
reportInteraction('dashboards_panelheader_datalink_clicked', { has_multiple_links: panelLinks.length > 1 });
panelLink.onClick?.(e, origin);
},
}));
panelLinksMenu.setState({ links });
};
}

View File

@@ -40,7 +40,8 @@ import { DashboardDTO } from 'app/types';
import { DashboardAnnotationsDataLayer } from '../scene/DashboardAnnotationsDataLayer';
import { DashboardScene } from '../scene/DashboardScene';
import { LibraryVizPanel } from '../scene/LibraryVizPanel';
import { panelMenuBehavior } from '../scene/PanelMenuBehavior';
import { VizPanelLinks, VizPanelLinksMenu } from '../scene/PanelLinks';
import { getPanelLinksBehavior, panelMenuBehavior } from '../scene/PanelMenuBehavior';
import { PanelRepeaterGridItem } from '../scene/PanelRepeaterGridItem';
import { PanelTimeRange } from '../scene/PanelTimeRange';
import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior';
@@ -346,9 +347,19 @@ export function buildGridItemForLibPanel(panel: PanelModel) {
}
export function buildGridItemForPanel(panel: PanelModel): SceneGridItemLike {
const hasPanelLinks = panel.links && panel.links.length > 0;
let panelLinks;
if (hasPanelLinks) {
panelLinks = new VizPanelLinks({
menu: new VizPanelLinksMenu({ $behaviors: [getPanelLinksBehavior(panel)] }),
});
}
const vizPanelState: VizPanelState = {
key: getVizPanelKeyForPanelId(panel.id),
title: panel.title,
description: panel.description,
pluginId: panel.type,
options: panel.options ?? {},
fieldConfig: panel.fieldConfig,
@@ -360,6 +371,8 @@ export function buildGridItemForPanel(panel: PanelModel): SceneGridItemLike {
menu: new VizPanelMenu({
$behaviors: [panelMenuBehavior],
}),
titleItems: panelLinks,
extendPanelContext: setDashboardPanelContext,
_UNSAFE_customMigrationHandler: getAngularPanelMigrationHandler(panel),
};

View File

@@ -139,7 +139,10 @@ export const getFieldLinksSupplier = (value: FieldDisplay): LinkModelSupplier<Fi
};
};
export const getPanelLinksSupplier = (panel: PanelModel): LinkModelSupplier<PanelModel> | undefined => {
export const getPanelLinksSupplier = (
panel: PanelModel,
replaceVariables?: InterpolateFunction
): LinkModelSupplier<PanelModel> | undefined => {
const links = panel.links;
if (!links || links.length === 0) {
@@ -149,7 +152,7 @@ export const getPanelLinksSupplier = (panel: PanelModel): LinkModelSupplier<Pane
return {
getLinks: () => {
return links.map((link) => {
return getLinkSrv().getDataLinkUIModel(link, panel.replaceVariables, panel);
return getLinkSrv().getDataLinkUIModel(link, replaceVariables || panel.replaceVariables, panel);
});
},
};