From bbe17673103205d5386b99db2a56a02ce230889f Mon Sep 17 00:00:00 2001 From: Dominik Prokop Date: Tue, 31 Oct 2023 09:57:38 +0100 Subject: [PATCH] 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 --- .betterer.results | 4 ++ package.json | 2 +- .../dashboard-scene/scene/PanelLinks.tsx | 48 +++++++++++++++++++ .../scene/PanelMenuBehavior.tsx | 35 +++++++++++++- .../transformSaveModelToScene.ts | 15 +++++- .../panel/panellinks/linkSuppliers.ts | 7 ++- yarn.lock | 10 ++-- 7 files changed, 110 insertions(+), 11 deletions(-) create mode 100644 public/app/features/dashboard-scene/scene/PanelLinks.tsx diff --git a/.betterer.results b/.betterer.results index 49ffde3a4fc..655d91af0bf 100644 --- a/.betterer.results +++ b/.betterer.results @@ -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"] ], diff --git a/package.json b/package.json index 6f11a1e678e..2e03d5fb567 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/public/app/features/dashboard-scene/scene/PanelLinks.tsx b/public/app/features/dashboard-scene/scene/PanelLinks.tsx new file mode 100644 index 00000000000..2a0bcd7501e --- /dev/null +++ b/public/app/features/dashboard-scene/scene/PanelLinks.tsx @@ -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 { + static Component = VizPanelLinksRenderer; +} + +function VizPanelLinksRenderer({ model }: SceneComponentProps) { + const { menu } = model.useState(); + + return ( + { + return ; + }} + > + + + ); +} + +export class VizPanelLinksMenu extends SceneObjectBase> { + static Component = VizPanelLinksMenuRenderer; +} + +function VizPanelLinksMenuRenderer({ model }: SceneComponentProps) { + const { links } = model.useState(); + + if (!links) { + return null; + } + + return ( + + {links?.map((link, idx) => { + return ; + })} + + ); +} diff --git a/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx b/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx index f377ab996c7..343c5030447 100644 --- a/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx +++ b/public/app/features/dashboard-scene/scene/PanelMenuBehavior.tsx @@ -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 }); + }; +} diff --git a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts index cd57d23a136..7057b410a2f 100644 --- a/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts +++ b/public/app/features/dashboard-scene/serialization/transformSaveModelToScene.ts @@ -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), }; diff --git a/public/app/features/panel/panellinks/linkSuppliers.ts b/public/app/features/panel/panellinks/linkSuppliers.ts index ddadc88d044..0fa5cf47fb0 100644 --- a/public/app/features/panel/panellinks/linkSuppliers.ts +++ b/public/app/features/panel/panellinks/linkSuppliers.ts @@ -139,7 +139,10 @@ export const getFieldLinksSupplier = (value: FieldDisplay): LinkModelSupplier | undefined => { +export const getPanelLinksSupplier = ( + panel: PanelModel, + replaceVariables?: InterpolateFunction +): LinkModelSupplier | undefined => { const links = panel.links; if (!links || links.length === 0) { @@ -149,7 +152,7 @@ export const getPanelLinksSupplier = (panel: PanelModel): LinkModelSupplier { return links.map((link) => { - return getLinkSrv().getDataLinkUIModel(link, panel.replaceVariables, panel); + return getLinkSrv().getDataLinkUIModel(link, replaceVariables || panel.replaceVariables, panel); }); }, }; diff --git a/yarn.lock b/yarn.lock index ab269388891..9e8a7b038af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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:*"