From a3e341f24b79884d5c3d3fe271ac4424485146ed Mon Sep 17 00:00:00 2001 From: Ryan McKinley Date: Fri, 6 Jan 2023 15:14:20 -0800 Subject: [PATCH] Inspect: Refactor InspectJSONTab to FC (#61106) --- .../features/inspector/InspectDataOptions.tsx | 6 +- .../features/inspector/InspectErrorTab.tsx | 2 +- .../app/features/inspector/InspectJSONTab.tsx | 216 ++++++++---------- .../features/inspector/InspectMetadataTab.tsx | 2 +- .../features/inspector/InspectStatsTab.tsx | 2 +- .../features/inspector/InspectStatsTable.tsx | 2 +- public/app/features/inspector/styles.ts | 18 +- 7 files changed, 116 insertions(+), 132 deletions(-) diff --git a/public/app/features/inspector/InspectDataOptions.tsx b/public/app/features/inspector/InspectDataOptions.tsx index 073e90eba44..663753d896c 100644 --- a/public/app/features/inspector/InspectDataOptions.tsx +++ b/public/app/features/inspector/InspectDataOptions.tsx @@ -1,14 +1,14 @@ import React from 'react'; import { DataFrame, DataTransformerID, getFrameDisplayName, SelectableValue } from '@grafana/data'; -import { Field, HorizontalGroup, Select, Switch, VerticalGroup } from '@grafana/ui'; +import { Field, HorizontalGroup, Select, Switch, VerticalGroup, useStyles2 } from '@grafana/ui'; import { QueryOperationRow } from 'app/core/components/QueryOperationRow/QueryOperationRow'; import { t } from 'app/core/internationalization'; import { PanelModel } from 'app/features/dashboard/state'; import { DetailText } from 'app/features/inspector/DetailText'; import { GetDataOptions } from 'app/features/query/state/PanelQueryRunner'; -import { getPanelInspectorStyles } from './styles'; +import { getPanelInspectorStyles2 } from './styles'; interface Props { options: GetDataOptions; @@ -37,7 +37,7 @@ export const InspectDataOptions = ({ downloadForExcel, toggleDownloadForExcel, }: Props) => { - const styles = getPanelInspectorStyles(); + const styles = useStyles2(getPanelInspectorStyles2); const panelTransformations = panel?.getTransformations(); const showPanelTransformationsOption = diff --git a/public/app/features/inspector/InspectErrorTab.tsx b/public/app/features/inspector/InspectErrorTab.tsx index f3609b187f4..d149e66c5cf 100644 --- a/public/app/features/inspector/InspectErrorTab.tsx +++ b/public/app/features/inspector/InspectErrorTab.tsx @@ -20,7 +20,7 @@ const parseErrorMessage = (message: string): { msg: string; json?: any } => { } }; -export const InspectErrorTab: React.FC = ({ error }) => { +export const InspectErrorTab = ({ error }: InspectErrorTabProps) => { if (!error) { return null; } diff --git a/public/app/features/inspector/InspectJSONTab.tsx b/public/app/features/inspector/InspectJSONTab.tsx index 313da7021e6..ff78fe81898 100644 --- a/public/app/features/inspector/InspectJSONTab.tsx +++ b/public/app/features/inspector/InspectJSONTab.tsx @@ -1,18 +1,19 @@ import { isEqual } from 'lodash'; -import React, { PureComponent } from 'react'; +import React, { useState, useCallback, useMemo } from 'react'; +import { useAsync } from 'react-use'; import AutoSizer from 'react-virtualized-auto-sizer'; import { firstValueFrom } from 'rxjs'; import { AppEvents, PanelData, SelectableValue, LoadingState } from '@grafana/data'; import { selectors } from '@grafana/e2e-selectors'; import { locationService } from '@grafana/runtime'; -import { Button, CodeEditor, Field, Select } from '@grafana/ui'; +import { Button, CodeEditor, Field, Select, useStyles2 } from '@grafana/ui'; import { appEvents } from 'app/core/core'; import { t } from 'app/core/internationalization'; import { DashboardModel, PanelModel } from 'app/features/dashboard/state'; import { getPanelDataFrames } from '../dashboard/components/HelpWizard/utils'; -import { getPanelInspectorStyles } from '../inspector/styles'; +import { getPanelInspectorStyles2 } from '../inspector/styles'; import { reportPanelInspectInteraction } from '../search/page/reporting'; import { InspectTab } from './types'; @@ -54,80 +55,32 @@ interface Props { data?: PanelData; } -interface State { - show: ShowContent; - text: string; -} - -export class InspectJSONTab extends PureComponent { - hasPanelJSON: boolean; - - constructor(props: Props) { - super(props); - this.hasPanelJSON = !!(props.panel && props.dashboard); - // If we are in panel, we want to show PanelJSON, otherwise show DataFrames - this.state = { - show: this.hasPanelJSON ? ShowContent.PanelJSON : ShowContent.DataFrames, - text: this.hasPanelJSON ? getPrettyJSON(props.panel!.getSaveModel()) : getPrettyJSON(props.data), - }; - } - - componentDidMount() { - // when opening the inspector we want to report the interaction - reportPanelInspectInteraction(InspectTab.JSON, 'panelJSON'); - } - - onSelectChanged = async (item: SelectableValue) => { - const show = await this.getJSONObject(item.value!); - const text = getPrettyJSON(show); - this.setState({ text, show: item.value! }); - }; - - // Called onBlur - onTextChanged = (text: string) => { - this.setState({ text }); - }; - - async getJSONObject(show: ShowContent) { - const { data, panel } = this.props; - if (show === ShowContent.PanelData) { - reportPanelInspectInteraction(InspectTab.JSON, 'panelData'); - return data; - } - - if (show === ShowContent.DataFrames) { - reportPanelInspectInteraction(InspectTab.JSON, 'dataFrame'); - - let d = data; - - // do not include transforms and - if (panel && data?.state === LoadingState.Done) { - d = await firstValueFrom( - panel.getQueryRunner().getData({ - withFieldConfig: false, - withTransforms: false, - }) - ); +export function InspectJSONTab({ panel, dashboard, data, onClose }: Props) { + const styles = useStyles2(getPanelInspectorStyles2); + const jsonOptions = useMemo(() => { + if (panel) { + if (panel.plugin?.meta.skipDataQuery) { + return [options[0]]; } - return getPanelDataFrames(d); + return options; } + return options.slice(1, options.length); + }, [panel]); + const [show, setShow] = useState(panel ? ShowContent.PanelJSON : ShowContent.DataFrames); + const [text, setText] = useState(''); - if (this.hasPanelJSON && show === ShowContent.PanelJSON) { - reportPanelInspectInteraction(InspectTab.JSON, 'panelJSON'); - return panel!.getSaveModel(); - } + useAsync(async () => { + const v = await getJSONObject(show, panel, data); + setText(getPrettyJSON(v)); + }, [show, panel, data]); - return { note: t('dashboard.inspect-json.unknown', 'Unknown Object: {{show}}', { show }) }; - } - - onApplyPanelModel = () => { - const { panel, dashboard, onClose } = this.props; - if (this.hasPanelJSON) { + const onApplyPanelModel = useCallback(() => { + if (panel && dashboard && text) { try { if (!dashboard!.meta.canEdit) { appEvents.emit(AppEvents.alertError, ['Unable to apply']); } else { - const updates = JSON.parse(this.state.text); + const updates = JSON.parse(text); dashboard!.shouldUpdateDashboardPanelFromJSON(updates, panel!); //Report relevant updates @@ -149,65 +102,90 @@ export class InspectJSONTab extends PureComponent { onClose(); } - }; + }, [panel, dashboard, onClose, text]); - onShowHelpWizard = () => { + const onShowHelpWizard = useCallback(() => { reportPanelInspectInteraction(InspectTab.JSON, 'supportWizard'); const queryParms = locationService.getSearch(); queryParms.set('inspectTab', InspectTab.Help.toString()); locationService.push('?' + queryParms.toString()); - }; + }, []); - render() { - const { dashboard } = this.props; - const { show, text } = this.state; - const jsonOptions = this.hasPanelJSON ? options : options.slice(1, options.length); - const selected = options.find((v) => v.value === show); - const isPanelJSON = show === ShowContent.PanelJSON; - const canEdit = dashboard && dashboard.meta.canEdit; - const styles = getPanelInspectorStyles(); + const isPanelJSON = show === ShowContent.PanelJSON; + const canEdit = dashboard && dashboard.meta.canEdit; - return ( -
-
- - v.value === show) ?? jsonOptions[0].value} + onChange={(v) => setShow(v.value!)} + /> + + {panel && isPanelJSON && canEdit && ( + + )} + {show === ShowContent.DataFrames && ( + + )}
- ); + +
+ + {({ height }) => ( + 100} + value={text || ''} + readOnly={!isPanelJSON} + onBlur={setText} + /> + )} + +
+
+ ); +} + +async function getJSONObject(show: ShowContent, panel?: PanelModel, data?: PanelData) { + if (show === ShowContent.PanelData) { + reportPanelInspectInteraction(InspectTab.JSON, 'panelData'); + return data; } + + if (show === ShowContent.DataFrames) { + reportPanelInspectInteraction(InspectTab.JSON, 'dataFrame'); + + let d = data; + + // do not include transforms and + if (panel && data?.state === LoadingState.Done) { + d = await firstValueFrom( + panel.getQueryRunner().getData({ + withFieldConfig: false, + withTransforms: false, + }) + ); + } + return getPanelDataFrames(d); + } + + if (show === ShowContent.PanelJSON && panel) { + reportPanelInspectInteraction(InspectTab.JSON, 'panelJSON'); + return panel!.getSaveModel(); + } + + return { note: t('dashboard.inspect-json.unknown', 'Unknown Object: {{show}}', { show }) }; } function getPrettyJSON(obj: any): string { diff --git a/public/app/features/inspector/InspectMetadataTab.tsx b/public/app/features/inspector/InspectMetadataTab.tsx index ac0b8203d29..53dcea54f5a 100644 --- a/public/app/features/inspector/InspectMetadataTab.tsx +++ b/public/app/features/inspector/InspectMetadataTab.tsx @@ -7,7 +7,7 @@ interface InspectMetadataTabProps { data: PanelData; metadataDatasource?: DataSourceApi; } -export const InspectMetadataTab: React.FC = ({ data, metadataDatasource }) => { +export const InspectMetadataTab = ({ data, metadataDatasource }: InspectMetadataTabProps) => { if (!metadataDatasource || !metadataDatasource.components?.MetadataInspector) { return No Metadata Inspector; } diff --git a/public/app/features/inspector/InspectStatsTab.tsx b/public/app/features/inspector/InspectStatsTab.tsx index b0a8004b9f8..541a3bd0da5 100644 --- a/public/app/features/inspector/InspectStatsTab.tsx +++ b/public/app/features/inspector/InspectStatsTab.tsx @@ -11,7 +11,7 @@ interface InspectStatsTabProps { timeZone: TimeZone; } -export const InspectStatsTab: React.FC = ({ data, timeZone }) => { +export const InspectStatsTab = ({ data, timeZone }: InspectStatsTabProps) => { if (!data.request) { return null; } diff --git a/public/app/features/inspector/InspectStatsTable.tsx b/public/app/features/inspector/InspectStatsTable.tsx index cc1c5ba6665..042aedcf8a1 100644 --- a/public/app/features/inspector/InspectStatsTable.tsx +++ b/public/app/features/inspector/InspectStatsTable.tsx @@ -17,7 +17,7 @@ interface InspectStatsTableProps { stats: QueryResultMetaStat[]; } -export const InspectStatsTable: React.FC = ({ timeZone, name, stats }) => { +export const InspectStatsTable = ({ timeZone, name, stats }: InspectStatsTableProps) => { const theme = useTheme2(); const styles = getStyles(theme); diff --git a/public/app/features/inspector/styles.ts b/public/app/features/inspector/styles.ts index 123ccb131a7..9e72803d2fa 100644 --- a/public/app/features/inspector/styles.ts +++ b/public/app/features/inspector/styles.ts @@ -1,9 +1,15 @@ import { css } from '@emotion/css'; +import { GrafanaTheme2 } from '@grafana/data'; import { stylesFactory } from '@grafana/ui'; import { config } from 'app/core/config'; +/** @deprecated */ export const getPanelInspectorStyles = stylesFactory(() => { + return getPanelInspectorStyles2(config.theme2); +}); + +export const getPanelInspectorStyles2 = (theme: GrafanaTheme2) => { return { wrap: css` display: flex; @@ -18,10 +24,10 @@ export const getPanelInspectorStyles = stylesFactory(() => { flex-grow: 0; align-items: center; justify-content: flex-end; - margin-bottom: ${config.theme.spacing.sm}; + margin-bottom: ${theme.v1.spacing.sm}; `, toolbarItem: css` - margin-left: ${config.theme.spacing.md}; + margin-left: ${theme.v1.spacing.md}; `, content: css` flex-grow: 1; @@ -49,18 +55,18 @@ export const getPanelInspectorStyles = stylesFactory(() => { } `, options: css` - padding-top: ${config.theme.spacing.sm}; + padding-top: ${theme.v1.spacing.sm}; `, dataDisplayOptions: css` flex-grow: 1; min-width: 300px; - margin-right: ${config.theme.spacing.sm}; + margin-right: ${theme.v1.spacing.sm}; `, selects: css` display: flex; > * { - margin-right: ${config.theme.spacing.sm}; + margin-right: ${theme.v1.spacing.sm}; } `, }; -}); +};