mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
parent
ab891d92fb
commit
f20053e4b6
@ -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"]
|
||||||
|
@ -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: [
|
||||||
{
|
{
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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",
|
||||||
|
@ -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());
|
||||||
|
|
||||||
|
@ -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', () => {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
@ -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;
|
||||||
|
@ -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',
|
||||||
|
@ -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);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user