mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Loki: Use single string expr as a state for the visual editor (#47566)
* Loki: Use expr as state for visual editor * Loki: Use query with line filter as default for visual editor * Refactor based on feedback * fix background for query text row Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
This commit is contained in:
parent
4c99e681b1
commit
5df05e31bb
@ -7,7 +7,6 @@ import { QueryBuilderLabelFilter } from 'app/plugins/datasource/prometheus/query
|
|||||||
import { lokiQueryModeller } from '../LokiQueryModeller';
|
import { lokiQueryModeller } from '../LokiQueryModeller';
|
||||||
import { DataSourceApi, SelectableValue } from '@grafana/data';
|
import { DataSourceApi, SelectableValue } from '@grafana/data';
|
||||||
import { EditorRow } from '@grafana/experimental';
|
import { EditorRow } from '@grafana/experimental';
|
||||||
import { QueryPreview } from './QueryPreview';
|
|
||||||
import { OperationsEditorRow } from 'app/plugins/datasource/prometheus/querybuilder/shared/OperationsEditorRow';
|
import { OperationsEditorRow } from 'app/plugins/datasource/prometheus/querybuilder/shared/OperationsEditorRow';
|
||||||
import { NestedQueryList } from './NestedQueryList';
|
import { NestedQueryList } from './NestedQueryList';
|
||||||
|
|
||||||
@ -84,11 +83,6 @@ export const LokiQueryBuilder = React.memo<Props>(({ datasource, query, nested,
|
|||||||
{query.binaryQueries && query.binaryQueries.length > 0 && (
|
{query.binaryQueries && query.binaryQueries.length > 0 && (
|
||||||
<NestedQueryList query={query} datasource={datasource} onChange={onChange} onRunQuery={onRunQuery} />
|
<NestedQueryList query={query} datasource={datasource} onChange={onChange} onRunQuery={onRunQuery} />
|
||||||
)}
|
)}
|
||||||
{!nested && (
|
|
||||||
<EditorRow>
|
|
||||||
<QueryPreview query={query} />
|
|
||||||
</EditorRow>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import { LokiQueryBuilderContainer } from './LokiQueryBuilderContainer';
|
||||||
|
import { LokiDatasource } from '../../datasource';
|
||||||
|
import { addOperation } from 'app/plugins/datasource/prometheus/querybuilder/shared/OperationList.testUtils';
|
||||||
|
|
||||||
|
describe('LokiQueryBuilderContainer', () => {
|
||||||
|
it('translates query between string and model', async () => {
|
||||||
|
const props = {
|
||||||
|
query: {
|
||||||
|
expr: '{job="testjob"}',
|
||||||
|
refId: 'A',
|
||||||
|
},
|
||||||
|
datasource: new LokiDatasource(
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
uid: '',
|
||||||
|
type: 'loki',
|
||||||
|
name: 'loki-test',
|
||||||
|
access: 'proxy',
|
||||||
|
url: '',
|
||||||
|
jsonData: {},
|
||||||
|
meta: {} as any,
|
||||||
|
},
|
||||||
|
undefined,
|
||||||
|
undefined
|
||||||
|
),
|
||||||
|
onChange: jest.fn(),
|
||||||
|
onRunQuery: () => {},
|
||||||
|
};
|
||||||
|
render(<LokiQueryBuilderContainer {...props} />);
|
||||||
|
expect(screen.getByText('testjob')).toBeInTheDocument();
|
||||||
|
addOperation('Range functions', 'Rate');
|
||||||
|
expect(props.onChange).toBeCalledWith({
|
||||||
|
expr: 'rate({job="testjob"} [$__interval])',
|
||||||
|
refId: 'A',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,82 @@
|
|||||||
|
import React, { useEffect, useReducer } from 'react';
|
||||||
|
import { LokiDatasource } from '../../datasource';
|
||||||
|
import { LokiQuery } from '../../types';
|
||||||
|
import { buildVisualQueryFromString } from '../parsing';
|
||||||
|
import { lokiQueryModeller } from '../LokiQueryModeller';
|
||||||
|
import { LokiQueryBuilder } from './LokiQueryBuilder';
|
||||||
|
import { QueryPreview } from './QueryPreview';
|
||||||
|
import { LokiVisualQuery } from '../types';
|
||||||
|
import { createSlice, PayloadAction } from '@reduxjs/toolkit';
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
query: LokiQuery;
|
||||||
|
datasource: LokiDatasource;
|
||||||
|
onChange: (update: LokiQuery) => void;
|
||||||
|
onRunQuery: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface State {
|
||||||
|
visQuery?: LokiVisualQuery;
|
||||||
|
expr: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This component is here just to contain the translation logic between string query and the visual query builder model.
|
||||||
|
*/
|
||||||
|
export function LokiQueryBuilderContainer(props: Props) {
|
||||||
|
const { query, onChange, onRunQuery, datasource } = props;
|
||||||
|
const [state, dispatch] = useReducer(stateSlice.reducer, {
|
||||||
|
expr: '',
|
||||||
|
visQuery: {
|
||||||
|
labels: [],
|
||||||
|
operations: [{ id: '__line_contains', params: [''] }],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Only rebuild visual query if expr changes from outside
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch(exprChanged(query.expr));
|
||||||
|
}, [query.expr]);
|
||||||
|
|
||||||
|
const onVisQueryChange = (visQuery: LokiVisualQuery) => {
|
||||||
|
const expr = lokiQueryModeller.renderQuery(visQuery);
|
||||||
|
dispatch(visualQueryChange({ visQuery, expr }));
|
||||||
|
onChange({ ...props.query, expr: expr });
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!state.visQuery) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<LokiQueryBuilder
|
||||||
|
query={state.visQuery}
|
||||||
|
datasource={datasource}
|
||||||
|
onChange={onVisQueryChange}
|
||||||
|
onRunQuery={onRunQuery}
|
||||||
|
/>
|
||||||
|
<QueryPreview query={query.expr} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const stateSlice = createSlice({
|
||||||
|
name: 'prom-builder-container',
|
||||||
|
initialState: { expr: '' } as State,
|
||||||
|
reducers: {
|
||||||
|
visualQueryChange: (state, action: PayloadAction<{ visQuery: LokiVisualQuery; expr: string }>) => {
|
||||||
|
state.expr = action.payload.expr;
|
||||||
|
state.visQuery = action.payload.visQuery;
|
||||||
|
},
|
||||||
|
exprChanged: (state, action: PayloadAction<string>) => {
|
||||||
|
if (!state.visQuery || state.expr !== action.payload) {
|
||||||
|
state.expr = action.payload;
|
||||||
|
const parseResult = buildVisualQueryFromString(action.payload);
|
||||||
|
state.visQuery = parseResult.query;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { visualQueryChange, exprChanged } = stateSlice.actions;
|
@ -4,19 +4,22 @@ import { Stack } from '@grafana/experimental';
|
|||||||
import { lokiQueryModeller } from '../LokiQueryModeller';
|
import { lokiQueryModeller } from '../LokiQueryModeller';
|
||||||
import { OperationListExplained } from 'app/plugins/datasource/prometheus/querybuilder/shared/OperationListExplained';
|
import { OperationListExplained } from 'app/plugins/datasource/prometheus/querybuilder/shared/OperationListExplained';
|
||||||
import { OperationExplainedBox } from 'app/plugins/datasource/prometheus/querybuilder/shared/OperationExplainedBox';
|
import { OperationExplainedBox } from 'app/plugins/datasource/prometheus/querybuilder/shared/OperationExplainedBox';
|
||||||
|
import { buildVisualQueryFromString } from '../parsing';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
query: LokiVisualQuery;
|
query: string;
|
||||||
nested?: boolean;
|
nested?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const LokiQueryBuilderExplained = React.memo<Props>(({ query, nested }) => {
|
export const LokiQueryBuilderExplained = React.memo<Props>(({ query, nested }) => {
|
||||||
|
const visQuery = buildVisualQueryFromString(query || '').query;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack gap={0} direction="column">
|
<Stack gap={0} direction="column">
|
||||||
<OperationExplainedBox stepNumber={1} title={`${lokiQueryModeller.renderLabels(query.labels)}`}>
|
<OperationExplainedBox stepNumber={1} title={`${lokiQueryModeller.renderLabels(visQuery.labels)}`}>
|
||||||
Fetch all log lines matching label filters.
|
Fetch all log lines matching label filters.
|
||||||
</OperationExplainedBox>
|
</OperationExplainedBox>
|
||||||
<OperationListExplained<LokiVisualQuery> stepNumber={2} queryModeller={lokiQueryModeller} query={query} />
|
<OperationListExplained<LokiVisualQuery> stepNumber={2} queryModeller={lokiQueryModeller} query={visQuery} />
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
});
|
});
|
@ -79,13 +79,6 @@ describe('LokiQueryEditorSelector', () => {
|
|||||||
expr: defaultQuery.expr,
|
expr: defaultQuery.expr,
|
||||||
queryType: LokiQueryType.Range,
|
queryType: LokiQueryType.Range,
|
||||||
editorMode: QueryEditorMode.Builder,
|
editorMode: QueryEditorMode.Builder,
|
||||||
visualQuery: {
|
|
||||||
labels: [
|
|
||||||
{ label: 'label1', op: '=', value: 'foo' },
|
|
||||||
{ label: 'label2', op: '=', value: 'bar' },
|
|
||||||
],
|
|
||||||
operations: [],
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -6,13 +6,10 @@ import { QueryEditorModeToggle } from 'app/plugins/datasource/prometheus/querybu
|
|||||||
import { QueryEditorMode } from 'app/plugins/datasource/prometheus/querybuilder/shared/types';
|
import { QueryEditorMode } from 'app/plugins/datasource/prometheus/querybuilder/shared/types';
|
||||||
import React, { useCallback, useState } from 'react';
|
import React, { useCallback, useState } from 'react';
|
||||||
import { LokiQueryEditorProps } from '../../components/types';
|
import { LokiQueryEditorProps } from '../../components/types';
|
||||||
import { LokiQuery } from '../../types';
|
|
||||||
|
|
||||||
import { lokiQueryModeller } from '../LokiQueryModeller';
|
import { lokiQueryModeller } from '../LokiQueryModeller';
|
||||||
import { getQueryWithDefaults } from '../state';
|
import { getQueryWithDefaults } from '../state';
|
||||||
import { getDefaultEmptyQuery, LokiVisualQuery } from '../types';
|
import { LokiQueryBuilderContainer } from './LokiQueryBuilderContainer';
|
||||||
import { LokiQueryBuilder } from './LokiQueryBuilder';
|
import { LokiQueryBuilderExplained } from './LokiQueryBuilderExplained';
|
||||||
import { LokiQueryBuilderExplained } from './LokiQueryBuilderExplaind';
|
|
||||||
import { LokiQueryBuilderOptions } from './LokiQueryBuilderOptions';
|
import { LokiQueryBuilderOptions } from './LokiQueryBuilderOptions';
|
||||||
import { LokiQueryCodeEditor } from './LokiQueryCodeEditor';
|
import { LokiQueryCodeEditor } from './LokiQueryCodeEditor';
|
||||||
import { buildVisualQueryFromString } from '../parsing';
|
import { buildVisualQueryFromString } from '../parsing';
|
||||||
@ -21,43 +18,26 @@ export const LokiQueryEditorSelector = React.memo<LokiQueryEditorProps>((props)
|
|||||||
const { onChange, onRunQuery, data } = props;
|
const { onChange, onRunQuery, data } = props;
|
||||||
const styles = useStyles2(getStyles);
|
const styles = useStyles2(getStyles);
|
||||||
const query = getQueryWithDefaults(props.query);
|
const query = getQueryWithDefaults(props.query);
|
||||||
const [visualQuery, setVisualQuery] = useState<LokiVisualQuery>(query.visualQuery ?? getDefaultEmptyQuery());
|
|
||||||
const [parseModalOpen, setParseModalOpen] = useState(false);
|
const [parseModalOpen, setParseModalOpen] = useState(false);
|
||||||
const [pendingChange, setPendingChange] = useState<LokiQuery | undefined>(undefined);
|
|
||||||
|
|
||||||
const onEditorModeChange = useCallback(
|
const onEditorModeChange = useCallback(
|
||||||
(newMetricEditorMode: QueryEditorMode) => {
|
(newMetricEditorMode: QueryEditorMode) => {
|
||||||
const change = { ...query, editorMode: newMetricEditorMode };
|
const change = { ...query, editorMode: newMetricEditorMode };
|
||||||
if (newMetricEditorMode === QueryEditorMode.Builder) {
|
if (newMetricEditorMode === QueryEditorMode.Builder) {
|
||||||
const result = buildVisualQueryFromString(query.expr);
|
const result = buildVisualQueryFromString(query.expr || '');
|
||||||
change.visualQuery = result.query;
|
|
||||||
// If there are errors, give user a chance to decide if they want to go to builder as that can loose some data.
|
// If there are errors, give user a chance to decide if they want to go to builder as that can loose some data.
|
||||||
if (result.errors.length) {
|
if (result.errors.length) {
|
||||||
setParseModalOpen(true);
|
setParseModalOpen(true);
|
||||||
setPendingChange(change);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setVisualQuery(change.visualQuery);
|
|
||||||
}
|
}
|
||||||
onChange(change);
|
onChange(change);
|
||||||
},
|
},
|
||||||
[onChange, query]
|
[onChange, query]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onChangeViewModel = (updatedQuery: LokiVisualQuery) => {
|
|
||||||
setVisualQuery(updatedQuery);
|
|
||||||
|
|
||||||
onChange({
|
|
||||||
...query,
|
|
||||||
expr: lokiQueryModeller.renderQuery(updatedQuery),
|
|
||||||
visualQuery: updatedQuery,
|
|
||||||
editorMode: QueryEditorMode.Builder,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// If no expr (ie new query) then default to builder
|
// If no expr (ie new query) then default to builder
|
||||||
const editorMode = query.editorMode ?? (query.expr ? QueryEditorMode.Code : QueryEditorMode.Builder);
|
const editorMode = query.editorMode ?? (query.expr ? QueryEditorMode.Code : QueryEditorMode.Builder);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ConfirmModal
|
<ConfirmModal
|
||||||
@ -66,8 +46,7 @@ export const LokiQueryEditorSelector = React.memo<LokiQueryEditorProps>((props)
|
|||||||
body="There were errors while trying to parse the query. Continuing to visual builder may loose some parts of the query."
|
body="There were errors while trying to parse the query. Continuing to visual builder may loose some parts of the query."
|
||||||
confirmText="Continue"
|
confirmText="Continue"
|
||||||
onConfirm={() => {
|
onConfirm={() => {
|
||||||
setVisualQuery(pendingChange!.visualQuery!);
|
onChange({ ...query, editorMode: QueryEditorMode.Builder });
|
||||||
onChange(pendingChange!);
|
|
||||||
setParseModalOpen(false);
|
setParseModalOpen(false);
|
||||||
}}
|
}}
|
||||||
onDismiss={() => setParseModalOpen(false)}
|
onDismiss={() => setParseModalOpen(false)}
|
||||||
@ -90,27 +69,29 @@ export const LokiQueryEditorSelector = React.memo<LokiQueryEditorProps>((props)
|
|||||||
placeholder="Query patterns"
|
placeholder="Query patterns"
|
||||||
allowCustomValue
|
allowCustomValue
|
||||||
onChange={({ value }) => {
|
onChange={({ value }) => {
|
||||||
onChangeViewModel({
|
const result = buildVisualQueryFromString(query.expr || '');
|
||||||
...visualQuery,
|
result.query.operations = value?.operations!;
|
||||||
operations: value?.operations!,
|
onChange({
|
||||||
|
...query,
|
||||||
|
expr: lokiQueryModeller.renderQuery(result.query),
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
options={lokiQueryModeller.getQueryPatterns().map((x) => ({ label: x.name, value: x }))}
|
options={lokiQueryModeller.getQueryPatterns().map((x) => ({ label: x.name, value: x }))}
|
||||||
/>
|
/>
|
||||||
<QueryEditorModeToggle mode={editorMode} onChange={onEditorModeChange} />
|
<QueryEditorModeToggle mode={editorMode!} onChange={onEditorModeChange} />
|
||||||
</EditorHeader>
|
</EditorHeader>
|
||||||
<Space v={0.5} />
|
<Space v={0.5} />
|
||||||
<EditorRows>
|
<EditorRows>
|
||||||
{editorMode === QueryEditorMode.Code && <LokiQueryCodeEditor {...props} />}
|
{editorMode === QueryEditorMode.Code && <LokiQueryCodeEditor {...props} />}
|
||||||
{editorMode === QueryEditorMode.Builder && (
|
{editorMode === QueryEditorMode.Builder && (
|
||||||
<LokiQueryBuilder
|
<LokiQueryBuilderContainer
|
||||||
datasource={props.datasource}
|
datasource={props.datasource}
|
||||||
query={visualQuery}
|
query={query}
|
||||||
onChange={onChangeViewModel}
|
onChange={onChange}
|
||||||
onRunQuery={props.onRunQuery}
|
onRunQuery={props.onRunQuery}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{editorMode === QueryEditorMode.Explain && <LokiQueryBuilderExplained query={visualQuery} />}
|
{editorMode === QueryEditorMode.Explain && <LokiQueryBuilderExplained query={query.expr} />}
|
||||||
{editorMode !== QueryEditorMode.Explain && (
|
{editorMode !== QueryEditorMode.Explain && (
|
||||||
<LokiQueryBuilderOptions query={query} onChange={onChange} onRunQuery={onRunQuery} />
|
<LokiQueryBuilderOptions query={query} onChange={onChange} onRunQuery={onRunQuery} />
|
||||||
)}
|
)}
|
||||||
|
@ -1,39 +1,38 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { LokiVisualQuery } from '../types';
|
|
||||||
import { useTheme2 } from '@grafana/ui';
|
import { useTheme2 } from '@grafana/ui';
|
||||||
import { GrafanaTheme2 } from '@grafana/data';
|
import { GrafanaTheme2 } from '@grafana/data';
|
||||||
import { css, cx } from '@emotion/css';
|
import { css, cx } from '@emotion/css';
|
||||||
import { EditorField, EditorFieldGroup } from '@grafana/experimental';
|
import { EditorField, EditorFieldGroup, EditorRow } from '@grafana/experimental';
|
||||||
import Prism from 'prismjs';
|
import Prism from 'prismjs';
|
||||||
import { lokiGrammar } from '../../syntax';
|
import { lokiGrammar } from '../../syntax';
|
||||||
import { lokiQueryModeller } from '../LokiQueryModeller';
|
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
query: LokiVisualQuery;
|
query: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function QueryPreview({ query }: Props) {
|
export function QueryPreview({ query }: Props) {
|
||||||
const theme = useTheme2();
|
const theme = useTheme2();
|
||||||
const styles = getStyles(theme);
|
const styles = getStyles(theme);
|
||||||
const hightlighted = Prism.highlight(lokiQueryModeller.renderQuery(query), lokiGrammar, 'lokiql');
|
const highlighted = Prism.highlight(query, lokiGrammar, 'lokiql');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<EditorRow>
|
||||||
<EditorFieldGroup>
|
<EditorFieldGroup>
|
||||||
<EditorField label="Query text">
|
<EditorField label="Raw query">
|
||||||
<div
|
<div
|
||||||
className={cx(styles.editorField, 'prism-syntax-highlight')}
|
className={cx(styles.editorField, 'prism-syntax-highlight')}
|
||||||
aria-label="selector"
|
aria-label="selector"
|
||||||
dangerouslySetInnerHTML={{ __html: hightlighted }}
|
dangerouslySetInnerHTML={{ __html: highlighted }}
|
||||||
/>
|
/>
|
||||||
</EditorField>
|
</EditorField>
|
||||||
</EditorFieldGroup>
|
</EditorFieldGroup>
|
||||||
|
</EditorRow>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => {
|
const getStyles = (theme: GrafanaTheme2) => {
|
||||||
return {
|
return {
|
||||||
editorField: css({
|
editorField: css({
|
||||||
padding: theme.spacing(0.25, 1),
|
|
||||||
fontFamily: theme.typography.fontFamilyMonospace,
|
fontFamily: theme.typography.fontFamilyMonospace,
|
||||||
fontSize: theme.typography.bodySmall.fontSize,
|
fontSize: theme.typography.bodySmall.fontSize,
|
||||||
}),
|
}),
|
||||||
|
@ -90,10 +90,3 @@ export enum LokiOperationOrder {
|
|||||||
RangeVectorFunction = 5,
|
RangeVectorFunction = 5,
|
||||||
Last = 6,
|
Last = 6,
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDefaultEmptyQuery(): LokiVisualQuery {
|
|
||||||
return {
|
|
||||||
labels: [],
|
|
||||||
operations: [{ id: '__line_contains', params: [''] }],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { DataQuery, DataSourceJsonData, QueryResultMeta, ScopedVars } from '@grafana/data';
|
import { DataQuery, DataSourceJsonData, QueryResultMeta, ScopedVars } from '@grafana/data';
|
||||||
import { QueryEditorMode } from '../prometheus/querybuilder/shared/types';
|
import { QueryEditorMode } from '../prometheus/querybuilder/shared/types';
|
||||||
import { LokiVisualQuery } from './querybuilder/types';
|
|
||||||
|
|
||||||
export interface LokiInstantQueryRequest {
|
export interface LokiInstantQueryRequest {
|
||||||
query: string;
|
query: string;
|
||||||
@ -43,8 +42,6 @@ export interface LokiQuery extends DataQuery {
|
|||||||
/* @deprecated now use queryType */
|
/* @deprecated now use queryType */
|
||||||
instant?: boolean;
|
instant?: boolean;
|
||||||
editorMode?: QueryEditorMode;
|
editorMode?: QueryEditorMode;
|
||||||
/** Temporary until we have a parser */
|
|
||||||
visualQuery?: LokiVisualQuery;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LokiOptions extends DataSourceJsonData {
|
export interface LokiOptions extends DataSourceJsonData {
|
||||||
|
@ -13,7 +13,7 @@ export interface Props {
|
|||||||
export function QueryPreview({ query }: Props) {
|
export function QueryPreview({ query }: Props) {
|
||||||
const theme = useTheme2();
|
const theme = useTheme2();
|
||||||
const styles = getStyles(theme);
|
const styles = getStyles(theme);
|
||||||
const hightlighted = Prism.highlight(query, promqlGrammar, 'promql');
|
const highlighted = Prism.highlight(query, promqlGrammar, 'promql');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<EditorRow>
|
<EditorRow>
|
||||||
@ -22,7 +22,7 @@ export function QueryPreview({ query }: Props) {
|
|||||||
<div
|
<div
|
||||||
className={cx(styles.editorField, 'prism-syntax-highlight')}
|
className={cx(styles.editorField, 'prism-syntax-highlight')}
|
||||||
aria-label="selector"
|
aria-label="selector"
|
||||||
dangerouslySetInnerHTML={{ __html: hightlighted }}
|
dangerouslySetInnerHTML={{ __html: highlighted }}
|
||||||
/>
|
/>
|
||||||
</EditorField>
|
</EditorField>
|
||||||
</EditorFieldGroup>
|
</EditorFieldGroup>
|
||||||
|
Loading…
Reference in New Issue
Block a user