DashboardScene: Allow editing panel links (#81560)

* Panel links supplier for VizPanel

* Update panel links behavior

* Allow editing panel links

* Update so that single link is rendered without a dropdown

* Serialise links in scene -> save model transformation

* Betterer fix

* Fix inspect json tab test
This commit is contained in:
Dominik Prokop 2024-01-31 03:30:27 -08:00 committed by GitHub
parent ab891d92fb
commit f20053e4b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 208 additions and 61 deletions

View File

@ -2442,7 +2442,8 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "2"], [0, 0, 0, "Do not use any type assertions.", "2"],
[0, 0, 0, "Do not use any type assertions.", "3"], [0, 0, 0, "Do not use any type assertions.", "3"],
[0, 0, 0, "Do not use any type assertions.", "4"], [0, 0, 0, "Do not use any type assertions.", "4"],
[0, 0, 0, "Do not use any type assertions.", "5"] [0, 0, 0, "Do not use any type assertions.", "5"],
[0, 0, 0, "Do not use any type assertions.", "6"]
], ],
"public/app/features/dashboard-scene/settings/variables/components/VariableSelectField.tsx:5381": [ "public/app/features/dashboard-scene/settings/variables/components/VariableSelectField.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"] [0, 0, 0, "Unexpected any. Specify a different type.", "0"]

View File

@ -12,6 +12,7 @@ import {
import { getStandardTransformers } from 'app/features/transformers/standardTransformers'; import { getStandardTransformers } from 'app/features/transformers/standardTransformers';
import { DashboardScene } from '../scene/DashboardScene'; import { DashboardScene } from '../scene/DashboardScene';
import { VizPanelLinks, VizPanelLinksMenu } from '../scene/PanelLinks';
import { activateFullSceneTree } from '../utils/test-utils'; import { activateFullSceneTree } from '../utils/test-utils';
import { findVizPanelByKey } from '../utils/utils'; import { findVizPanelByKey } from '../utils/utils';
@ -90,6 +91,7 @@ async function buildTestScene() {
title: 'Panel A', title: 'Panel A',
pluginId: 'table', pluginId: 'table',
key: 'panel-12', key: 'panel-12',
titleItems: [new VizPanelLinks({ menu: new VizPanelLinksMenu({}) })],
$data: new SceneDataTransformer({ $data: new SceneDataTransformer({
transformations: [ transformations: [
{ {

View File

@ -1,10 +1,13 @@
import React from 'react'; import React from 'react';
import { LinkModel } from '@grafana/data'; import { DataLink, LinkModel } from '@grafana/data';
import { SceneComponentProps, SceneObjectBase, SceneObjectState } from '@grafana/scenes'; import { SceneComponentProps, SceneObjectBase, SceneObjectState, VizPanel } from '@grafana/scenes';
import { Dropdown, Menu, ToolbarButton } from '@grafana/ui'; import { Dropdown, Icon, Menu, PanelChrome, ToolbarButton } from '@grafana/ui';
import { getPanelLinks } from './PanelMenuBehavior';
interface VizPanelLinksState extends SceneObjectState { interface VizPanelLinksState extends SceneObjectState {
rawLinks?: DataLink[];
links?: LinkModel[]; links?: LinkModel[];
menu: VizPanelLinksMenu; menu: VizPanelLinksMenu;
} }
@ -14,7 +17,24 @@ export class VizPanelLinks extends SceneObjectBase<VizPanelLinksState> {
} }
function VizPanelLinksRenderer({ model }: SceneComponentProps<VizPanelLinks>) { function VizPanelLinksRenderer({ model }: SceneComponentProps<VizPanelLinks>) {
const { menu } = model.useState(); const { menu, rawLinks } = model.useState();
if (!(model.parent instanceof VizPanel)) {
throw new Error('VizPanelLinks must be a child of VizPanel');
}
if (!rawLinks || rawLinks.length === 0) {
return null;
}
if (rawLinks.length === 1) {
const link = getPanelLinks(model.parent)[0];
return (
<PanelChrome.TitleItem href={link.href} onClick={link.onClick} target={link.target} title={link.title}>
<Icon name="external-link-alt" size="md" />
</PanelChrome.TitleItem>
);
}
return ( return (
<Dropdown <Dropdown
@ -27,7 +47,7 @@ function VizPanelLinksRenderer({ model }: SceneComponentProps<VizPanelLinks>) {
); );
} }
export class VizPanelLinksMenu extends SceneObjectBase<Omit<VizPanelLinksState, 'menu'>> { export class VizPanelLinksMenu extends SceneObjectBase<Omit<VizPanelLinksState, 'menu' | 'rawLinks'>> {
static Component = VizPanelLinksMenuRenderer; static Component = VizPanelLinksMenuRenderer;
} }

View File

@ -10,9 +10,8 @@ import { config, getPluginLinkExtensions, locationService } from '@grafana/runti
import { LocalValueVariable, SceneGridRow, VizPanel, VizPanelMenu, sceneGraph } from '@grafana/scenes'; import { LocalValueVariable, SceneGridRow, VizPanel, VizPanelMenu, sceneGraph } from '@grafana/scenes';
import { DataQuery } from '@grafana/schema'; import { DataQuery } from '@grafana/schema';
import { t } from 'app/core/internationalization'; import { t } from 'app/core/internationalization';
import { PanelModel } from 'app/features/dashboard/state';
import { InspectTab } from 'app/features/inspector/types'; import { InspectTab } from 'app/features/inspector/types';
import { getPanelLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers'; import { getScenePanelLinksSupplier } from 'app/features/panel/panellinks/linkSuppliers';
import { createExtensionSubMenu } from 'app/features/plugins/extensions/utils'; import { createExtensionSubMenu } from 'app/features/plugins/extensions/utils';
import { addDataTrailPanelAction } from 'app/features/trails/dashboardIntegration'; import { addDataTrailPanelAction } from 'app/features/trails/dashboardIntegration';
@ -23,7 +22,7 @@ import { getDashboardSceneFor, getPanelIdForVizPanel, getQueryRunnerFor } from '
import { DashboardScene } from './DashboardScene'; import { DashboardScene } from './DashboardScene';
import { LibraryVizPanel } from './LibraryVizPanel'; import { LibraryVizPanel } from './LibraryVizPanel';
import { VizPanelLinks } from './PanelLinks'; import { VizPanelLinks, VizPanelLinksMenu } from './PanelLinks';
/** /**
* Behavior is called when VizPanelMenu is activated (ie when it's opened). * Behavior is called when VizPanelMenu is activated (ie when it's opened).
@ -217,29 +216,39 @@ function getInspectMenuItem(
/** /**
* Behavior is called when VizPanelLinksMenu is activated (when it's opened). * Behavior is called when VizPanelLinksMenu is activated (when it's opened).
*/ */
export function getPanelLinksBehavior(panel: PanelModel) { export function panelLinksBehavior(panelLinksMenu: VizPanelLinksMenu) {
return (panelLinksMenu: VizPanelLinks) => { if (!(panelLinksMenu.parent instanceof VizPanelLinks)) {
const interpolate: InterpolateFunction = (v, scopedVars) => { throw new Error('parent of VizPanelLinksMenu must be VizPanelLinks');
return sceneGraph.interpolate(panelLinksMenu, v, scopedVars); }
}; const panel = panelLinksMenu.parent.parent;
const linkSupplier = getPanelLinksSupplier(panel, interpolate); if (!(panel instanceof VizPanel)) {
throw new Error('parent of VizPanelLinks must be VizPanel');
}
if (!linkSupplier) { panelLinksMenu.setState({ links: getPanelLinks(panel) });
return; }
}
const panelLinks = linkSupplier && linkSupplier.getLinks(interpolate); export function getPanelLinks(panel: VizPanel) {
const interpolate: InterpolateFunction = (v, scopedVars) => {
const links = panelLinks.map((panelLink) => ({ return sceneGraph.interpolate(panel, v, scopedVars);
...panelLink,
onClick: (e: any, origin: any) => {
DashboardInteractions.panelLinkClicked({ has_multiple_links: panelLinks.length > 1 });
panelLink.onClick?.(e, origin);
},
}));
panelLinksMenu.setState({ links });
}; };
const linkSupplier = getScenePanelLinksSupplier(panel, interpolate);
if (!linkSupplier) {
return [];
}
const panelLinks = linkSupplier.getLinks(interpolate);
return panelLinks.map((panelLink) => ({
...panelLink,
onClick: (e: any, origin: any) => {
DashboardInteractions.panelLinkClicked({ has_multiple_links: panelLinks.length > 1 });
panelLink.onClick?.(e, origin);
},
}));
} }
function createExtensionContext(panel: VizPanel, dashboard: DashboardScene): PluginExtensionPanelContext { function createExtensionContext(panel: VizPanel, dashboard: DashboardScene): PluginExtensionPanelContext {

View File

@ -110,6 +110,7 @@ exports[`transformSceneToSaveModel Given a scene with rows Should transform back
"y": 1, "y": 1,
}, },
"id": 15, "id": 15,
"links": [],
"options": { "options": {
"code": { "code": {
"language": "plaintext", "language": "plaintext",
@ -164,6 +165,7 @@ exports[`transformSceneToSaveModel Given a scene with rows Should transform back
"y": 26, "y": 26,
}, },
"id": 30, "id": 30,
"links": [],
"options": { "options": {
"code": { "code": {
"language": "plaintext", "language": "plaintext",
@ -376,6 +378,7 @@ exports[`transformSceneToSaveModel Given a simple scene with custom settings Sho
"y": 0, "y": 0,
}, },
"id": 28, "id": 28,
"links": [],
"options": { "options": {
"legend": { "legend": {
"calcs": [], "calcs": [],
@ -434,6 +437,7 @@ exports[`transformSceneToSaveModel Given a simple scene with custom settings Sho
"y": 9, "y": 9,
}, },
"id": 29, "id": 29,
"links": [],
"options": {}, "options": {},
"targets": [ "targets": [
{ {
@ -464,6 +468,7 @@ exports[`transformSceneToSaveModel Given a simple scene with custom settings Sho
"y": 9, "y": 9,
}, },
"id": 25, "id": 25,
"links": [],
"options": { "options": {
"code": { "code": {
"language": "plaintext", "language": "plaintext",
@ -692,6 +697,7 @@ exports[`transformSceneToSaveModel Given a simple scene with variables Should tr
"y": 0, "y": 0,
}, },
"id": 28, "id": 28,
"links": [],
"options": { "options": {
"legend": { "legend": {
"calcs": [], "calcs": [],
@ -750,6 +756,7 @@ exports[`transformSceneToSaveModel Given a simple scene with variables Should tr
"y": 9, "y": 9,
}, },
"id": 29, "id": 29,
"links": [],
"options": {}, "options": {},
"targets": [ "targets": [
{ {
@ -780,6 +787,7 @@ exports[`transformSceneToSaveModel Given a simple scene with variables Should tr
"y": 9, "y": 9,
}, },
"id": 25, "id": 25,
"links": [],
"options": { "options": {
"code": { "code": {
"language": "plaintext", "language": "plaintext",

View File

@ -40,7 +40,7 @@ import { registerDashboardMacro } from '../scene/DashboardMacro';
import { DashboardScene } from '../scene/DashboardScene'; import { DashboardScene } from '../scene/DashboardScene';
import { LibraryVizPanel } from '../scene/LibraryVizPanel'; import { LibraryVizPanel } from '../scene/LibraryVizPanel';
import { VizPanelLinks, VizPanelLinksMenu } from '../scene/PanelLinks'; import { VizPanelLinks, VizPanelLinksMenu } from '../scene/PanelLinks';
import { getPanelLinksBehavior, panelMenuBehavior } from '../scene/PanelMenuBehavior'; import { panelLinksBehavior, panelMenuBehavior } from '../scene/PanelMenuBehavior';
import { PanelNotices } from '../scene/PanelNotices'; import { PanelNotices } from '../scene/PanelNotices';
import { PanelRepeaterGridItem } from '../scene/PanelRepeaterGridItem'; import { PanelRepeaterGridItem } from '../scene/PanelRepeaterGridItem';
import { PanelTimeRange } from '../scene/PanelTimeRange'; import { PanelTimeRange } from '../scene/PanelTimeRange';
@ -409,16 +409,14 @@ export function buildGridItemForLibPanel(panel: PanelModel) {
} }
export function buildGridItemForPanel(panel: PanelModel): SceneGridItemLike { export function buildGridItemForPanel(panel: PanelModel): SceneGridItemLike {
const hasPanelLinks = panel.links && panel.links.length > 0;
const titleItems: SceneObject[] = []; const titleItems: SceneObject[] = [];
let panelLinks;
if (hasPanelLinks) { titleItems.push(
panelLinks = new VizPanelLinks({ new VizPanelLinks({
menu: new VizPanelLinksMenu({ $behaviors: [getPanelLinksBehavior(panel)] }), rawLinks: panel.links,
}); menu: new VizPanelLinksMenu({ $behaviors: [panelLinksBehavior] }),
titleItems.push(panelLinks); })
} );
titleItems.push(new PanelNotices()); titleItems.push(new PanelNotices());

View File

@ -277,6 +277,39 @@ describe('transformSceneToSaveModel', () => {
expect(saveModel.gridPos?.w).toBe(12); expect(saveModel.gridPos?.w).toBe(12);
expect(saveModel.gridPos?.h).toBe(8); expect(saveModel.gridPos?.h).toBe(8);
}); });
it('Given panel with links', () => {
const gridItem = buildGridItemFromPanelSchema({
title: '',
type: 'text-plugin-34',
gridPos: { x: 1, y: 2, w: 12, h: 8 },
links: [
// @ts-expect-error Panel link is wrongly typed as DashboardLink
{
title: 'Link 1',
url: 'http://some.test.link1',
},
// @ts-expect-error Panel link is wrongly typed as DashboardLink
{
targetBlank: true,
title: 'Link 2',
url: 'http://some.test.link2',
},
],
});
const saveModel = gridItemToPanel(gridItem);
expect(saveModel.links).toEqual([
{
title: 'Link 1',
url: 'http://some.test.link1',
},
{
targetBlank: true,
title: 'Link 2',
url: 'http://some.test.link2',
},
]);
});
}); });
describe('Library panels', () => { describe('Library panels', () => {

View File

@ -17,6 +17,7 @@ import {
import { import {
AnnotationQuery, AnnotationQuery,
Dashboard, Dashboard,
DashboardLink,
DataTransformerConfig, DataTransformerConfig,
defaultDashboard, defaultDashboard,
defaultTimePickerConfig, defaultTimePickerConfig,
@ -37,6 +38,7 @@ import { LibraryVizPanel } from '../scene/LibraryVizPanel';
import { PanelRepeaterGridItem } from '../scene/PanelRepeaterGridItem'; import { PanelRepeaterGridItem } from '../scene/PanelRepeaterGridItem';
import { PanelTimeRange } from '../scene/PanelTimeRange'; import { PanelTimeRange } from '../scene/PanelTimeRange';
import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior'; import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior';
import { dashboardSceneGraph } from '../utils/dashboardSceneGraph';
import { getPanelIdForVizPanel } from '../utils/utils'; import { getPanelIdForVizPanel } from '../utils/utils';
import { GRAFANA_DATASOURCE_REF } from './const'; import { GRAFANA_DATASOURCE_REF } from './const';
@ -223,6 +225,9 @@ export function gridItemToPanel(gridItem: SceneGridItemLike, isSnapshot = false)
panel.repeatDirection = gridItem.getRepeatDirection(); panel.repeatDirection = gridItem.getRepeatDirection();
} }
const panelLinks = dashboardSceneGraph.getPanelLinks(vizPanel);
panel.links = (panelLinks.state.rawLinks as DashboardLink[]) ?? [];
return panel; return panel;
} }

View File

@ -11,8 +11,10 @@ import {
import { DashboardControls } from '../scene/DashboardControls'; import { DashboardControls } from '../scene/DashboardControls';
import { DashboardLinksControls } from '../scene/DashboardLinksControls'; import { DashboardLinksControls } from '../scene/DashboardLinksControls';
import { DashboardScene, DashboardSceneState } from '../scene/DashboardScene'; import { DashboardScene, DashboardSceneState } from '../scene/DashboardScene';
import { VizPanelLinks, VizPanelLinksMenu } from '../scene/PanelLinks';
import { dashboardSceneGraph } from './dashboardSceneGraph'; import { dashboardSceneGraph } from './dashboardSceneGraph';
import { findVizPanelByKey } from './utils';
describe('dashboardSceneGraph', () => { describe('dashboardSceneGraph', () => {
describe('getTimePicker', () => { describe('getTimePicker', () => {
@ -75,6 +77,20 @@ describe('dashboardSceneGraph', () => {
expect(dashboardControls).not.toBeNull(); expect(dashboardControls).not.toBeNull();
}); });
}); });
describe('getPanelLinks', () => {
it('should throw if no links object defined', () => {
const scene = buildTestScene();
const panelWithNoLinks = findVizPanelByKey(scene, 'panel-1')!;
expect(() => dashboardSceneGraph.getPanelLinks(panelWithNoLinks)).toThrow();
});
it('should resolve VizPanelLinks object', () => {
const scene = buildTestScene();
const panelWithNoLinks = findVizPanelByKey(scene, 'panel-with-links')!;
expect(dashboardSceneGraph.getPanelLinks(panelWithNoLinks)).toBeInstanceOf(VizPanelLinks);
});
});
}); });
function buildTestScene(overrides?: Partial<DashboardSceneState>) { function buildTestScene(overrides?: Partial<DashboardSceneState>) {
@ -121,6 +137,15 @@ function buildTestScene(overrides?: Partial<DashboardSceneState>) {
$data: new SceneQueryRunner({ key: 'data-query-runner2', queries: [{ refId: 'A' }] }), $data: new SceneQueryRunner({ key: 'data-query-runner2', queries: [{ refId: 'A' }] }),
}), }),
}), }),
new SceneGridItem({
body: new VizPanel({
title: 'Panel B',
key: 'panel-with-links',
pluginId: 'table',
$data: new SceneQueryRunner({ key: 'data-query-runner3', queries: [{ refId: 'A' }] }),
titleItems: [new VizPanelLinks({ menu: new VizPanelLinksMenu({}) })],
}),
}),
], ],
}), }),
...overrides, ...overrides,

View File

@ -1,7 +1,8 @@
import { SceneTimePicker, SceneRefreshPicker } from '@grafana/scenes'; import { SceneTimePicker, SceneRefreshPicker, VizPanel } from '@grafana/scenes';
import { DashboardControls } from '../scene/DashboardControls'; import { DashboardControls } from '../scene/DashboardControls';
import { DashboardScene } from '../scene/DashboardScene'; import { DashboardScene } from '../scene/DashboardScene';
import { VizPanelLinks } from '../scene/PanelLinks';
function getTimePicker(scene: DashboardScene) { function getTimePicker(scene: DashboardScene) {
const dashboardControls = getDashboardControls(scene); const dashboardControls = getDashboardControls(scene);
@ -36,8 +37,21 @@ function getDashboardControls(scene: DashboardScene) {
return null; return null;
} }
function getPanelLinks(panel: VizPanel) {
if (
panel.state.titleItems &&
Array.isArray(panel.state.titleItems) &&
panel.state.titleItems[0] instanceof VizPanelLinks
) {
return panel.state.titleItems[0];
}
throw new Error('VizPanelLinks links not found');
}
export const dashboardSceneGraph = { export const dashboardSceneGraph = {
getTimePicker, getTimePicker,
getRefreshPicker, getRefreshPicker,
getDashboardControls, getDashboardControls,
getPanelLinks,
}; };

View File

@ -15,6 +15,7 @@ import { DashboardLoaderSrv, setDashboardLoaderSrv } from 'app/features/dashboar
import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE } from 'app/features/variables/constants'; import { ALL_VARIABLE_TEXT, ALL_VARIABLE_VALUE } from 'app/features/variables/constants';
import { DashboardDTO } from 'app/types'; import { DashboardDTO } from 'app/types';
import { VizPanelLinks, VizPanelLinksMenu } from '../scene/PanelLinks';
import { PanelRepeaterGridItem, RepeatDirection } from '../scene/PanelRepeaterGridItem'; import { PanelRepeaterGridItem, RepeatDirection } from '../scene/PanelRepeaterGridItem';
import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior'; import { RowRepeaterBehavior } from '../scene/RowRepeaterBehavior';
@ -120,7 +121,11 @@ export function buildPanelRepeaterScene(options: SceneOptions) {
y: 0, y: 0,
width: 10, width: 10,
height: 10, height: 10,
body: new VizPanel({ title: 'Panel $server', pluginId: 'timeseries' }), body: new VizPanel({
title: 'Panel $server',
pluginId: 'timeseries',
titleItems: [new VizPanelLinks({ menu: new VizPanelLinksMenu({}) })],
}),
}); });
const rowChildren = defaults.usePanelRepeater ? repeater : gridItem; const rowChildren = defaults.usePanelRepeater ? repeater : gridItem;

View File

@ -3,6 +3,7 @@ import React from 'react';
import { config } from '@grafana/runtime'; import { config } from '@grafana/runtime';
import { VizPanel } from '@grafana/scenes'; import { VizPanel } from '@grafana/scenes';
import { DataLinksInlineEditor, Input, RadioButtonGroup, Select, Switch, TextArea } from '@grafana/ui'; import { DataLinksInlineEditor, Input, RadioButtonGroup, Select, Switch, TextArea } from '@grafana/ui';
import { dashboardSceneGraph } from 'app/features/dashboard-scene/utils/dashboardSceneGraph';
import { getPanelLinksVariableSuggestions } from 'app/features/panel/panellinks/link_srv'; import { getPanelLinksVariableSuggestions } from 'app/features/panel/panellinks/link_srv';
import { GenAIPanelDescriptionButton } from '../GenAI/GenAIPanelDescriptionButton'; import { GenAIPanelDescriptionButton } from '../GenAI/GenAIPanelDescriptionButton';
@ -180,6 +181,9 @@ export function getPanelFrameCategory2(panel: VizPanel): OptionsPaneCategoryDesc
isOpenDefault: true, isOpenDefault: true,
}); });
const panelLinksObject = dashboardSceneGraph.getPanelLinks(panel);
const links = panelLinksObject.state.rawLinks;
return descriptor return descriptor
.addItem( .addItem(
new OptionsPaneItemDescriptor({ new OptionsPaneItemDescriptor({
@ -234,29 +238,31 @@ export function getPanelFrameCategory2(panel: VizPanel): OptionsPaneCategoryDesc
); );
}, },
}) })
)
.addCategory(
new OptionsPaneCategoryDescriptor({
title: 'Panel links',
id: 'Panel links',
isOpenDefault: false,
itemsCount: links?.length,
}).addItem(
new OptionsPaneItemDescriptor({
title: 'Panel links',
render: function renderLinks() {
const { rawLinks: links } = panelLinksObject.useState();
return (
<DataLinksInlineEditor
links={links}
onChange={(links) => panelLinksObject.setState({ rawLinks: links })}
getSuggestions={getPanelLinksVariableSuggestions}
data={[]}
/>
);
},
})
)
); );
// .addCategory( //
// new OptionsPaneCategoryDescriptor({
// title: 'Panel links',
// id: 'Panel links',
// isOpenDefault: false,
// itemsCount: panel.state.links?.length,
// }).addItem(
// new OptionsPaneItemDescriptor({
// title: 'Panel links',
// render: function renderLinks() {
// return (
// <DataLinksInlineEditor
// links={panel.links}
// onChange={(links) => onPanelConfigChange('links', links)}
// getSuggestions={getPanelLinksVariableSuggestions}
// data={[]}
// />
// );
// },
// })
// )
// )
// .addCategory( // .addCategory(
// new OptionsPaneCategoryDescriptor({ // new OptionsPaneCategoryDescriptor({
// title: 'Repeat options', // title: 'Repeat options',

View File

@ -11,7 +11,9 @@ import {
ScopedVar, ScopedVar,
ScopedVars, ScopedVars,
} from '@grafana/data'; } from '@grafana/data';
import { VizPanel } from '@grafana/scenes';
import { PanelModel } from 'app/features/dashboard/state/PanelModel'; import { PanelModel } from 'app/features/dashboard/state/PanelModel';
import { dashboardSceneGraph } from 'app/features/dashboard-scene/utils/dashboardSceneGraph';
import { getLinkSrv } from './link_srv'; import { getLinkSrv } from './link_srv';
@ -157,3 +159,22 @@ export const getPanelLinksSupplier = (
}, },
}; };
}; };
export const getScenePanelLinksSupplier = (
panel: VizPanel,
replaceVariables: InterpolateFunction
): LinkModelSupplier<VizPanel> | undefined => {
const links = dashboardSceneGraph.getPanelLinks(panel).state.rawLinks;
if (!links || links.length === 0) {
return undefined;
}
return {
getLinks: () => {
return links.map((link) => {
return getLinkSrv().getDataLinkUIModel(link, replaceVariables, panel);
});
},
};
};