import { isEqual } from 'lodash'; import AutoSizer from 'react-virtualized-auto-sizer'; import { SelectableValue } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { SceneComponentProps, SceneDataTransformer, sceneGraph, SceneGridItemStateLike, SceneObjectBase, SceneObjectRef, SceneObjectState, SceneQueryRunner, sceneUtils, VizPanel, } from '@grafana/scenes'; import { LibraryPanel } from '@grafana/schema/'; import { Button, CodeEditor, Field, Select, useStyles2 } from '@grafana/ui'; import { t } from 'app/core/internationalization'; import { getPanelDataFrames } from 'app/features/dashboard/components/HelpWizard/utils'; import { PanelModel } from 'app/features/dashboard/state/PanelModel'; import { getPanelInspectorStyles2 } from 'app/features/inspector/styles'; import { InspectTab } from 'app/features/inspector/types'; import { getPrettyJSON } from 'app/features/inspector/utils/utils'; import { reportPanelInspectInteraction } from 'app/features/search/page/reporting'; import { DashboardGridItem } from '../scene/layout-default/DashboardGridItem'; import { buildGridItemForPanel } from '../serialization/transformSaveModelToScene'; import { gridItemToPanel, vizPanelToPanel } from '../serialization/transformSceneToSaveModel'; import { getDashboardSceneFor, getLibraryPanelBehavior, getPanelIdForVizPanel, getQueryRunnerFor, isLibraryPanel, } from '../utils/utils'; export type ShowContent = 'panel-json' | 'panel-data' | 'data-frames'; export interface InspectJsonTabState extends SceneObjectState { panelRef: SceneObjectRef; source: ShowContent; jsonText: string; onClose: () => void; } export class InspectJsonTab extends SceneObjectBase { public constructor(state: Omit) { super({ ...state, source: 'panel-json', jsonText: getJsonText('panel-json', state.panelRef.resolve()), }); } public getTabLabel() { return t('dashboard.inspect.json-tab', 'JSON'); } public getTabValue() { return InspectTab.JSON; } public getOptions(): Array> { const panel = this.state.panelRef.resolve(); const dataProvider = panel.state.$data ?? panel.parent?.state.$data; const options: Array> = [ { label: t('dashboard.inspect-json.panel-json-label', 'Panel JSON'), description: t( 'dashboard.inspect-json.panel-json-description', 'The model saved in the dashboard JSON that configures how everything works.' ), value: 'panel-json', }, ]; if (dataProvider) { options.push({ label: t('dashboard.inspect-json.panel-data-label', 'Panel data'), description: t( 'dashboard.inspect-json.panel-data-description', 'The raw model passed to the panel visualization' ), value: 'panel-data', }); options.push({ label: t('dashboard.inspect-json.dataframe-label', 'DataFrame JSON (from Query)'), description: t( 'dashboard.inspect-json.dataframe-description', 'Raw data without transformations and field config applied. ' ), value: 'data-frames', }); } return options; } public onChangeSource = (value: SelectableValue) => { this.setState({ source: value.value!, jsonText: getJsonText(value.value!, this.state.panelRef.resolve()) }); }; public onApplyChange = () => { const panel = this.state.panelRef.resolve(); const dashboard = getDashboardSceneFor(panel); const jsonObj = JSON.parse(this.state.jsonText); const panelModel = new PanelModel(jsonObj); const gridItem = buildGridItemForPanel(panelModel); const newState = sceneUtils.cloneSceneObjectState(gridItem.state); if (!(panel.parent instanceof DashboardGridItem)) { console.error('Cannot update state of panel', panel, gridItem); return; } this.state.onClose(); if (!dashboard.state.isEditing) { dashboard.onEnterEditMode(); } panel.parent.setState(newState); //Report relevant updates reportPanelInspectInteraction(InspectTab.JSON, 'apply', { panel_type_changed: panel.state.pluginId !== panelModel.type, panel_id_changed: getPanelIdForVizPanel(panel) !== panelModel.id, panel_grid_pos_changed: hasGridPosChanged(panel.parent.state, newState), panel_targets_changed: hasQueriesChanged(getQueryRunnerFor(panel), getQueryRunnerFor(newState.$data)), }); }; public onCodeEditorBlur = (value: string) => { this.setState({ jsonText: value }); }; public isEditable() { if (this.state.source !== 'panel-json') { return false; } const panel = this.state.panelRef.resolve(); // Library panels are not editable from the inspect if (isLibraryPanel(panel)) { return false; } // Only support normal grid items for now and not repeated items if (panel.parent instanceof DashboardGridItem && panel.parent.isRepeated()) { return false; } const dashboard = getDashboardSceneFor(panel); return dashboard.state.meta.canEdit; } static Component = ({ model }: SceneComponentProps) => { const { source: show, jsonText } = model.useState(); const styles = useStyles2(getPanelInspectorStyles2); const options = model.getOptions(); return (