diff --git a/public/app/plugins/datasource/loki/components/AnnotationsQueryEditor.tsx b/public/app/plugins/datasource/loki/components/AnnotationsQueryEditor.tsx index ba84fa6f830..e401eb8bcc6 100644 --- a/public/app/plugins/datasource/loki/components/AnnotationsQueryEditor.tsx +++ b/public/app/plugins/datasource/loki/components/AnnotationsQueryEditor.tsx @@ -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 (
- {}} + onBlur={() => {}} history={[]} - absoluteRange={absolute} + absoluteRange={absoluteRange} + ExtraFieldElement={ + {}} + onChange={onChange} + /> + } />
); diff --git a/public/app/plugins/datasource/loki/components/LokiExploreQueryEditor.tsx b/public/app/plugins/datasource/loki/components/LokiExploreQueryEditor.tsx index 4ade8c83841..ab8e20098c4 100644 --- a/public/app/plugins/datasource/loki/components/LokiExploreQueryEditor.tsx +++ b/public/app/plugins/datasource/loki/components/LokiExploreQueryEditor.tsx @@ -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; 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 ( + } /> ); } diff --git a/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx b/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx index 67ded1bd8ad..fc1afe27570 100644 --- a/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx +++ b/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx @@ -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; 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) => { 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={ + <> + + {legendField} + + } /> ); } diff --git a/public/app/plugins/datasource/loki/components/LokiQueryField.tsx b/public/app/plugins/datasource/loki/components/LokiQueryField.tsx index 37946b487ce..bfce16b58d1 100644 --- a/public/app/plugins/datasource/loki/components/LokiQueryField.tsx +++ b/public/app/plugins/datasource/loki/components/LokiQueryField.tsx @@ -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 = (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 ; -}; +// 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 { + history: LokiHistoryItem[]; + absoluteRange: AbsoluteTimeRange; + ExtraFieldElement?: ReactNode; +} + +interface LokiQueryFieldState { + labelsLoaded: boolean; + labelBrowserVisible: boolean; +} + +export class LokiQueryField extends React.PureComponent { + 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 => { + 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 ( + <> +
+ +
+ +
+
+ {labelBrowserVisible && ( +
+ +
+ )} + + {ExtraFieldElement} + + ); + } +} diff --git a/public/app/plugins/datasource/loki/components/LokiQueryFieldForm.tsx b/public/app/plugins/datasource/loki/components/LokiQueryFieldForm.tsx deleted file mode 100644 index 1da18e0e764..00000000000 --- a/public/app/plugins/datasource/loki/components/LokiQueryFieldForm.tsx +++ /dev/null @@ -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 { - history: LokiHistoryItem[]; - absoluteRange: AbsoluteTimeRange; - ExtraFieldElement?: ReactNode; - runOnBlur?: boolean; -} - -interface LokiQueryFieldFormState { - labelsLoaded: boolean; - labelBrowserVisible: boolean; -} - -export class LokiQueryFieldForm extends React.PureComponent { - 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 => { - 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 ( - <> -
- -
- -
-
- {labelBrowserVisible && ( -
- -
- )} - - {ExtraFieldElement} - - ); - } -} diff --git a/public/app/plugins/datasource/loki/components/__snapshots__/LokiExploreQueryEditor.test.tsx.snap b/public/app/plugins/datasource/loki/components/__snapshots__/LokiExploreQueryEditor.test.tsx.snap index 5b3345352fd..4e70de00a8d 100644 --- a/public/app/plugins/datasource/loki/components/__snapshots__/LokiExploreQueryEditor.test.tsx.snap +++ b/public/app/plugins/datasource/loki/components/__snapshots__/LokiExploreQueryEditor.test.tsx.snap @@ -2,6 +2,27 @@ exports[`LokiExploreQueryEditor should render component 1`] = ` + } + 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", - } - } /> `; diff --git a/public/app/plugins/datasource/loki/components/__snapshots__/LokiQueryEditor.test.tsx.snap b/public/app/plugins/datasource/loki/components/__snapshots__/LokiQueryEditor.test.tsx.snap index c22188a19bd..a42bb5b248c 100644 --- a/public/app/plugins/datasource/loki/components/__snapshots__/LokiQueryEditor.test.tsx.snap +++ b/public/app/plugins/datasource/loki/components/__snapshots__/LokiQueryEditor.test.tsx.snap @@ -3,29 +3,51 @@ exports[`Render LokiQueryEditor with legend should render 1`] = ` + +
- - Legend - - + + Legend + + +
- +
+ } + 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`] = ` + +
- - Legend - - + + Legend + + +
- +
+ } + 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} /> `; diff --git a/public/app/plugins/datasource/prometheus/components/PromQueryEditor.tsx b/public/app/plugins/datasource/prometheus/components/PromQueryEditor.tsx index 7fdcaf24dd4..b3ed70825a2 100644 --- a/public/app/plugins/datasource/prometheus/components/PromQueryEditor.tsx +++ b/public/app/plugins/datasource/prometheus/components/PromQueryEditor.tsx @@ -110,92 +110,91 @@ export class PromQueryEditor extends PureComponent { const { formatOption, instant, interval, intervalFactorOption, legendFormat, exemplar } = this.state; return ( -
- - -
-
- +
+ - Legend - - -
- -
- - An additional lower limit for the step parameter of the Prometheus query and for the{' '} - $__interval and $__rate_interval variables. The limit is absolute and not - modified by the "Resolution" setting. - - } - > - Min step - - -
- -
-
Resolution
- - - - - + Legend + + - -
+
- -
-
+
+ + An additional lower limit for the step parameter of the Prometheus query and for the{' '} + $__interval and $__rate_interval variables. The limit is absolute and not + modified by the "Resolution" setting. + + } + > + Min step + + +
+ +
+
Resolution
+ + + + + + +
+ + + + } + /> ); } } diff --git a/public/app/plugins/datasource/prometheus/components/__snapshots__/PromQueryEditor.test.tsx.snap b/public/app/plugins/datasource/prometheus/components/__snapshots__/PromQueryEditor.test.tsx.snap index 4b6beed89eb..a3f1dffa559 100644 --- a/public/app/plugins/datasource/prometheus/components/__snapshots__/PromQueryEditor.test.tsx.snap +++ b/public/app/plugins/datasource/prometheus/components/__snapshots__/PromQueryEditor.test.tsx.snap @@ -1,197 +1,197 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Render PromQueryEditor with basic options should render 1`] = ` -
- -
+ - - Legend - - -
-
- - An additional lower limit for the step parameter of the Prometheus query and for the - - - $__interval - - and - - $__rate_interval - - variables. The limit is absolute and not modified by the "Resolution" setting. - - } - width={7} - > - Min step - - -
-
- Resolution + + Legend + +
- +
+
+
+ Resolution +
+ - - - - -
- +
+
+ Format +
+