From a54e0ff79df4c2be5bbb4e62a6967bbc2a2220ab Mon Sep 17 00:00:00 2001 From: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com> Date: Tue, 1 Dec 2020 20:43:35 +0100 Subject: [PATCH] Loki: Add query type and line limit to query editor in dashboard (#29356) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * WIP Add line limit and query type switch toLoki dashboard editor * Refactor, reuse code for both - Explore and Dashboard * Üpdate snapshot tests * Refactor and unify * Rename test file * Update test --- .../loki/components/LokiExploreExtraField.tsx | 72 -------- .../LokiExploreQueryEditor.test.tsx | 4 +- .../components/LokiExploreQueryEditor.tsx | 53 ------ ...eld.test.tsx => LokiOptionFields.test.tsx} | 8 +- .../loki/components/LokiOptionFields.tsx | 131 ++++++++++++++ .../loki/components/LokiQueryEditor.test.tsx | 2 +- .../loki/components/LokiQueryEditor.tsx | 117 +++++-------- .../loki/components/LokiQueryFieldForm.tsx | 11 ++ .../LokiExploreQueryEditor.test.tsx.snap | 9 - .../LokiQueryEditor.test.tsx.snap | 164 +++++++++--------- 10 files changed, 276 insertions(+), 295 deletions(-) delete mode 100644 public/app/plugins/datasource/loki/components/LokiExploreExtraField.tsx rename public/app/plugins/datasource/loki/components/{LokiExploreExtraField.test.tsx => LokiOptionFields.test.tsx} (73%) create mode 100644 public/app/plugins/datasource/loki/components/LokiOptionFields.tsx diff --git a/public/app/plugins/datasource/loki/components/LokiExploreExtraField.tsx b/public/app/plugins/datasource/loki/components/LokiExploreExtraField.tsx deleted file mode 100644 index a8e96ffd9b6..00000000000 --- a/public/app/plugins/datasource/loki/components/LokiExploreExtraField.tsx +++ /dev/null @@ -1,72 +0,0 @@ -// Libraries -import React, { memo } from 'react'; -import { css, cx } from 'emotion'; - -// Types -import { InlineFormLabel, RadioButtonGroup } from '@grafana/ui'; - -export interface LokiExploreExtraFieldProps { - lineLimitValue: string; - queryType: string; - onLineLimitChange: (e: React.SyntheticEvent) => void; - onKeyDownFunc: (e: React.KeyboardEvent) => void; - onQueryTypeChange: (value: string) => void; -} - -export function LokiExploreExtraField(props: LokiExploreExtraFieldProps) { - const { onLineLimitChange, onKeyDownFunc, lineLimitValue, queryType, onQueryTypeChange } = props; - - const rangeOptions = [ - { value: 'range', label: 'Range' }, - { value: 'instant', label: 'Instant' }, - ]; - - return ( -
- {/*Query type field*/} -
- - Query type - - - -
- {/*Line limit field*/} -
- Line limit - -
-
- ); -} - -export default memo(LokiExploreExtraField); diff --git a/public/app/plugins/datasource/loki/components/LokiExploreQueryEditor.test.tsx b/public/app/plugins/datasource/loki/components/LokiExploreQueryEditor.test.tsx index 5e3f00df210..f9d4e74b810 100644 --- a/public/app/plugins/datasource/loki/components/LokiExploreQueryEditor.test.tsx +++ b/public/app/plugins/datasource/loki/components/LokiExploreQueryEditor.test.tsx @@ -2,7 +2,7 @@ import React from 'react'; import { mount, shallow } from 'enzyme'; import { act } from 'react-dom/test-utils'; import LokiExploreQueryEditor from './LokiExploreQueryEditor'; -import { LokiExploreExtraField } from './LokiExploreExtraField'; +import { LokiOptionFields } from './LokiOptionFields'; import { LokiDatasource } from '../datasource'; import { LokiQuery } from '../types'; import { ExploreMode, LoadingState, PanelData, toUtc, TimeRange } from '@grafana/data'; @@ -93,7 +93,7 @@ describe('LokiExploreQueryEditor', () => { // @ts-ignore strict null error TS2345: Argument of type '() => Promise' is not assignable to parameter of type '() => void | undefined'. await act(async () => { const wrapper = setup(mount); - expect(wrapper.find(LokiExploreExtraField).length).toBe(1); + expect(wrapper.find(LokiOptionFields).length).toBe(1); }); }); }); diff --git a/public/app/plugins/datasource/loki/components/LokiExploreQueryEditor.tsx b/public/app/plugins/datasource/loki/components/LokiExploreQueryEditor.tsx index 01abd8807ae..0949fa8cb49 100644 --- a/public/app/plugins/datasource/loki/components/LokiExploreQueryEditor.tsx +++ b/public/app/plugins/datasource/loki/components/LokiExploreQueryEditor.tsx @@ -7,56 +7,12 @@ import { ExploreQueryFieldProps } from '@grafana/data'; import { LokiDatasource } from '../datasource'; import { LokiQuery, LokiOptions } from '../types'; import { LokiQueryField } from './LokiQueryField'; -import LokiExploreExtraField from './LokiExploreExtraField'; type Props = ExploreQueryFieldProps; export function LokiExploreQueryEditor(props: Props) { const { range, query, data, datasource, history, onChange, onRunQuery } = props; - function onChangeQueryLimit(value: string) { - const { query, onChange } = props; - const nextQuery = { ...query, maxLines: preprocessMaxLines(value) }; - onChange(nextQuery); - } - - function onQueryTypeChange(value: string) { - const { query, onChange } = props; - let nextQuery; - if (value === 'instant') { - nextQuery = { ...query, instant: true, range: false }; - } else { - nextQuery = { ...query, instant: false, range: true }; - } - onChange(nextQuery); - } - - function preprocessMaxLines(value: string): number { - if (value.length === 0) { - // empty input - falls back to dataSource.maxLines limit - return NaN; - } else if (value.length > 0 && (isNaN(+value) || +value < 0)) { - // input with at least 1 character and that is either incorrect (value in the input field is not a number) or negative - // falls back to the limit of 0 lines - return 0; - } else { - // default case - correct input - return +value; - } - } - - function onMaxLinesChange(e: React.SyntheticEvent) { - if (query.maxLines !== preprocessMaxLines(e.currentTarget.value)) { - onChangeQueryLimit(e.currentTarget.value); - } - } - - function onReturnKeyDown(e: React.KeyboardEvent) { - if (e.key === 'Enter') { - onRunQuery(); - } - } - return ( - } /> ); } diff --git a/public/app/plugins/datasource/loki/components/LokiExploreExtraField.test.tsx b/public/app/plugins/datasource/loki/components/LokiOptionFields.test.tsx similarity index 73% rename from public/app/plugins/datasource/loki/components/LokiExploreExtraField.test.tsx rename to public/app/plugins/datasource/loki/components/LokiOptionFields.test.tsx index 486d4114758..c91d0546c89 100644 --- a/public/app/plugins/datasource/loki/components/LokiExploreExtraField.test.tsx +++ b/public/app/plugins/datasource/loki/components/LokiOptionFields.test.tsx @@ -1,8 +1,8 @@ import React from 'react'; import { render, screen } from '@testing-library/react'; -import { LokiExploreExtraFieldProps, LokiExploreExtraField } from './LokiExploreExtraField'; +import { LokiOptionFieldsProps, LokiOptionFields } from './LokiOptionFields'; -const setup = (propOverrides?: LokiExploreExtraFieldProps) => { +const setup = (propOverrides?: LokiOptionFieldsProps) => { const queryType = 'range'; const lineLimitValue = '1'; const onLineLimitChange = jest.fn(); @@ -19,10 +19,10 @@ const setup = (propOverrides?: LokiExploreExtraFieldProps) => { Object.assign(props, propOverrides); - return render(); + return render(); }; -describe('LokiExploreExtraField', () => { +describe('LokiOptionFields', () => { it('should render step field', () => { setup(); expect(screen.getByTestId('lineLimitField')).toBeInTheDocument(); diff --git a/public/app/plugins/datasource/loki/components/LokiOptionFields.tsx b/public/app/plugins/datasource/loki/components/LokiOptionFields.tsx new file mode 100644 index 00000000000..d5fd8edeb24 --- /dev/null +++ b/public/app/plugins/datasource/loki/components/LokiOptionFields.tsx @@ -0,0 +1,131 @@ +// Libraries +import React, { memo } from 'react'; +import { css, cx } from 'emotion'; +import { LokiQuery } from '../types'; + +// Types +import { InlineFormLabel, RadioButtonGroup } from '@grafana/ui'; + +export interface LokiOptionFieldsProps { + lineLimitValue: string; + queryType: LokiQueryType; + query: LokiQuery; + onChange: (value: LokiQuery) => void; + onRunQuery: () => void; + runOnBlur?: boolean; +} + +type LokiQueryType = 'instant' | 'range'; + +const queryTypeOptions = [ + { value: 'range', label: 'Range' }, + { value: 'instant', label: 'Instant' }, +]; + +export function LokiOptionFields(props: LokiOptionFieldsProps) { + const { lineLimitValue, queryType, query, onRunQuery, runOnBlur, onChange } = props; + + function onChangeQueryLimit(value: string) { + const nextQuery = { ...query, maxLines: preprocessMaxLines(value) }; + onChange(nextQuery); + } + + function onQueryTypeChange(value: LokiQueryType) { + let nextQuery; + if (value === 'instant') { + nextQuery = { ...query, instant: true, range: false }; + } else { + nextQuery = { ...query, instant: false, range: true }; + } + onChange(nextQuery); + } + + function preprocessMaxLines(value: string): number { + if (value.length === 0) { + // empty input - falls back to dataSource.maxLines limit + return NaN; + } else if (value.length > 0 && (isNaN(+value) || +value < 0)) { + // input with at least 1 character and that is either incorrect (value in the input field is not a number) or negative + // falls back to the limit of 0 lines + return 0; + } else { + // default case - correct input + return +value; + } + } + + function onMaxLinesChange(e: React.SyntheticEvent) { + if (query.maxLines !== preprocessMaxLines(e.currentTarget.value)) { + onChangeQueryLimit(e.currentTarget.value); + } + } + + function onReturnKeyDown(e: React.KeyboardEvent) { + if (e.key === 'Enter') { + onRunQuery(); + } + } + + return ( +
+ {/*Query type field*/} +
+ + Query type + + + { + onQueryTypeChange(type); + if (runOnBlur) { + onRunQuery(); + } + }} + /> +
+ {/*Line limit field*/} +
+ Line limit + { + if (runOnBlur) { + onRunQuery(); + } + }} + /> +
+
+ ); +} + +export default memo(LokiOptionFields); diff --git a/public/app/plugins/datasource/loki/components/LokiQueryEditor.test.tsx b/public/app/plugins/datasource/loki/components/LokiQueryEditor.test.tsx index dbae1054843..53485f9db95 100644 --- a/public/app/plugins/datasource/loki/components/LokiQueryEditor.test.tsx +++ b/public/app/plugins/datasource/loki/components/LokiQueryEditor.test.tsx @@ -38,7 +38,7 @@ const setup = (propOverrides?: object) => { Object.assign(props, propOverrides); const wrapper = shallow(); - const instance = wrapper.instance() as LokiQueryEditor; + const instance = wrapper.instance(); return { instance, diff --git a/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx b/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx index 2db38ea19e9..67ded1bd8ad 100644 --- a/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx +++ b/public/app/plugins/datasource/loki/components/LokiQueryEditor.tsx @@ -1,90 +1,59 @@ // Libraries -import React, { PureComponent } from 'react'; +import React, { memo } from 'react'; // Types import { QueryEditorProps } from '@grafana/data'; import { InlineFormLabel } from '@grafana/ui'; import { LokiDatasource } from '../datasource'; -import { LokiQuery } from '../types'; +import { LokiQuery, LokiOptions } from '../types'; import { LokiQueryField } from './LokiQueryField'; -type Props = QueryEditorProps; +type Props = QueryEditorProps; -interface State { - legendFormat: string; -} +export function LokiQueryEditor(props: Props) { + const { range, query, data, datasource, onChange, onRunQuery } = props; -export class LokiQueryEditor extends PureComponent { - // Query target to be modified and used for queries - query: LokiQuery; - - 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 ?? '', - }; - } - - onFieldChange = (query: LokiQuery, override?: any) => { - this.query.expr = query.expr; + const onLegendChange = (e: React.SyntheticEvent) => { + const nextQuery = { ...query, legendFormat: e.currentTarget.value }; + onChange(nextQuery); }; - 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, range } = this.props; - const { legendFormat } = this.state; - - return ( -
- - -
-
- +
+ - Legend - - -
-
+ > + Legend + +
- ); - } +
+ ); + + return ( + + ); } -export default LokiQueryEditor; +export default memo(LokiQueryEditor); diff --git a/public/app/plugins/datasource/loki/components/LokiQueryFieldForm.tsx b/public/app/plugins/datasource/loki/components/LokiQueryFieldForm.tsx index 931fb679067..763603e4659 100644 --- a/public/app/plugins/datasource/loki/components/LokiQueryFieldForm.tsx +++ b/public/app/plugins/datasource/loki/components/LokiQueryFieldForm.tsx @@ -23,6 +23,7 @@ import { LokiQuery, LokiOptions } from '../types'; import { Grammar } from 'prismjs'; import LokiLanguageProvider, { LokiHistoryItem } from '../language_provider'; import LokiDatasource from '../datasource'; +import LokiOptionFields from './LokiOptionFields'; function getChooserText(hasSyntax: boolean, hasLogLabels: boolean) { if (!hasSyntax) { @@ -70,6 +71,7 @@ export interface LokiQueryFieldFormProps extends ExploreQueryFieldProps void; onLabelsRefresh?: () => void; ExtraFieldElement?: ReactNode; + runOnBlur?: boolean; } export class LokiQueryFieldForm extends React.PureComponent { @@ -140,6 +142,7 @@ export class LokiQueryFieldForm extends React.PureComponent + {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 fc69ac05249..aa142f94ec3 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,15 +2,6 @@ exports[`LokiExploreQueryEditor should render component 1`] = ` - } data={ Object { "request": Object { 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 b08d1b16b88..cd8848192c7 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 @@ -1,95 +1,99 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Render LokiQueryEditor with legend should render 1`] = ` -
- -
+ - - Legend - - + + Legend + + +
- - + } + datasource={Object {}} + history={Array []} + onBlur={[MockFunction]} + onChange={[MockFunction]} + onRunQuery={[MockFunction]} + query={ + Object { + "expr": "", + "legendFormat": "My Legend", + "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 + + +
- - + } + datasource={Object {}} + history={Array []} + onBlur={[MockFunction]} + onChange={[MockFunction]} + onRunQuery={[MockFunction]} + query={ + Object { + "expr": "", + "legendFormat": "My Legend", + "refId": "A", + } + } + range={ + Object { + "from": "2019-01-01T00:00:00.000Z", + "to": "2020-01-02T00:00:00.000Z", + } + } + runOnBlur={true} +/> `;