From 855da80542299eb603a33a8853f74cd2c526fcc9 Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Tue, 18 May 2021 13:55:35 -0700 Subject: [PATCH] Panel Inspect: allow plugins to add custom actions to the panel inspect popup (#34086) --- .../components/Inspector/InspectContent.tsx | 2 + .../Inspector/PanelInspectActions.tsx | 84 +++++++++++++++++++ .../components/Inspector/PanelInspector.tsx | 2 +- .../dashboard/components/Inspector/hooks.ts | 13 ++- public/app/features/inspector/types.ts | 1 + 5 files changed, 99 insertions(+), 3 deletions(-) create mode 100644 public/app/features/dashboard/components/Inspector/PanelInspectActions.tsx diff --git a/public/app/features/dashboard/components/Inspector/InspectContent.tsx b/public/app/features/dashboard/components/Inspector/InspectContent.tsx index 31045988324..618a0959d37 100644 --- a/public/app/features/dashboard/components/Inspector/InspectContent.tsx +++ b/public/app/features/dashboard/components/Inspector/InspectContent.tsx @@ -13,6 +13,7 @@ import { InspectDataTab } from 'app/features/inspector/InspectDataTab'; import { InspectTab } from 'app/features/inspector/types'; import { DashboardModel, PanelModel } from '../../state'; import { GetDataOptions } from '../../../query/state/PanelQueryRunner'; +import { InspectActionsTab } from './PanelInspectActions'; interface Props { dashboard: DashboardModel; @@ -97,6 +98,7 @@ export const InspectContent: React.FC = ({ {data && activeTab === InspectTab.Query && ( panel.refresh()} /> )} + {activeTab === InspectTab.Actions && } diff --git a/public/app/features/dashboard/components/Inspector/PanelInspectActions.tsx b/public/app/features/dashboard/components/Inspector/PanelInspectActions.tsx new file mode 100644 index 00000000000..795f7a897d0 --- /dev/null +++ b/public/app/features/dashboard/components/Inspector/PanelInspectActions.tsx @@ -0,0 +1,84 @@ +import { PanelModel } from '../../state'; + +export interface PanelInspectActionProps { + panel: PanelModel; + data?: PanelData; +} + +export interface PanelInspectAction { + title: string; + description: string; + + component: ComponentType; +} + +export interface PanelInspectActionSupplier { + getActions: (panel: PanelModel) => PanelInspectAction[] | null; +} + +// const dummySupplier: PanelInspectActionSupplier = { +// getActions: (panel: PanelModel) => { +// return [ +// { +// title: 'Do something', +// description: 'that thingy', +// // eslint-disable-next-line react/display-name +// component: ({ panel }) => { +// return ( +//
+// DO THING ONE. Panel:
{JSON.stringify(panel.targets)}
+//
+// ); +// }, +// }, +// { +// title: 'Do something else', +// description: 'another thing', +// // eslint-disable-next-line react/display-name +// component: ({ panel }) => { +// return ( +//
+// DO THING TWO. Panel:
{JSON.stringify(panel.targets)}
+//
+// ); +// }, +// }, +// ]; +// }, +// }; + +// In Grafana 8.1, this can be improved and moved to `@grafana/runtime` +// NOTE: This is an internal/experimental API/hack and will change! +// (window as any).grafanaPanelInspectActionSupplier = dummySupplier; + +import React, { ComponentType } from 'react'; +import { PanelData } from '@grafana/data'; + +interface InspectActionsTabProps { + panel: PanelModel; + data?: PanelData; +} + +export const InspectActionsTab: React.FC = ({ panel, data }) => { + const supplier = (window as any).grafanaPanelInspectActionSupplier as PanelInspectActionSupplier; + if (!supplier) { + return
Missing actions
; + } + + const actions = supplier.getActions(panel); + if (!actions?.length) { + return
No actions avaliable
; + } + + return ( +
+ {actions.map((a, idx) => ( +
+

{a.title}

+ {a.description} + +
+ ))} +
+ ); +}; diff --git a/public/app/features/dashboard/components/Inspector/PanelInspector.tsx b/public/app/features/dashboard/components/Inspector/PanelInspector.tsx index 90e3c793520..6d0c05e5859 100644 --- a/public/app/features/dashboard/components/Inspector/PanelInspector.tsx +++ b/public/app/features/dashboard/components/Inspector/PanelInspector.tsx @@ -31,7 +31,7 @@ const PanelInspectorUnconnected: React.FC = ({ panel, dashboard, plugin } const location = useLocation(); const { data, isLoading, error } = usePanelLatestData(panel, dataOptions, true); const metaDs = useDatasourceMetadata(data); - const tabs = useInspectTabs(dashboard, plugin, error, metaDs); + const tabs = useInspectTabs(panel, dashboard, plugin, error, metaDs); const defaultTab = new URLSearchParams(location.search).get('inspectTab') as InspectTab; const onClose = () => { diff --git a/public/app/features/dashboard/components/Inspector/hooks.ts b/public/app/features/dashboard/components/Inspector/hooks.ts index a3aca48b476..efe29ccafde 100644 --- a/public/app/features/dashboard/components/Inspector/hooks.ts +++ b/public/app/features/dashboard/components/Inspector/hooks.ts @@ -1,10 +1,11 @@ import { DataQueryError, DataSourceApi, PanelData, PanelPlugin } from '@grafana/data'; import useAsync from 'react-use/lib/useAsync'; import { getDataSourceSrv } from '@grafana/runtime'; -import { DashboardModel } from 'app/features/dashboard/state'; +import { DashboardModel, PanelModel } from 'app/features/dashboard/state'; import { useMemo } from 'react'; import { supportsDataQuery } from '../PanelEditor/utils'; import { InspectTab } from 'app/features/inspector/types'; +import { PanelInspectActionSupplier } from './PanelInspectActions'; /** * Given PanelData return first data source supporting metadata inspector @@ -34,6 +35,7 @@ export const useDatasourceMetadata = (data?: PanelData) => { * Configures tabs for PanelInspector */ export const useInspectTabs = ( + panel: PanelModel, dashboard: DashboardModel, plugin: PanelPlugin | undefined | null, error?: DataQueryError, @@ -56,9 +58,16 @@ export const useInspectTabs = ( tabs.push({ label: 'Error', value: InspectTab.Error }); } + // This is a quick internal hack to allow custom actions in inspect + // For 8.1, something like this should be exposed through grafana/runtime + const supplier = (window as any).grafanaPanelInspectActionSupplier as PanelInspectActionSupplier; + if (supplier && supplier.getActions(panel)) { + tabs.push({ label: 'Actions', value: InspectTab.Actions }); + } + if (dashboard.meta.canEdit && supportsDataQuery(plugin)) { tabs.push({ label: 'Query', value: InspectTab.Query }); } return tabs; - }, [plugin, metaDs, dashboard, error]); + }, [panel, plugin, metaDs, dashboard, error]); }; diff --git a/public/app/features/inspector/types.ts b/public/app/features/inspector/types.ts index 757278ce5ef..77fe612a563 100644 --- a/public/app/features/inspector/types.ts +++ b/public/app/features/inspector/types.ts @@ -5,4 +5,5 @@ export enum InspectTab { Stats = 'stats', JSON = 'json', Query = 'query', + Actions = 'actions', // ALPHA! }