mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Inspect: Refactor InspectJSONTab to FC (#61106)
This commit is contained in:
parent
9799ac252b
commit
a3e341f24b
@ -1,14 +1,14 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import { DataFrame, DataTransformerID, getFrameDisplayName, SelectableValue } from '@grafana/data';
|
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 { QueryOperationRow } from 'app/core/components/QueryOperationRow/QueryOperationRow';
|
||||||
import { t } from 'app/core/internationalization';
|
import { t } from 'app/core/internationalization';
|
||||||
import { PanelModel } from 'app/features/dashboard/state';
|
import { PanelModel } from 'app/features/dashboard/state';
|
||||||
import { DetailText } from 'app/features/inspector/DetailText';
|
import { DetailText } from 'app/features/inspector/DetailText';
|
||||||
import { GetDataOptions } from 'app/features/query/state/PanelQueryRunner';
|
import { GetDataOptions } from 'app/features/query/state/PanelQueryRunner';
|
||||||
|
|
||||||
import { getPanelInspectorStyles } from './styles';
|
import { getPanelInspectorStyles2 } from './styles';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
options: GetDataOptions;
|
options: GetDataOptions;
|
||||||
@ -37,7 +37,7 @@ export const InspectDataOptions = ({
|
|||||||
downloadForExcel,
|
downloadForExcel,
|
||||||
toggleDownloadForExcel,
|
toggleDownloadForExcel,
|
||||||
}: Props) => {
|
}: Props) => {
|
||||||
const styles = getPanelInspectorStyles();
|
const styles = useStyles2(getPanelInspectorStyles2);
|
||||||
|
|
||||||
const panelTransformations = panel?.getTransformations();
|
const panelTransformations = panel?.getTransformations();
|
||||||
const showPanelTransformationsOption =
|
const showPanelTransformationsOption =
|
||||||
|
@ -20,7 +20,7 @@ const parseErrorMessage = (message: string): { msg: string; json?: any } => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const InspectErrorTab: React.FC<InspectErrorTabProps> = ({ error }) => {
|
export const InspectErrorTab = ({ error }: InspectErrorTabProps) => {
|
||||||
if (!error) {
|
if (!error) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
import { isEqual } from 'lodash';
|
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 AutoSizer from 'react-virtualized-auto-sizer';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { firstValueFrom } from 'rxjs';
|
||||||
|
|
||||||
import { AppEvents, PanelData, SelectableValue, LoadingState } from '@grafana/data';
|
import { AppEvents, PanelData, SelectableValue, LoadingState } from '@grafana/data';
|
||||||
import { selectors } from '@grafana/e2e-selectors';
|
import { selectors } from '@grafana/e2e-selectors';
|
||||||
import { locationService } from '@grafana/runtime';
|
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 { appEvents } from 'app/core/core';
|
||||||
import { t } from 'app/core/internationalization';
|
import { t } from 'app/core/internationalization';
|
||||||
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
||||||
|
|
||||||
import { getPanelDataFrames } from '../dashboard/components/HelpWizard/utils';
|
import { getPanelDataFrames } from '../dashboard/components/HelpWizard/utils';
|
||||||
import { getPanelInspectorStyles } from '../inspector/styles';
|
import { getPanelInspectorStyles2 } from '../inspector/styles';
|
||||||
import { reportPanelInspectInteraction } from '../search/page/reporting';
|
import { reportPanelInspectInteraction } from '../search/page/reporting';
|
||||||
|
|
||||||
import { InspectTab } from './types';
|
import { InspectTab } from './types';
|
||||||
@ -54,80 +55,32 @@ interface Props {
|
|||||||
data?: PanelData;
|
data?: PanelData;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
export function InspectJSONTab({ panel, dashboard, data, onClose }: Props) {
|
||||||
show: ShowContent;
|
const styles = useStyles2(getPanelInspectorStyles2);
|
||||||
text: string;
|
const jsonOptions = useMemo(() => {
|
||||||
}
|
if (panel) {
|
||||||
|
if (panel.plugin?.meta.skipDataQuery) {
|
||||||
export class InspectJSONTab extends PureComponent<Props, State> {
|
return [options[0]];
|
||||||
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<ShowContent>) => {
|
|
||||||
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,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
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) {
|
useAsync(async () => {
|
||||||
reportPanelInspectInteraction(InspectTab.JSON, 'panelJSON');
|
const v = await getJSONObject(show, panel, data);
|
||||||
return panel!.getSaveModel();
|
setText(getPrettyJSON(v));
|
||||||
}
|
}, [show, panel, data]);
|
||||||
|
|
||||||
return { note: t('dashboard.inspect-json.unknown', 'Unknown Object: {{show}}', { show }) };
|
const onApplyPanelModel = useCallback(() => {
|
||||||
}
|
if (panel && dashboard && text) {
|
||||||
|
|
||||||
onApplyPanelModel = () => {
|
|
||||||
const { panel, dashboard, onClose } = this.props;
|
|
||||||
if (this.hasPanelJSON) {
|
|
||||||
try {
|
try {
|
||||||
if (!dashboard!.meta.canEdit) {
|
if (!dashboard!.meta.canEdit) {
|
||||||
appEvents.emit(AppEvents.alertError, ['Unable to apply']);
|
appEvents.emit(AppEvents.alertError, ['Unable to apply']);
|
||||||
} else {
|
} else {
|
||||||
const updates = JSON.parse(this.state.text);
|
const updates = JSON.parse(text);
|
||||||
dashboard!.shouldUpdateDashboardPanelFromJSON(updates, panel!);
|
dashboard!.shouldUpdateDashboardPanelFromJSON(updates, panel!);
|
||||||
|
|
||||||
//Report relevant updates
|
//Report relevant updates
|
||||||
@ -149,65 +102,90 @@ export class InspectJSONTab extends PureComponent<Props, State> {
|
|||||||
|
|
||||||
onClose();
|
onClose();
|
||||||
}
|
}
|
||||||
};
|
}, [panel, dashboard, onClose, text]);
|
||||||
|
|
||||||
onShowHelpWizard = () => {
|
const onShowHelpWizard = useCallback(() => {
|
||||||
reportPanelInspectInteraction(InspectTab.JSON, 'supportWizard');
|
reportPanelInspectInteraction(InspectTab.JSON, 'supportWizard');
|
||||||
const queryParms = locationService.getSearch();
|
const queryParms = locationService.getSearch();
|
||||||
queryParms.set('inspectTab', InspectTab.Help.toString());
|
queryParms.set('inspectTab', InspectTab.Help.toString());
|
||||||
locationService.push('?' + queryParms.toString());
|
locationService.push('?' + queryParms.toString());
|
||||||
};
|
}, []);
|
||||||
|
|
||||||
render() {
|
const isPanelJSON = show === ShowContent.PanelJSON;
|
||||||
const { dashboard } = this.props;
|
const canEdit = dashboard && dashboard.meta.canEdit;
|
||||||
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();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrap}>
|
<div className={styles.wrap}>
|
||||||
<div className={styles.toolbar} aria-label={selectors.components.PanelInspector.Json.content}>
|
<div className={styles.toolbar} aria-label={selectors.components.PanelInspector.Json.content}>
|
||||||
<Field label={t('dashboard.inspect-json.select-source', 'Select source')} className="flex-grow-1">
|
<Field label={t('dashboard.inspect-json.select-source', 'Select source')} className="flex-grow-1">
|
||||||
<Select
|
<Select
|
||||||
inputId="select-source-dropdown"
|
inputId="select-source-dropdown"
|
||||||
options={jsonOptions}
|
options={jsonOptions}
|
||||||
value={selected}
|
value={jsonOptions.find((v) => v.value === show) ?? jsonOptions[0].value}
|
||||||
onChange={this.onSelectChanged}
|
onChange={(v) => setShow(v.value!)}
|
||||||
/>
|
/>
|
||||||
</Field>
|
</Field>
|
||||||
{this.hasPanelJSON && isPanelJSON && canEdit && (
|
{panel && isPanelJSON && canEdit && (
|
||||||
<Button className={styles.toolbarItem} onClick={this.onApplyPanelModel}>
|
<Button className={styles.toolbarItem} onClick={onApplyPanelModel}>
|
||||||
Apply
|
Apply
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{show === ShowContent.DataFrames && (
|
{show === ShowContent.DataFrames && (
|
||||||
<Button className={styles.toolbarItem} onClick={this.onShowHelpWizard}>
|
<Button className={styles.toolbarItem} onClick={onShowHelpWizard}>
|
||||||
Support
|
Support
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
<div className={styles.content}>
|
|
||||||
<AutoSizer disableWidth>
|
|
||||||
{({ height }) => (
|
|
||||||
<CodeEditor
|
|
||||||
width="100%"
|
|
||||||
height={height}
|
|
||||||
language="json"
|
|
||||||
showLineNumbers={true}
|
|
||||||
showMiniMap={(text && text.length) > 100}
|
|
||||||
value={text || ''}
|
|
||||||
readOnly={!isPanelJSON}
|
|
||||||
onBlur={this.onTextChanged}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</AutoSizer>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
|
||||||
|
<div className={styles.content}>
|
||||||
|
<AutoSizer disableWidth>
|
||||||
|
{({ height }) => (
|
||||||
|
<CodeEditor
|
||||||
|
width="100%"
|
||||||
|
height={height}
|
||||||
|
language="json"
|
||||||
|
showLineNumbers={true}
|
||||||
|
showMiniMap={(text && text.length) > 100}
|
||||||
|
value={text || ''}
|
||||||
|
readOnly={!isPanelJSON}
|
||||||
|
onBlur={setText}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</AutoSizer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
function getPrettyJSON(obj: any): string {
|
||||||
|
@ -7,7 +7,7 @@ interface InspectMetadataTabProps {
|
|||||||
data: PanelData;
|
data: PanelData;
|
||||||
metadataDatasource?: DataSourceApi;
|
metadataDatasource?: DataSourceApi;
|
||||||
}
|
}
|
||||||
export const InspectMetadataTab: React.FC<InspectMetadataTabProps> = ({ data, metadataDatasource }) => {
|
export const InspectMetadataTab = ({ data, metadataDatasource }: InspectMetadataTabProps) => {
|
||||||
if (!metadataDatasource || !metadataDatasource.components?.MetadataInspector) {
|
if (!metadataDatasource || !metadataDatasource.components?.MetadataInspector) {
|
||||||
return <Trans i18nKey="dashboard.inspect-meta.no-inspector">No Metadata Inspector</Trans>;
|
return <Trans i18nKey="dashboard.inspect-meta.no-inspector">No Metadata Inspector</Trans>;
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ interface InspectStatsTabProps {
|
|||||||
timeZone: TimeZone;
|
timeZone: TimeZone;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const InspectStatsTab: React.FC<InspectStatsTabProps> = ({ data, timeZone }) => {
|
export const InspectStatsTab = ({ data, timeZone }: InspectStatsTabProps) => {
|
||||||
if (!data.request) {
|
if (!data.request) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ interface InspectStatsTableProps {
|
|||||||
stats: QueryResultMetaStat[];
|
stats: QueryResultMetaStat[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const InspectStatsTable: React.FC<InspectStatsTableProps> = ({ timeZone, name, stats }) => {
|
export const InspectStatsTable = ({ timeZone, name, stats }: InspectStatsTableProps) => {
|
||||||
const theme = useTheme2();
|
const theme = useTheme2();
|
||||||
const styles = getStyles(theme);
|
const styles = getStyles(theme);
|
||||||
|
|
||||||
|
@ -1,9 +1,15 @@
|
|||||||
import { css } from '@emotion/css';
|
import { css } from '@emotion/css';
|
||||||
|
|
||||||
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { stylesFactory } from '@grafana/ui';
|
import { stylesFactory } from '@grafana/ui';
|
||||||
import { config } from 'app/core/config';
|
import { config } from 'app/core/config';
|
||||||
|
|
||||||
|
/** @deprecated */
|
||||||
export const getPanelInspectorStyles = stylesFactory(() => {
|
export const getPanelInspectorStyles = stylesFactory(() => {
|
||||||
|
return getPanelInspectorStyles2(config.theme2);
|
||||||
|
});
|
||||||
|
|
||||||
|
export const getPanelInspectorStyles2 = (theme: GrafanaTheme2) => {
|
||||||
return {
|
return {
|
||||||
wrap: css`
|
wrap: css`
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -18,10 +24,10 @@ export const getPanelInspectorStyles = stylesFactory(() => {
|
|||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
margin-bottom: ${config.theme.spacing.sm};
|
margin-bottom: ${theme.v1.spacing.sm};
|
||||||
`,
|
`,
|
||||||
toolbarItem: css`
|
toolbarItem: css`
|
||||||
margin-left: ${config.theme.spacing.md};
|
margin-left: ${theme.v1.spacing.md};
|
||||||
`,
|
`,
|
||||||
content: css`
|
content: css`
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
@ -49,18 +55,18 @@ export const getPanelInspectorStyles = stylesFactory(() => {
|
|||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
options: css`
|
options: css`
|
||||||
padding-top: ${config.theme.spacing.sm};
|
padding-top: ${theme.v1.spacing.sm};
|
||||||
`,
|
`,
|
||||||
dataDisplayOptions: css`
|
dataDisplayOptions: css`
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
min-width: 300px;
|
min-width: 300px;
|
||||||
margin-right: ${config.theme.spacing.sm};
|
margin-right: ${theme.v1.spacing.sm};
|
||||||
`,
|
`,
|
||||||
selects: css`
|
selects: css`
|
||||||
display: flex;
|
display: flex;
|
||||||
> * {
|
> * {
|
||||||
margin-right: ${config.theme.spacing.sm};
|
margin-right: ${theme.v1.spacing.sm};
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
};
|
};
|
||||||
});
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user