Tracing: Move upload trace to button (#69402)
* Tempo: Move upload trace to its own button * Zipkin: Move upload trace to its own button * Jaeger: Move upload trace to its own button * Fix test * Remove extra upload section from editor body --------- Co-authored-by: Joey Tawadrous <joey.tawadrous@grafana.com>
This commit is contained in:
co-authored by
Joey Tawadrous
parent
6180c77763
commit
08ed68e675
@@ -1,8 +1,19 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { GrafanaTheme2, QueryEditorProps } from '@grafana/data';
|
||||
import { FileDropzone, InlineField, InlineFieldRow, QueryField, RadioButtonGroup, useStyles2 } from '@grafana/ui';
|
||||
import { QueryEditorProps } from '@grafana/data';
|
||||
import {
|
||||
Button,
|
||||
FileDropzone,
|
||||
HorizontalGroup,
|
||||
InlineField,
|
||||
InlineFieldRow,
|
||||
Modal,
|
||||
QueryField,
|
||||
RadioButtonGroup,
|
||||
useStyles2,
|
||||
useTheme2,
|
||||
} from '@grafana/ui';
|
||||
|
||||
import { JaegerDatasource } from '../datasource';
|
||||
import { JaegerQuery, JaegerQueryType } from '../types';
|
||||
@@ -12,6 +23,8 @@ import { SearchForm } from './SearchForm';
|
||||
type Props = QueryEditorProps<JaegerDatasource, JaegerQuery>;
|
||||
|
||||
export function QueryEditor({ datasource, query, onChange, onRunQuery }: Props) {
|
||||
const [uploadModalOpen, setUploadModalOpen] = useState(false);
|
||||
const theme = useTheme2();
|
||||
const styles = useStyles2(getStyles);
|
||||
|
||||
const onChangeQuery = (value: string) => {
|
||||
@@ -23,18 +36,6 @@ export function QueryEditor({ datasource, query, onChange, onRunQuery }: Props)
|
||||
switch (query.queryType) {
|
||||
case 'search':
|
||||
return <SearchForm datasource={datasource} query={query} onChange={onChange} />;
|
||||
case 'upload':
|
||||
return (
|
||||
<div className={styles.fileDropzoneContainer}>
|
||||
<FileDropzone
|
||||
options={{ multiple: false }}
|
||||
onLoad={(result) => {
|
||||
datasource.uploadedJson = result;
|
||||
onRunQuery();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<InlineFieldRow>
|
||||
@@ -54,24 +55,50 @@ export function QueryEditor({ datasource, query, onChange, onRunQuery }: Props)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal title={'Upload trace'} isOpen={uploadModalOpen} onDismiss={() => setUploadModalOpen(false)}>
|
||||
<div className={css({ padding: theme.spacing(2) })}>
|
||||
<FileDropzone
|
||||
options={{ multiple: false }}
|
||||
onLoad={(result) => {
|
||||
datasource.uploadedJson = result;
|
||||
onChange({
|
||||
...query,
|
||||
queryType: 'upload',
|
||||
});
|
||||
setUploadModalOpen(false);
|
||||
onRunQuery();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
<div className={styles.container}>
|
||||
<InlineFieldRow>
|
||||
<InlineField label="Query type">
|
||||
<RadioButtonGroup<JaegerQueryType>
|
||||
options={[
|
||||
{ value: 'search', label: 'Search' },
|
||||
{ value: undefined, label: 'TraceID' },
|
||||
{ value: 'upload', label: 'JSON File' },
|
||||
]}
|
||||
value={query.queryType}
|
||||
onChange={(v) =>
|
||||
onChange({
|
||||
...query,
|
||||
queryType: v,
|
||||
})
|
||||
}
|
||||
size="md"
|
||||
/>
|
||||
<InlineField label="Query type" grow={true}>
|
||||
<HorizontalGroup spacing={'sm'} align={'center'} justify={'space-between'}>
|
||||
<RadioButtonGroup<JaegerQueryType>
|
||||
options={[
|
||||
{ value: 'search', label: 'Search' },
|
||||
{ value: undefined, label: 'TraceID' },
|
||||
]}
|
||||
value={query.queryType}
|
||||
onChange={(v) =>
|
||||
onChange({
|
||||
...query,
|
||||
queryType: v,
|
||||
})
|
||||
}
|
||||
size="md"
|
||||
/>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setUploadModalOpen(true);
|
||||
}}
|
||||
>
|
||||
Import trace
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
</InlineField>
|
||||
</InlineFieldRow>
|
||||
{renderEditorBody()}
|
||||
@@ -80,11 +107,8 @@ export function QueryEditor({ datasource, query, onChange, onRunQuery }: Props)
|
||||
);
|
||||
}
|
||||
|
||||
const getStyles = (theme: GrafanaTheme2) => ({
|
||||
const getStyles = () => ({
|
||||
container: css`
|
||||
width: 100%;
|
||||
`,
|
||||
fileDropzoneContainer: css`
|
||||
padding: ${theme.spacing(2)};
|
||||
`,
|
||||
});
|
||||
|
||||
@@ -5,10 +5,13 @@ import useAsync from 'react-use/lib/useAsync';
|
||||
import { QueryEditorProps, SelectableValue } from '@grafana/data';
|
||||
import { config, reportInteraction } from '@grafana/runtime';
|
||||
import {
|
||||
Button,
|
||||
FileDropzone,
|
||||
HorizontalGroup,
|
||||
InlineField,
|
||||
InlineFieldRow,
|
||||
InlineLabel,
|
||||
Modal,
|
||||
RadioButtonGroup,
|
||||
Themeable2,
|
||||
withTheme2,
|
||||
@@ -28,12 +31,18 @@ import { ServiceGraphSection } from './ServiceGraphSection';
|
||||
import { getDS } from './utils';
|
||||
|
||||
interface Props extends QueryEditorProps<TempoDatasource, TempoQuery>, Themeable2 {}
|
||||
interface State {
|
||||
uploadModalOpen: boolean;
|
||||
}
|
||||
|
||||
const DEFAULT_QUERY_TYPE: TempoQueryType = config.featureToggles.traceqlSearch ? 'traceqlSearch' : 'traceql';
|
||||
|
||||
class TempoQueryFieldComponent extends React.PureComponent<Props> {
|
||||
class TempoQueryFieldComponent extends React.PureComponent<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
uploadModalOpen: false,
|
||||
};
|
||||
}
|
||||
|
||||
// Set the default query type when the component mounts.
|
||||
@@ -80,7 +89,6 @@ class TempoQueryFieldComponent extends React.PureComponent<Props> {
|
||||
|
||||
let queryTypeOptions: Array<SelectableValue<TempoQueryType>> = [
|
||||
{ value: 'traceql', label: 'TraceQL' },
|
||||
{ value: 'upload', label: 'JSON File' },
|
||||
{ value: 'serviceMap', label: 'Service Graph' },
|
||||
];
|
||||
|
||||
@@ -104,29 +112,60 @@ class TempoQueryFieldComponent extends React.PureComponent<Props> {
|
||||
|
||||
return (
|
||||
<>
|
||||
<InlineFieldRow>
|
||||
<InlineField label="Query type">
|
||||
<RadioButtonGroup<TempoQueryType>
|
||||
options={queryTypeOptions}
|
||||
value={query.queryType}
|
||||
onChange={(v) => {
|
||||
reportInteraction('grafana_traces_query_type_changed', {
|
||||
datasourceType: 'tempo',
|
||||
app: app ?? '',
|
||||
grafana_version: config.buildInfo.version,
|
||||
newQueryType: v,
|
||||
previousQueryType: query.queryType ?? '',
|
||||
});
|
||||
|
||||
this.onClearResults();
|
||||
|
||||
<Modal
|
||||
title={'Upload trace'}
|
||||
isOpen={this.state.uploadModalOpen}
|
||||
onDismiss={() => this.setState({ uploadModalOpen: false })}
|
||||
>
|
||||
<div className={css({ padding: this.props.theme.spacing(2) })}>
|
||||
<FileDropzone
|
||||
options={{ multiple: false }}
|
||||
onLoad={(result) => {
|
||||
this.props.datasource.uploadedJson = result;
|
||||
onChange({
|
||||
...query,
|
||||
queryType: v,
|
||||
queryType: 'upload',
|
||||
});
|
||||
this.setState({ uploadModalOpen: false });
|
||||
this.props.onRunQuery();
|
||||
}}
|
||||
size="md"
|
||||
/>
|
||||
</div>
|
||||
</Modal>
|
||||
<InlineFieldRow>
|
||||
<InlineField label="Query type" grow={true}>
|
||||
<HorizontalGroup spacing={'sm'} align={'center'} justify={'space-between'}>
|
||||
<RadioButtonGroup<TempoQueryType>
|
||||
options={queryTypeOptions}
|
||||
value={query.queryType}
|
||||
onChange={(v) => {
|
||||
reportInteraction('grafana_traces_query_type_changed', {
|
||||
datasourceType: 'tempo',
|
||||
app: app ?? '',
|
||||
grafana_version: config.buildInfo.version,
|
||||
newQueryType: v,
|
||||
previousQueryType: query.queryType ?? '',
|
||||
});
|
||||
|
||||
this.onClearResults();
|
||||
|
||||
onChange({
|
||||
...query,
|
||||
queryType: v,
|
||||
});
|
||||
}}
|
||||
size="md"
|
||||
/>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
this.setState({ uploadModalOpen: true });
|
||||
}}
|
||||
>
|
||||
Import trace
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
</InlineField>
|
||||
</InlineFieldRow>
|
||||
{query.queryType === 'search' && (
|
||||
@@ -154,17 +193,6 @@ class TempoQueryFieldComponent extends React.PureComponent<Props> {
|
||||
onBlur={this.props.onBlur}
|
||||
/>
|
||||
)}
|
||||
{query.queryType === 'upload' && (
|
||||
<div className={css({ padding: this.props.theme.spacing(2) })}>
|
||||
<FileDropzone
|
||||
options={{ multiple: false }}
|
||||
onLoad={(result) => {
|
||||
this.props.datasource.uploadedJson = result;
|
||||
this.props.onRunQuery();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{query.queryType === 'serviceMap' && (
|
||||
<ServiceGraphSection graphDatasourceUid={graphDatasourceUid} query={query} onChange={onChange} />
|
||||
)}
|
||||
|
||||
@@ -15,7 +15,7 @@ describe('QueryField', () => {
|
||||
<ZipkinQueryField
|
||||
history={[]}
|
||||
datasource={ds}
|
||||
query={{ query: '1234' } as ZipkinQuery}
|
||||
query={{ query: '1234', queryType: 'traceID' } as ZipkinQuery}
|
||||
onRunQuery={() => {}}
|
||||
onChange={() => {}}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { css } from '@emotion/css';
|
||||
import { fromPairs } from 'lodash';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useAsyncFn, useMount, useMountedState } from 'react-use';
|
||||
import { AsyncState } from 'react-use/lib/useAsyncFn';
|
||||
|
||||
@@ -15,6 +15,9 @@ import {
|
||||
useTheme2,
|
||||
QueryField,
|
||||
useStyles2,
|
||||
Modal,
|
||||
HorizontalGroup,
|
||||
Button,
|
||||
} from '@grafana/ui';
|
||||
import { notifyApp } from 'app/core/actions';
|
||||
import { createErrorNotification } from 'app/core/copy/appNotification';
|
||||
@@ -36,6 +39,7 @@ const getStyles = (theme: GrafanaTheme2) => {
|
||||
};
|
||||
|
||||
export const ZipkinQueryField = ({ query, onChange, onRunQuery, datasource }: Props) => {
|
||||
const [uploadModalOpen, setUploadModalOpen] = useState(false);
|
||||
const serviceOptions = useServices(datasource);
|
||||
const theme = useTheme2();
|
||||
const styles = useStyles2(getStyles);
|
||||
@@ -52,6 +56,15 @@ export const ZipkinQueryField = ({ query, onChange, onRunQuery, datasource }: Pr
|
||||
[onChange, onRunQuery, query]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!query.queryType) {
|
||||
onChange({
|
||||
...query,
|
||||
queryType: 'traceID',
|
||||
});
|
||||
}
|
||||
}, [query, onChange]);
|
||||
|
||||
const onChangeQuery = (value: string) => {
|
||||
const nextQuery = { ...query, query: value };
|
||||
onChange(nextQuery);
|
||||
@@ -61,35 +74,49 @@ export const ZipkinQueryField = ({ query, onChange, onRunQuery, datasource }: Pr
|
||||
|
||||
return (
|
||||
<>
|
||||
<InlineFieldRow>
|
||||
<InlineField label="Query type">
|
||||
<RadioButtonGroup<ZipkinQueryType>
|
||||
options={[
|
||||
{ value: 'traceID', label: 'TraceID' },
|
||||
{ value: 'upload', label: 'JSON File' },
|
||||
]}
|
||||
value={query.queryType || 'traceID'}
|
||||
onChange={(v) =>
|
||||
onChange({
|
||||
...query,
|
||||
queryType: v,
|
||||
})
|
||||
}
|
||||
size="md"
|
||||
/>
|
||||
</InlineField>
|
||||
</InlineFieldRow>
|
||||
{query.queryType === 'upload' ? (
|
||||
<Modal title={'Upload trace'} isOpen={uploadModalOpen} onDismiss={() => setUploadModalOpen(false)}>
|
||||
<div className={css({ padding: theme.spacing(2) })}>
|
||||
<FileDropzone
|
||||
options={{ multiple: false }}
|
||||
onLoad={(result) => {
|
||||
datasource.uploadedJson = result;
|
||||
onChange({
|
||||
...query,
|
||||
queryType: 'upload',
|
||||
});
|
||||
setUploadModalOpen(false);
|
||||
onRunQuery();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
</Modal>
|
||||
<InlineFieldRow>
|
||||
<InlineField label="Query type" grow={true}>
|
||||
<HorizontalGroup spacing={'sm'} align={'center'} justify={'space-between'}>
|
||||
<RadioButtonGroup<ZipkinQueryType>
|
||||
options={[{ value: 'traceID', label: 'TraceID' }]}
|
||||
value={query.queryType || 'traceID'}
|
||||
onChange={(v) =>
|
||||
onChange({
|
||||
...query,
|
||||
queryType: v,
|
||||
})
|
||||
}
|
||||
size="md"
|
||||
/>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
setUploadModalOpen(true);
|
||||
}}
|
||||
>
|
||||
Import trace
|
||||
</Button>
|
||||
</HorizontalGroup>
|
||||
</InlineField>
|
||||
</InlineFieldRow>
|
||||
{query.queryType === 'traceID' && (
|
||||
<InlineFieldRow>
|
||||
<ButtonCascader
|
||||
options={cascaderOptions}
|
||||
|
||||
Reference in New Issue
Block a user