Loki: Refactor query editor so query field can be used independently (#33276)

* Refactor Loki so LokiQueryField can be used independently

* Refactor PromQueryEditor
This commit is contained in:
Ivana Huckova 2021-04-23 10:08:23 +02:00 committed by GitHub
parent c809d63065
commit c32c682f81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 590 additions and 541 deletions

View File

@ -1,9 +1,9 @@
// Libraries
import React, { memo } from 'react';
// Types
import { LokiQuery } from '../types';
import { LokiQueryFieldForm } from './LokiQueryFieldForm';
import { LokiQueryField } from './LokiQueryField';
import { LokiOptionFields } from './LokiOptionFields';
import LokiDatasource from '../datasource';
interface Props {
@ -18,7 +18,7 @@ export const LokiAnnotationsQueryEditor = memo(function LokiAnnotationQueryEdito
const { expr, maxLines, instant, datasource, onChange } = props;
// Timerange to get existing labels from. Hard coding like this seems to be good enough right now.
const absolute = {
const absoluteRange = {
from: Date.now() - 10000,
to: Date.now(),
};
@ -31,13 +31,23 @@ export const LokiAnnotationsQueryEditor = memo(function LokiAnnotationQueryEdito
};
return (
<div className="gf-form-group">
<LokiQueryFieldForm
<LokiQueryField
datasource={datasource}
query={queryWithRefId}
onChange={onChange}
onRunQuery={() => {}}
onBlur={() => {}}
history={[]}
absoluteRange={absolute}
absoluteRange={absoluteRange}
ExtraFieldElement={
<LokiOptionFields
queryType={queryWithRefId.instant ? 'instant' : 'range'}
lineLimitValue={queryWithRefId?.maxLines?.toString() || ''}
query={queryWithRefId}
onRunQuery={() => {}}
onChange={onChange}
/>
}
/>
</div>
);

View File

@ -6,11 +6,13 @@ import { ExploreQueryFieldProps } from '@grafana/data';
import { LokiDatasource } from '../datasource';
import { LokiQuery, LokiOptions } from '../types';
import { LokiQueryField } from './LokiQueryField';
import { LokiOptionFields } from './LokiOptionFields';
type Props = ExploreQueryFieldProps<LokiDatasource, LokiQuery, LokiOptions>;
export function LokiExploreQueryEditor(props: Props) {
const { range, query, data, datasource, history, onChange, onRunQuery } = props;
const absoluteTimeRange = { from: range!.from!.valueOf(), to: range!.to!.valueOf() }; // Range here is never optional
return (
<LokiQueryField
@ -21,7 +23,16 @@ export function LokiExploreQueryEditor(props: Props) {
onRunQuery={onRunQuery}
history={history}
data={data}
range={range}
absoluteRange={absoluteTimeRange}
ExtraFieldElement={
<LokiOptionFields
queryType={query.instant ? 'instant' : 'range'}
lineLimitValue={query?.maxLines?.toString() || ''}
query={query}
onRunQuery={onRunQuery}
onChange={onChange}
/>
}
/>
);
}

View File

@ -7,11 +7,13 @@ import { InlineFormLabel } from '@grafana/ui';
import { LokiDatasource } from '../datasource';
import { LokiQuery, LokiOptions } from '../types';
import { LokiQueryField } from './LokiQueryField';
import { LokiOptionFields } from './LokiOptionFields';
type Props = QueryEditorProps<LokiDatasource, LokiQuery, LokiOptions>;
export function LokiQueryEditor(props: Props) {
const { range, query, data, datasource, onChange, onRunQuery } = props;
const absoluteTimeRange = { from: range!.from!.valueOf(), to: range!.to!.valueOf() }; // Range here is never optional
const onLegendChange = (e: React.SyntheticEvent<HTMLInputElement>) => {
const nextQuery = { ...query, legendFormat: e.currentTarget.value };
@ -49,9 +51,20 @@ export function LokiQueryEditor(props: Props) {
onBlur={onRunQuery}
history={[]}
data={data}
range={range}
runOnBlur={true}
ExtraFieldElement={legendField}
absoluteRange={absoluteTimeRange}
ExtraFieldElement={
<>
<LokiOptionFields
queryType={query.instant ? 'instant' : 'range'}
lineLimitValue={query?.maxLines?.toString() || ''}
query={query}
onRunQuery={onRunQuery}
onChange={onChange}
runOnBlur={true}
/>
{legendField}
</>
}
/>
);
}

View File

@ -1,16 +1,185 @@
import React, { FunctionComponent } from 'react';
import { LokiQueryFieldForm, LokiQueryFieldFormProps } from './LokiQueryFieldForm';
// Libraries
import React, { ReactNode } from 'react';
type LokiQueryFieldProps = Omit<
LokiQueryFieldFormProps,
'labelsLoaded' | 'onLoadOptions' | 'onLabelsRefresh' | 'absoluteRange'
>;
import {
SlatePrism,
TypeaheadOutput,
SuggestionsState,
QueryField,
TypeaheadInput,
BracesPlugin,
DOMUtil,
Icon,
} from '@grafana/ui';
export const LokiQueryField: FunctionComponent<LokiQueryFieldProps> = (props) => {
const { datasource, range, ...otherProps } = props;
const absoluteTimeRange = { from: range!.from!.valueOf(), to: range!.to!.valueOf() }; // Range here is never optional
// Utils & Services
// dom also includes Element polyfills
import { Plugin, Node } from 'slate';
import { LokiLabelBrowser } from './LokiLabelBrowser';
return <LokiQueryFieldForm datasource={datasource} absoluteRange={absoluteTimeRange} {...otherProps} />;
};
// Types
import { ExploreQueryFieldProps, AbsoluteTimeRange } from '@grafana/data';
import { LokiQuery, LokiOptions } from '../types';
import { LanguageMap, languages as prismLanguages } from 'prismjs';
import LokiLanguageProvider, { LokiHistoryItem } from '../language_provider';
import LokiDatasource from '../datasource';
export default LokiQueryField;
function getChooserText(hasSyntax: boolean, hasLogLabels: boolean) {
if (!hasSyntax) {
return 'Loading labels...';
}
if (!hasLogLabels) {
return '(No logs found)';
}
return 'Log browser';
}
function willApplySuggestion(suggestion: string, { typeaheadContext, typeaheadText }: SuggestionsState): string {
// Modify suggestion based on context
switch (typeaheadContext) {
case 'context-labels': {
const nextChar = DOMUtil.getNextCharacter();
if (!nextChar || nextChar === '}' || nextChar === ',') {
suggestion += '=';
}
break;
}
case 'context-label-values': {
// Always add quotes and remove existing ones instead
if (!typeaheadText.match(/^(!?=~?"|")/)) {
suggestion = `"${suggestion}`;
}
if (DOMUtil.getNextCharacter() !== '"') {
suggestion = `${suggestion}"`;
}
break;
}
default:
}
return suggestion;
}
export interface LokiQueryFieldProps extends ExploreQueryFieldProps<LokiDatasource, LokiQuery, LokiOptions> {
history: LokiHistoryItem[];
absoluteRange: AbsoluteTimeRange;
ExtraFieldElement?: ReactNode;
}
interface LokiQueryFieldState {
labelsLoaded: boolean;
labelBrowserVisible: boolean;
}
export class LokiQueryField extends React.PureComponent<LokiQueryFieldProps, LokiQueryFieldState> {
plugins: Plugin[];
constructor(props: LokiQueryFieldProps) {
super(props);
this.state = { labelsLoaded: false, labelBrowserVisible: false };
this.plugins = [
BracesPlugin(),
SlatePrism(
{
onlyIn: (node: Node) => node.object === 'block' && node.type === 'code_block',
getSyntax: (node: Node) => 'logql',
},
{ ...(prismLanguages as LanguageMap), logql: this.props.datasource.languageProvider.getSyntax() }
),
];
}
async componentDidUpdate() {
await this.props.datasource.languageProvider.start();
this.setState({ labelsLoaded: true });
}
onChangeLogLabels = (selector: string) => {
this.onChangeQuery(selector, true);
this.setState({ labelBrowserVisible: false });
};
onChangeQuery = (value: string, override?: boolean) => {
// Send text change to parent
const { query, onChange, onRunQuery } = this.props;
if (onChange) {
const nextQuery = { ...query, expr: value };
onChange(nextQuery);
if (override && onRunQuery) {
onRunQuery();
}
}
};
onClickChooserButton = () => {
this.setState((state) => ({ labelBrowserVisible: !state.labelBrowserVisible }));
};
onTypeahead = async (typeahead: TypeaheadInput): Promise<TypeaheadOutput> => {
const { datasource } = this.props;
if (!datasource.languageProvider) {
return { suggestions: [] };
}
const lokiLanguageProvider = datasource.languageProvider as LokiLanguageProvider;
const { history } = this.props;
const { prefix, text, value, wrapperClasses, labelKey } = typeahead;
const result = await lokiLanguageProvider.provideCompletionItems(
{ text, value, prefix, wrapperClasses, labelKey },
{ history }
);
return result;
};
render() {
const { ExtraFieldElement, query, datasource } = this.props;
const { labelsLoaded, labelBrowserVisible } = this.state;
const lokiLanguageProvider = datasource.languageProvider as LokiLanguageProvider;
const cleanText = datasource.languageProvider ? lokiLanguageProvider.cleanText : undefined;
const hasLogLabels = lokiLanguageProvider.getLabelKeys().length > 0;
const chooserText = getChooserText(labelsLoaded, hasLogLabels);
const buttonDisabled = !(labelsLoaded && hasLogLabels);
return (
<>
<div className="gf-form-inline gf-form-inline--xs-view-flex-column flex-grow-1">
<button
className="gf-form-label query-keyword pointer"
onClick={this.onClickChooserButton}
disabled={buttonDisabled}
>
{chooserText}
<Icon name={labelBrowserVisible ? 'angle-down' : 'angle-right'} />
</button>
<div className="gf-form gf-form--grow flex-shrink-1 min-width-15">
<QueryField
additionalPlugins={this.plugins}
cleanText={cleanText}
query={query.expr}
onTypeahead={this.onTypeahead}
onWillApplySuggestion={willApplySuggestion}
onChange={this.onChangeQuery}
onBlur={this.props.onBlur}
onRunQuery={this.props.onRunQuery}
placeholder="Enter a Loki query (run with Shift+Enter)"
portalOrigin="loki"
/>
</div>
</div>
{labelBrowserVisible && (
<div className="gf-form">
<LokiLabelBrowser languageProvider={lokiLanguageProvider} onChange={this.onChangeLogLabels} />
</div>
)}
{ExtraFieldElement}
</>
);
}
}

View File

@ -1,194 +0,0 @@
// Libraries
import React, { ReactNode } from 'react';
import {
SlatePrism,
TypeaheadOutput,
SuggestionsState,
QueryField,
TypeaheadInput,
BracesPlugin,
DOMUtil,
Icon,
} from '@grafana/ui';
// Utils & Services
// dom also includes Element polyfills
import { Plugin, Node } from 'slate';
import { LokiLabelBrowser } from './LokiLabelBrowser';
// Types
import { ExploreQueryFieldProps, AbsoluteTimeRange } from '@grafana/data';
import { LokiQuery, LokiOptions } from '../types';
import { LanguageMap, languages as prismLanguages } from 'prismjs';
import LokiLanguageProvider, { LokiHistoryItem } from '../language_provider';
import LokiDatasource from '../datasource';
import LokiOptionFields from './LokiOptionFields';
function getChooserText(hasSyntax: boolean, hasLogLabels: boolean) {
if (!hasSyntax) {
return 'Loading labels...';
}
if (!hasLogLabels) {
return '(No logs found)';
}
return 'Log browser';
}
function willApplySuggestion(suggestion: string, { typeaheadContext, typeaheadText }: SuggestionsState): string {
// Modify suggestion based on context
switch (typeaheadContext) {
case 'context-labels': {
const nextChar = DOMUtil.getNextCharacter();
if (!nextChar || nextChar === '}' || nextChar === ',') {
suggestion += '=';
}
break;
}
case 'context-label-values': {
// Always add quotes and remove existing ones instead
if (!typeaheadText.match(/^(!?=~?"|")/)) {
suggestion = `"${suggestion}`;
}
if (DOMUtil.getNextCharacter() !== '"') {
suggestion = `${suggestion}"`;
}
break;
}
default:
}
return suggestion;
}
export interface LokiQueryFieldFormProps extends ExploreQueryFieldProps<LokiDatasource, LokiQuery, LokiOptions> {
history: LokiHistoryItem[];
absoluteRange: AbsoluteTimeRange;
ExtraFieldElement?: ReactNode;
runOnBlur?: boolean;
}
interface LokiQueryFieldFormState {
labelsLoaded: boolean;
labelBrowserVisible: boolean;
}
export class LokiQueryFieldForm extends React.PureComponent<LokiQueryFieldFormProps, LokiQueryFieldFormState> {
plugins: Plugin[];
constructor(props: LokiQueryFieldFormProps) {
super(props);
this.state = { labelsLoaded: false, labelBrowserVisible: false };
this.plugins = [
BracesPlugin(),
SlatePrism(
{
onlyIn: (node: Node) => node.object === 'block' && node.type === 'code_block',
getSyntax: (node: Node) => 'logql',
},
{ ...(prismLanguages as LanguageMap), logql: this.props.datasource.languageProvider.getSyntax() }
),
];
}
async componentDidUpdate() {
await this.props.datasource.languageProvider.start();
this.setState({ labelsLoaded: true });
}
onChangeLogLabels = (selector: string) => {
this.onChangeQuery(selector, true);
this.setState({ labelBrowserVisible: false });
};
onChangeQuery = (value: string, override?: boolean) => {
// Send text change to parent
const { query, onChange, onRunQuery } = this.props;
if (onChange) {
const nextQuery = { ...query, expr: value };
onChange(nextQuery);
if (override && onRunQuery) {
onRunQuery();
}
}
};
onClickChooserButton = () => {
this.setState((state) => ({ labelBrowserVisible: !state.labelBrowserVisible }));
};
onTypeahead = async (typeahead: TypeaheadInput): Promise<TypeaheadOutput> => {
const { datasource } = this.props;
if (!datasource.languageProvider) {
return { suggestions: [] };
}
const lokiLanguageProvider = datasource.languageProvider as LokiLanguageProvider;
const { history } = this.props;
const { prefix, text, value, wrapperClasses, labelKey } = typeahead;
const result = await lokiLanguageProvider.provideCompletionItems(
{ text, value, prefix, wrapperClasses, labelKey },
{ history }
);
return result;
};
render() {
const { ExtraFieldElement, query, datasource, runOnBlur } = this.props;
const { labelsLoaded, labelBrowserVisible } = this.state;
const lokiLanguageProvider = datasource.languageProvider as LokiLanguageProvider;
const cleanText = datasource.languageProvider ? lokiLanguageProvider.cleanText : undefined;
const hasLogLabels = lokiLanguageProvider.getLabelKeys().length > 0;
const chooserText = getChooserText(labelsLoaded, hasLogLabels);
const buttonDisabled = !(labelsLoaded && hasLogLabels);
return (
<>
<div className="gf-form-inline gf-form-inline--xs-view-flex-column flex-grow-1">
<button
className="gf-form-label query-keyword pointer"
onClick={this.onClickChooserButton}
disabled={buttonDisabled}
>
{chooserText}
<Icon name={labelBrowserVisible ? 'angle-down' : 'angle-right'} />
</button>
<div className="gf-form gf-form--grow flex-shrink-1 min-width-15">
<QueryField
additionalPlugins={this.plugins}
cleanText={cleanText}
query={query.expr}
onTypeahead={this.onTypeahead}
onWillApplySuggestion={willApplySuggestion}
onChange={this.onChangeQuery}
onBlur={this.props.onBlur}
onRunQuery={this.props.onRunQuery}
placeholder="Enter a Loki query (run with Shift+Enter)"
portalOrigin="loki"
/>
</div>
</div>
{labelBrowserVisible && (
<div className="gf-form">
<LokiLabelBrowser languageProvider={lokiLanguageProvider} onChange={this.onChangeLogLabels} />
</div>
)}
<LokiOptionFields
queryType={query.instant ? 'instant' : 'range'}
lineLimitValue={query?.maxLines?.toString() || ''}
query={query}
onRunQuery={this.props.onRunQuery}
onChange={this.props.onChange}
runOnBlur={runOnBlur}
/>
{ExtraFieldElement}
</>
);
}
}

View File

@ -2,6 +2,27 @@
exports[`LokiExploreQueryEditor should render component 1`] = `
<LokiQueryField
ExtraFieldElement={
<LokiOptionFields
lineLimitValue="0"
onChange={[MockFunction]}
onRunQuery={[MockFunction]}
query={
Object {
"expr": "",
"maxLines": 0,
"refId": "A",
}
}
queryType="range"
/>
}
absoluteRange={
Object {
"from": 1577836800000,
"to": 1577923200000,
}
}
data={
Object {
"request": Object {
@ -98,15 +119,5 @@ exports[`LokiExploreQueryEditor should render component 1`] = `
"refId": "A",
}
}
range={
Object {
"from": "2020-01-01T00:00:00.000Z",
"raw": Object {
"from": "2020-01-01T00:00:00.000Z",
"to": "2020-01-02T00:00:00.000Z",
},
"to": "2020-01-02T00:00:00.000Z",
}
}
/>
`;

View File

@ -3,29 +3,51 @@
exports[`Render LokiQueryEditor with legend should render 1`] = `
<LokiQueryField
ExtraFieldElement={
<div
className="gf-form-inline"
>
<React.Fragment>
<LokiOptionFields
lineLimitValue=""
onChange={[MockFunction]}
onRunQuery={[MockFunction]}
query={
Object {
"expr": "",
"legendFormat": "My Legend",
"refId": "A",
}
}
queryType="range"
runOnBlur={true}
/>
<div
className="gf-form"
className="gf-form-inline"
>
<FormLabel
tooltip="Controls the name of the time series, using name or pattern. For example
{{hostname}} will be replaced with label value for the label hostname. The legend only applies to metric queries."
width={6}
<div
className="gf-form"
>
Legend
</FormLabel>
<input
className="gf-form-input"
onBlur={[MockFunction]}
onChange={[Function]}
placeholder="legend format"
type="text"
value="My Legend"
/>
<FormLabel
tooltip="Controls the name of the time series, using name or pattern. For example
{{hostname}} will be replaced with label value for the label hostname. The legend only applies to metric queries."
width={6}
>
Legend
</FormLabel>
<input
className="gf-form-input"
onBlur={[MockFunction]}
onChange={[Function]}
placeholder="legend format"
type="text"
value="My Legend"
/>
</div>
</div>
</div>
</React.Fragment>
}
absoluteRange={
Object {
"from": 1577836800000,
"to": 1577923200000,
}
}
datasource={Object {}}
history={Array []}
@ -39,42 +61,57 @@ exports[`Render LokiQueryEditor with legend should render 1`] = `
"refId": "A",
}
}
range={
Object {
"from": "2020-01-01T00:00:00.000Z",
"to": "2020-01-02T00:00:00.000Z",
}
}
runOnBlur={true}
/>
`;
exports[`Render LokiQueryEditor with legend should update timerange 1`] = `
<LokiQueryField
ExtraFieldElement={
<div
className="gf-form-inline"
>
<React.Fragment>
<LokiOptionFields
lineLimitValue=""
onChange={[MockFunction]}
onRunQuery={[MockFunction]}
query={
Object {
"expr": "",
"legendFormat": "My Legend",
"refId": "A",
}
}
queryType="range"
runOnBlur={true}
/>
<div
className="gf-form"
className="gf-form-inline"
>
<FormLabel
tooltip="Controls the name of the time series, using name or pattern. For example
{{hostname}} will be replaced with label value for the label hostname. The legend only applies to metric queries."
width={6}
<div
className="gf-form"
>
Legend
</FormLabel>
<input
className="gf-form-input"
onBlur={[MockFunction]}
onChange={[Function]}
placeholder="legend format"
type="text"
value="My Legend"
/>
<FormLabel
tooltip="Controls the name of the time series, using name or pattern. For example
{{hostname}} will be replaced with label value for the label hostname. The legend only applies to metric queries."
width={6}
>
Legend
</FormLabel>
<input
className="gf-form-input"
onBlur={[MockFunction]}
onChange={[Function]}
placeholder="legend format"
type="text"
value="My Legend"
/>
</div>
</div>
</div>
</React.Fragment>
}
absoluteRange={
Object {
"from": 1546300800000,
"to": 1577923200000,
}
}
datasource={Object {}}
history={Array []}
@ -88,12 +125,5 @@ exports[`Render LokiQueryEditor with legend should update timerange 1`] = `
"refId": "A",
}
}
range={
Object {
"from": "2019-01-01T00:00:00.000Z",
"to": "2020-01-02T00:00:00.000Z",
}
}
runOnBlur={true}
/>
`;

View File

@ -110,92 +110,91 @@ export class PromQueryEditor extends PureComponent<Props, State> {
const { formatOption, instant, interval, intervalFactorOption, legendFormat, exemplar } = this.state;
return (
<div>
<PromQueryField
datasource={datasource}
query={query}
range={range}
onRunQuery={this.onRunQuery}
onChange={this.onFieldChange}
history={[]}
data={data}
/>
<div className="gf-form-inline">
<div className="gf-form">
<InlineFormLabel
width={7}
tooltip="Controls the name of the time series, using name or pattern. For example
<PromQueryField
datasource={datasource}
query={query}
range={range}
onRunQuery={this.onRunQuery}
onChange={this.onFieldChange}
history={[]}
data={data}
ExtraFieldElement={
<div className="gf-form-inline">
<div className="gf-form">
<InlineFormLabel
width={7}
tooltip="Controls the name of the time series, using name or pattern. For example
{{hostname}} will be replaced with label value for the label hostname."
>
Legend
</InlineFormLabel>
<input
type="text"
className="gf-form-input"
placeholder="legend format"
value={legendFormat}
onChange={this.onLegendChange}
onBlur={this.onRunQuery}
/>
</div>
<div className="gf-form">
<InlineFormLabel
width={7}
tooltip={
<>
An additional lower limit for the step parameter of the Prometheus query and for the{' '}
<code>$__interval</code> and <code>$__rate_interval</code> variables. The limit is absolute and not
modified by the &quot;Resolution&quot; setting.
</>
}
>
Min step
</InlineFormLabel>
<input
type="text"
className="gf-form-input width-8"
placeholder={interval}
onChange={this.onIntervalChange}
onBlur={this.onRunQuery}
value={interval}
/>
</div>
<div className="gf-form">
<div className="gf-form-label">Resolution</div>
<Select
isSearchable={false}
options={INTERVAL_FACTOR_OPTIONS}
onChange={this.onIntervalFactorChange}
value={intervalFactorOption}
/>
</div>
<div className="gf-form">
<div className="gf-form-label width-7">Format</div>
<Select
width={16}
isSearchable={false}
options={FORMAT_OPTIONS}
onChange={this.onFormatChange}
value={formatOption}
/>
<Switch label="Instant" checked={instant} onChange={this.onInstantChange} />
<InlineFormLabel width={10} tooltip="Link to Graph in Prometheus">
<PromLink
datasource={datasource}
query={this.query} // Use modified query
panelData={data}
>
Legend
</InlineFormLabel>
<input
type="text"
className="gf-form-input"
placeholder="legend format"
value={legendFormat}
onChange={this.onLegendChange}
onBlur={this.onRunQuery}
/>
</InlineFormLabel>
</div>
</div>
<PromExemplarField isEnabled={exemplar} onChange={this.onExemplarChange} datasource={datasource} />
</div>
</div>
<div className="gf-form">
<InlineFormLabel
width={7}
tooltip={
<>
An additional lower limit for the step parameter of the Prometheus query and for the{' '}
<code>$__interval</code> and <code>$__rate_interval</code> variables. The limit is absolute and not
modified by the &quot;Resolution&quot; setting.
</>
}
>
Min step
</InlineFormLabel>
<input
type="text"
className="gf-form-input width-8"
placeholder={interval}
onChange={this.onIntervalChange}
onBlur={this.onRunQuery}
value={interval}
/>
</div>
<div className="gf-form">
<div className="gf-form-label">Resolution</div>
<Select
isSearchable={false}
options={INTERVAL_FACTOR_OPTIONS}
onChange={this.onIntervalFactorChange}
value={intervalFactorOption}
/>
</div>
<div className="gf-form">
<div className="gf-form-label width-7">Format</div>
<Select
width={16}
isSearchable={false}
options={FORMAT_OPTIONS}
onChange={this.onFormatChange}
value={formatOption}
/>
<Switch label="Instant" checked={instant} onChange={this.onInstantChange} />
<InlineFormLabel width={10} tooltip="Link to Graph in Prometheus">
<PromLink
datasource={datasource}
query={this.query} // Use modified query
panelData={data}
/>
</InlineFormLabel>
</div>
<PromExemplarField isEnabled={exemplar} onChange={this.onExemplarChange} datasource={datasource} />
</div>
}
/>
);
}
}

View File

@ -1,197 +1,197 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Render PromQueryEditor with basic options should render 1`] = `
<div>
<PromQueryField
datasource={
Object {
"createQuery": [MockFunction],
"getPrometheusTime": [MockFunction],
}
}
history={Array []}
onChange={[Function]}
onRunQuery={[Function]}
query={
Object {
"expr": "",
"refId": "A",
}
}
/>
<div
className="gf-form-inline"
>
<PromQueryField
ExtraFieldElement={
<div
className="gf-form"
>
<FormLabel
tooltip="Controls the name of the time series, using name or pattern. For example
{{hostname}} will be replaced with label value for the label hostname."
width={7}
>
Legend
</FormLabel>
<input
className="gf-form-input"
onBlur={[Function]}
onChange={[Function]}
placeholder="legend format"
type="text"
value=""
/>
</div>
<div
className="gf-form"
>
<FormLabel
tooltip={
<React.Fragment>
An additional lower limit for the step parameter of the Prometheus query and for the
<code>
$__interval
</code>
and
<code>
$__rate_interval
</code>
variables. The limit is absolute and not modified by the "Resolution" setting.
</React.Fragment>
}
width={7}
>
Min step
</FormLabel>
<input
className="gf-form-input width-8"
onBlur={[Function]}
onChange={[Function]}
placeholder=""
type="text"
value=""
/>
</div>
<div
className="gf-form"
className="gf-form-inline"
>
<div
className="gf-form-label"
className="gf-form"
>
Resolution
<FormLabel
tooltip="Controls the name of the time series, using name or pattern. For example
{{hostname}} will be replaced with label value for the label hostname."
width={7}
>
Legend
</FormLabel>
<input
className="gf-form-input"
onBlur={[Function]}
onChange={[Function]}
placeholder="legend format"
type="text"
value=""
/>
</div>
<Select
isSearchable={false}
onChange={[Function]}
options={
Array [
<div
className="gf-form"
>
<FormLabel
tooltip={
<React.Fragment>
An additional lower limit for the step parameter of the Prometheus query and for the
<code>
$__interval
</code>
and
<code>
$__rate_interval
</code>
variables. The limit is absolute and not modified by the "Resolution" setting.
</React.Fragment>
}
width={7}
>
Min step
</FormLabel>
<input
className="gf-form-input width-8"
onBlur={[Function]}
onChange={[Function]}
placeholder=""
type="text"
value=""
/>
</div>
<div
className="gf-form"
>
<div
className="gf-form-label"
>
Resolution
</div>
<Select
isSearchable={false}
onChange={[Function]}
options={
Array [
Object {
"label": "1/1",
"value": 1,
},
Object {
"label": "1/2",
"value": 2,
},
Object {
"label": "1/3",
"value": 3,
},
Object {
"label": "1/4",
"value": 4,
},
Object {
"label": "1/5",
"value": 5,
},
Object {
"label": "1/10",
"value": 10,
},
]
}
value={
Object {
"label": "1/1",
"value": 1,
},
Object {
"label": "1/2",
"value": 2,
},
Object {
"label": "1/3",
"value": 3,
},
Object {
"label": "1/4",
"value": 4,
},
Object {
"label": "1/5",
"value": 5,
},
Object {
"label": "1/10",
"value": 10,
},
]
}
value={
Object {
"label": "1/1",
"value": 1,
}
}
/>
</div>
<div
className="gf-form"
>
<div
className="gf-form-label width-7"
>
Format
</div>
<Select
isSearchable={false}
onChange={[Function]}
options={
Array [
Object {
"label": "Time series",
"value": "time_series",
},
Object {
"label": "Table",
"value": "table",
},
Object {
"label": "Heatmap",
"value": "heatmap",
},
]
}
value={
Object {
"label": "Time series",
"value": "time_series",
}
}
width={16}
/>
<Switch
checked={false}
label="Instant"
onChange={[Function]}
/>
<FormLabel
tooltip="Link to Graph in Prometheus"
width={10}
>
<Memo(PromLink)
datasource={
Object {
"createQuery": [MockFunction],
"getPrometheusTime": [MockFunction],
}
}
query={
Object {
"exemplar": true,
"expr": "",
"interval": "",
"legendFormat": "",
"refId": "A",
}
}
/>
</FormLabel>
</div>
<PromExemplarField
datasource={
Object {
"createQuery": [MockFunction],
"getPrometheusTime": [MockFunction],
</div>
<div
className="gf-form"
>
<div
className="gf-form-label width-7"
>
Format
</div>
<Select
isSearchable={false}
onChange={[Function]}
options={
Array [
Object {
"label": "Time series",
"value": "time_series",
},
Object {
"label": "Table",
"value": "table",
},
Object {
"label": "Heatmap",
"value": "heatmap",
},
]
}
value={
Object {
"label": "Time series",
"value": "time_series",
}
}
width={16}
/>
<Switch
checked={false}
label="Instant"
onChange={[Function]}
/>
<FormLabel
tooltip="Link to Graph in Prometheus"
width={10}
>
<Memo(PromLink)
datasource={
Object {
"createQuery": [MockFunction],
"getPrometheusTime": [MockFunction],
}
}
query={
Object {
"exemplar": true,
"expr": "",
"interval": "",
"legendFormat": "",
"refId": "A",
}
}
/>
</FormLabel>
</div>
<PromExemplarField
datasource={
Object {
"createQuery": [MockFunction],
"getPrometheusTime": [MockFunction],
}
}
}
isEnabled={true}
onChange={[Function]}
/>
</div>
</div>
isEnabled={true}
onChange={[Function]}
/>
</div>
}
datasource={
Object {
"createQuery": [MockFunction],
"getPrometheusTime": [MockFunction],
}
}
history={Array []}
onChange={[Function]}
onRunQuery={[Function]}
query={
Object {
"expr": "",
"refId": "A",
}
}
/>
`;