import { useLocation } from 'react-router-dom-v5-compat'; import { locationUtil } from '@grafana/data'; import { locationService } from '@grafana/runtime'; import { SceneComponentProps, SceneObjectBase, SceneObjectState, sceneGraph, VizPanel, SceneObjectRef, } from '@grafana/scenes'; import { Alert, Drawer, Tab, TabsBar } from '@grafana/ui'; import { getDataSourceWithInspector } from 'app/features/dashboard/components/Inspector/hooks'; import { supportsDataQuery } from 'app/features/dashboard/components/PanelEditor/utils'; import { InspectTab } from 'app/features/inspector/types'; import { DashboardScene } from '../scene/DashboardScene'; import { getDashboardUrl } from '../utils/getDashboardUrl'; import { getDashboardSceneFor } from '../utils/utils'; import { HelpWizard } from './HelpWizard/HelpWizard'; import { InspectDataTab } from './InspectDataTab'; import { InspectJsonTab } from './InspectJsonTab'; import { InspectMetaDataTab } from './InspectMetaDataTab'; import { InspectQueryTab } from './InspectQueryTab'; import { InspectStatsTab } from './InspectStatsTab'; import { SceneInspectTab } from './types'; interface PanelInspectDrawerState extends SceneObjectState { tabs?: SceneInspectTab[]; panelRef: SceneObjectRef; pluginNotLoaded?: boolean; canEdit?: boolean; } export class PanelInspectDrawer extends SceneObjectBase { static Component = PanelInspectRenderer; constructor(state: PanelInspectDrawerState) { super(state); this.addActivationHandler(() => this._activationHandler()); } private _activationHandler() { this.buildTabs(0); } /** * We currently have no async await to get the panel plugin from the VizPanel. * That is why there is a retry argument here and a setTimeout, to try again a bit later. */ async buildTabs(retry: number) { const panelRef = this.state.panelRef; const plugin = panelRef.resolve()?.getPlugin(); const tabs: SceneInspectTab[] = []; if (!plugin) { if (retry < 2000) { setTimeout(() => this.buildTabs(retry + 100), 100); } else { this.setState({ pluginNotLoaded: true }); } } if (panelRef) { if (supportsDataQuery(plugin)) { const data = sceneGraph.getData(panelRef.resolve()); tabs.push(new InspectDataTab({ panelRef })); tabs.push(new InspectStatsTab({ panelRef })); tabs.push(new InspectQueryTab({ panelRef })); const dsWithInspector = await getDataSourceWithInspector(data.state.data); if (dsWithInspector) { tabs.push(new InspectMetaDataTab({ panelRef, dataSource: dsWithInspector })); } } tabs.push(new InspectJsonTab({ panelRef, onClose: this.onClose })); } this.setState({ tabs }); } getDrawerTitle() { const panel = this.state.panelRef?.resolve(); if (panel) { return sceneGraph.interpolate(panel, `Inspect: ${panel.state.title}`); } return `Inspect panel`; } onClose = () => { onPanelInspectClose(getDashboardSceneFor(this)); }; } function PanelInspectRenderer({ model }: SceneComponentProps) { const { tabs, pluginNotLoaded, panelRef } = model.useState(); const location = useLocation(); const queryParams = new URLSearchParams(location.search); if (!tabs) { return null; } const urlTab = queryParams.get('inspectTab'); const currentTab = tabs.find((tab) => tab.getTabValue() === urlTab) ?? tabs[0]; const vizPanel = panelRef!.resolve(); if (urlTab === InspectTab.Help) { return ; } return ( {tabs.map((tab) => { return ( ); })} } > {pluginNotLoaded && ( Make sure the panel you want to inspect is visible and has been displayed before opening inspect. )} {currentTab && currentTab.Component && } ); } export function onPanelInspectClose(dashboard: DashboardScene) { const meta = dashboard.state.meta; // Checking for location here as well, otherwise down below isHomeDashboard will be set to true // as it doesn't have uid neither slug nor url. const isNew = !dashboard.state.uid && locationService.getLocation().pathname === '/dashboard/new'; locationService.push( getDashboardUrl({ uid: dashboard.state.uid, slug: dashboard.state.meta.slug, currentQueryParams: locationService.getLocation().search, updateQuery: { inspect: null, inspectTab: null, }, isHomeDashboard: !meta.url && !meta.slug && !isNew, }) ); }