diff --git a/public/app/plugins/datasource/loki/components/LokiQueryEditor.test.tsx b/public/app/plugins/datasource/loki/components/LokiQueryEditor.test.tsx
new file mode 100644
index 00000000000..f117ec6f505
--- /dev/null
+++ b/public/app/plugins/datasource/loki/components/LokiQueryEditor.test.tsx
@@ -0,0 +1,66 @@
+import React from 'react';
+import { shallow } from 'enzyme';
+import { toUtc } from '@grafana/data';
+
+import { LokiQueryEditor } from './LokiQueryEditor';
+import { LokiDatasource } from '../datasource';
+import { LokiQuery } from '../types';
+
+const createMockRequestRange = (from: string, to: string) => {
+ return {
+ request: {
+ range: {
+ from: toUtc(from, 'YYYY-MM-DD'),
+ to: toUtc(to, 'YYYY-MM-DD'),
+ },
+ },
+ };
+};
+
+const setup = (propOverrides?: object) => {
+ const datasourceMock: unknown = {};
+ const datasource: LokiDatasource = datasourceMock as LokiDatasource;
+ const onRunQuery = jest.fn();
+ const onChange = jest.fn();
+
+ const query: LokiQuery = {
+ expr: '',
+ refId: 'A',
+ legendFormat: 'My Legend',
+ };
+
+ const data = createMockRequestRange('2020-01-01', '2020-01-02');
+
+ const props: any = {
+ datasource,
+ onChange,
+ onRunQuery,
+ query,
+ data,
+ };
+
+ Object.assign(props, propOverrides);
+
+ const wrapper = shallow();
+ const instance = wrapper.instance() as LokiQueryEditor;
+
+ return {
+ instance,
+ wrapper,
+ };
+};
+
+describe('Render LokiQueryEditor with legend', () => {
+ it('should render', () => {
+ const { wrapper } = setup();
+ expect(wrapper).toMatchSnapshot();
+ });
+
+ it('should update absolute timerange', () => {
+ const { wrapper } = setup();
+ wrapper.setProps({
+ data: createMockRequestRange('2019-01-01', '2020-01-02'),
+ });
+ expect(wrapper).toMatchSnapshot();
+ });
+});
diff --git a/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx b/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx
index b8d47578850..7782269f853 100644
--- a/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx
+++ b/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx
@@ -1,45 +1,105 @@
// Libraries
-import React, { memo } from 'react';
+import React, { PureComponent } from 'react';
// Types
-import { AbsoluteTimeRange, QueryEditorProps } from '@grafana/data';
+import { AbsoluteTimeRange, QueryEditorProps, PanelData } from '@grafana/data';
+import { InlineFormLabel } from '@grafana/ui';
import { LokiDatasource } from '../datasource';
import { LokiQuery } from '../types';
import { LokiQueryField } from './LokiQueryField';
type Props = QueryEditorProps;
-export const LokiQueryEditor = memo(function LokiQueryEditor(props: Props) {
- const { query, data, datasource, onChange, onRunQuery } = props;
+interface State {
+ legendFormat: string;
+}
- let absolute: AbsoluteTimeRange;
+export class LokiQueryEditor extends PureComponent {
+ // Query target to be modified and used for queries
+ query: LokiQuery;
- if (data && data.request) {
- const { range } = data.request;
- absolute = {
- from: range.from.valueOf(),
- to: range.to.valueOf(),
- };
- } else {
- absolute = {
- from: Date.now() - 10000,
- to: Date.now(),
+ constructor(props: Props) {
+ super(props);
+ // Use default query to prevent undefined input values
+ const defaultQuery: Partial = { expr: '', legendFormat: '' };
+ const query = Object.assign({}, defaultQuery, props.query);
+ this.query = query;
+ // Query target properties that are fully controlled inputs
+ this.state = {
+ // Fully controlled text inputs
+ legendFormat: query.legendFormat,
};
}
- return (
-
-
-
- );
-});
+ calcAbsoluteRange = (data: PanelData): AbsoluteTimeRange => {
+ if (data && data.request) {
+ const { range } = data.request;
+ return {
+ from: range.from.valueOf(),
+ to: range.to.valueOf(),
+ };
+ }
+
+ return {
+ from: Date.now() - 10000,
+ to: Date.now(),
+ };
+ };
+
+ onFieldChange = (query: LokiQuery, override?: any) => {
+ this.query.expr = query.expr;
+ };
+
+ onLegendChange = (e: React.SyntheticEvent) => {
+ const legendFormat = e.currentTarget.value;
+ this.query.legendFormat = legendFormat;
+ this.setState({ legendFormat });
+ };
+
+ onRunQuery = () => {
+ const { query } = this;
+ this.props.onChange(query);
+ this.props.onRunQuery();
+ };
+
+ render() {
+ const { datasource, query, data } = this.props;
+ const { legendFormat } = this.state;
+
+ return (
+
+ );
+ }
+}
export default LokiQueryEditor;
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
new file mode 100644
index 00000000000..d7d39fcb240
--- /dev/null
+++ b/public/app/plugins/datasource/loki/components/__snapshots__/LokiQueryEditor.test.tsx.snap
@@ -0,0 +1,115 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Render LokiQueryEditor with legend should render 1`] = `
+
+`;
+
+exports[`Render LokiQueryEditor with legend should update absolute timerange 1`] = `
+
+`;
diff --git a/public/app/plugins/datasource/loki/result_transformer.ts b/public/app/plugins/datasource/loki/result_transformer.ts
index 4ae03bd1d64..8c2ba7409da 100644
--- a/public/app/plugins/datasource/loki/result_transformer.ts
+++ b/public/app/plugins/datasource/loki/result_transformer.ts
@@ -143,8 +143,10 @@ function createUid(ts: string, labelsString: string, line: string): string {
}
function lokiMatrixToTimeSeries(matrixResult: LokiMatrixResult, options: TransformerOptions): TimeSeries {
+ const name = createMetricLabel(matrixResult.metric, options);
return {
- target: createMetricLabel(matrixResult.metric, options),
+ target: name,
+ title: name,
datapoints: lokiPointsToTimeseriesPoints(matrixResult.values, options),
tags: matrixResult.metric,
meta: options.meta,