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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 110 additions and 11 deletions

View File

@ -2885,6 +2885,10 @@ exports[`better eslint`] = {
"public/app/features/dashboard-scene/inspect/InspectJsonTab.tsx:5381": [
[0, 0, 0, "Use data-testid for E2E selectors instead of aria-label", "0"]
],
"public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/features/dashboard-scene/scene/setDashboardPanelContext.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],

View File

@ -257,7 +257,7 @@
"@grafana/lezer-traceql": "0.0.8",
"@grafana/monaco-logql": "^0.0.7",
"@grafana/runtime": "workspace:*",
"@grafana/scenes": "^1.18.0",
"@grafana/scenes": "^1.20.1",
"@grafana/schema": "workspace:*",
"@grafana/ui": "workspace:*",
"@kusto/monaco-kusto": "^7.4.0",

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);
});
},
};

View File

@ -3304,9 +3304,9 @@ __metadata:
languageName: unknown
linkType: soft
"@grafana/scenes@npm:^1.18.0":
version: 1.20.0
resolution: "@grafana/scenes@npm:1.20.0"
"@grafana/scenes@npm:^1.20.1":
version: 1.20.1
resolution: "@grafana/scenes@npm:1.20.1"
dependencies:
"@grafana/e2e-selectors": 10.0.2
react-grid-layout: 1.3.4
@ -3318,7 +3318,7 @@ __metadata:
"@grafana/runtime": 10.0.3
"@grafana/schema": 10.0.3
"@grafana/ui": 10.0.3
checksum: cc56fa2aec1e31598a9cbb7f4837d25fc03f32879247fc2063269c1d9c8d8543c518f6cac3c4b64ea32b4f0841e334bd1af4bd38b45860c0a89473680385cafd
checksum: 44f81303cc096885afb1bce32c3ce80739091c961dfd5a21268deb90cbbc2af2b8b5647357a833f1db4106eb4618b43e403e0fe127157ed1a302a68f89a59907
languageName: node
linkType: hard
@ -17135,7 +17135,7 @@ __metadata:
"@grafana/lezer-traceql": 0.0.8
"@grafana/monaco-logql": ^0.0.7
"@grafana/runtime": "workspace:*"
"@grafana/scenes": ^1.18.0
"@grafana/scenes": ^1.20.1
"@grafana/schema": "workspace:*"
"@grafana/tsconfig": ^1.3.0-rc1
"@grafana/ui": "workspace:*"