From 9507eda9d1e48b3cd3ad224018ffc0a7716f97f2 Mon Sep 17 00:00:00 2001 From: David Date: Wed, 6 Nov 2019 15:08:30 +0000 Subject: [PATCH] QueryField: Prevent query runs on blur in Explore (#20180) As discussed in a UX feedback session, it's annoying that queries are automatically executed in Explore. This change adds props to override the blur behavior. - add `onBlur` to Explore query field props - Explore's query row will pass down an empty function for onBlur to the query fields - pass onBlur through to the QueryField component for Loki and Prometheus - add test to QueryField to make sure if onBlur is specified, the onRunQuery is not executed --- packages/grafana-data/src/types/datasource.ts | 1 + .../components/QueryField/QueryField.test.tsx | 32 +++++++++++++++++++ .../src/components/QueryField/QueryField.tsx | 17 +++++++--- public/app/features/explore/QueryRow.tsx | 4 +++ .../loki/components/LokiQueryFieldForm.tsx | 1 + .../prometheus/components/PromQueryField.tsx | 1 + 6 files changed, 51 insertions(+), 5 deletions(-) diff --git a/packages/grafana-data/src/types/datasource.ts b/packages/grafana-data/src/types/datasource.ts index 2529a3fdd2a..320db61c0de 100644 --- a/packages/grafana-data/src/types/datasource.ts +++ b/packages/grafana-data/src/types/datasource.ts @@ -281,6 +281,7 @@ export interface ExploreQueryFieldProps< TOptions extends DataSourceJsonData = DataSourceJsonData > extends QueryEditorProps { history: any[]; + onBlur?: () => void; onHint?: (action: QueryFixAction) => void; } diff --git a/packages/grafana-ui/src/components/QueryField/QueryField.test.tsx b/packages/grafana-ui/src/components/QueryField/QueryField.test.tsx index 5e8303b01b2..1a453f1f06c 100644 --- a/packages/grafana-ui/src/components/QueryField/QueryField.test.tsx +++ b/packages/grafana-ui/src/components/QueryField/QueryField.test.tsx @@ -1,6 +1,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { QueryField } from './QueryField'; +import { Editor } from 'slate'; describe('', () => { it('should render with null initial value', () => { @@ -17,4 +18,35 @@ describe('', () => { const wrapper = shallow(); expect(wrapper.find('div').exists()).toBeTruthy(); }); + + it('should execute query on blur', () => { + const onRun = jest.fn(); + const wrapper = shallow( + + ); + const field = wrapper.instance() as QueryField; + expect(onRun.mock.calls.length).toBe(0); + field.handleBlur(new Event('bogus'), new Editor({}), () => {}); + expect(onRun.mock.calls.length).toBe(1); + }); + + it('should run custom on blur, but not necessarily execute query', () => { + const onBlur = jest.fn(); + const onRun = jest.fn(); + const wrapper = shallow( + + ); + const field = wrapper.instance() as QueryField; + expect(onBlur.mock.calls.length).toBe(0); + expect(onRun.mock.calls.length).toBe(0); + field.handleBlur(new Event('bogus'), new Editor({}), () => {}); + expect(onBlur.mock.calls.length).toBe(1); + expect(onRun.mock.calls.length).toBe(0); + }); }); diff --git a/packages/grafana-ui/src/components/QueryField/QueryField.tsx b/packages/grafana-ui/src/components/QueryField/QueryField.tsx index 58c6fb3c4d1..7bfe98e84aa 100644 --- a/packages/grafana-ui/src/components/QueryField/QueryField.tsx +++ b/packages/grafana-ui/src/components/QueryField/QueryField.tsx @@ -27,6 +27,7 @@ export interface QueryFieldProps { // creating a two way binding. query: string | null; onRunQuery?: () => void; + onBlur?: () => void; onChange?: (value: string) => void; onTypeahead?: (typeahead: TypeaheadInput) => Promise; onWillApplySuggestion?: (suggestion: string, state: SuggestionsState) => string; @@ -171,11 +172,17 @@ export class QueryField extends React.PureComponent { - const previousValue = this.lastExecutedValue ? Plain.serialize(this.lastExecutedValue) : null; - const currentValue = Plain.serialize(editor.value); - - if (previousValue !== currentValue) { - this.runOnChangeAndRunQuery(); + const { onBlur } = this.props; + if (onBlur) { + onBlur(); + } else { + // Run query by default on blur + const previousValue = this.lastExecutedValue ? Plain.serialize(this.lastExecutedValue) : null; + const currentValue = Plain.serialize(editor.value); + + if (previousValue !== currentValue) { + this.runOnChangeAndRunQuery(); + } } return next(); }; diff --git a/public/app/features/explore/QueryRow.tsx b/public/app/features/explore/QueryRow.tsx index f0514f24c4e..845e3f462b4 100644 --- a/public/app/features/explore/QueryRow.tsx +++ b/public/app/features/explore/QueryRow.tsx @@ -57,6 +57,9 @@ interface QueryRowState { textEditModeEnabled: boolean; } +// Empty function to override blur execution on query field +const noopOnBlur = () => {}; + export class QueryRow extends PureComponent { state: QueryRowState = { textEditModeEnabled: false, @@ -159,6 +162,7 @@ export class QueryRow extends PureComponent { history={history} onRunQuery={this.onRunQuery} onHint={this.onClickHintFix} + onBlur={noopOnBlur} onChange={this.onChange} data={queryResponse} absoluteRange={absoluteRange} diff --git a/public/app/plugins/datasource/loki/components/LokiQueryFieldForm.tsx b/public/app/plugins/datasource/loki/components/LokiQueryFieldForm.tsx index 19af2973783..040372434cc 100644 --- a/public/app/plugins/datasource/loki/components/LokiQueryFieldForm.tsx +++ b/public/app/plugins/datasource/loki/components/LokiQueryFieldForm.tsx @@ -172,6 +172,7 @@ export class LokiQueryFieldForm extends React.PureComponent