mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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
This commit is contained in:
parent
620a3f2f9a
commit
9507eda9d1
@ -281,6 +281,7 @@ export interface ExploreQueryFieldProps<
|
|||||||
TOptions extends DataSourceJsonData = DataSourceJsonData
|
TOptions extends DataSourceJsonData = DataSourceJsonData
|
||||||
> extends QueryEditorProps<DSType, TQuery, TOptions> {
|
> extends QueryEditorProps<DSType, TQuery, TOptions> {
|
||||||
history: any[];
|
history: any[];
|
||||||
|
onBlur?: () => void;
|
||||||
onHint?: (action: QueryFixAction) => void;
|
onHint?: (action: QueryFixAction) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { shallow } from 'enzyme';
|
import { shallow } from 'enzyme';
|
||||||
import { QueryField } from './QueryField';
|
import { QueryField } from './QueryField';
|
||||||
|
import { Editor } from 'slate';
|
||||||
|
|
||||||
describe('<QueryField />', () => {
|
describe('<QueryField />', () => {
|
||||||
it('should render with null initial value', () => {
|
it('should render with null initial value', () => {
|
||||||
@ -17,4 +18,35 @@ describe('<QueryField />', () => {
|
|||||||
const wrapper = shallow(<QueryField query="my query" onTypeahead={jest.fn()} portalOrigin="mock-origin" />);
|
const wrapper = shallow(<QueryField query="my query" onTypeahead={jest.fn()} portalOrigin="mock-origin" />);
|
||||||
expect(wrapper.find('div').exists()).toBeTruthy();
|
expect(wrapper.find('div').exists()).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should execute query on blur', () => {
|
||||||
|
const onRun = jest.fn();
|
||||||
|
const wrapper = shallow(
|
||||||
|
<QueryField query="my query" onTypeahead={jest.fn()} onRunQuery={onRun} portalOrigin="mock-origin" />
|
||||||
|
);
|
||||||
|
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(
|
||||||
|
<QueryField
|
||||||
|
query="my query"
|
||||||
|
onTypeahead={jest.fn()}
|
||||||
|
onBlur={onBlur}
|
||||||
|
onRunQuery={onRun}
|
||||||
|
portalOrigin="mock-origin"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -27,6 +27,7 @@ export interface QueryFieldProps {
|
|||||||
// creating a two way binding.
|
// creating a two way binding.
|
||||||
query: string | null;
|
query: string | null;
|
||||||
onRunQuery?: () => void;
|
onRunQuery?: () => void;
|
||||||
|
onBlur?: () => void;
|
||||||
onChange?: (value: string) => void;
|
onChange?: (value: string) => void;
|
||||||
onTypeahead?: (typeahead: TypeaheadInput) => Promise<TypeaheadOutput>;
|
onTypeahead?: (typeahead: TypeaheadInput) => Promise<TypeaheadOutput>;
|
||||||
onWillApplySuggestion?: (suggestion: string, state: SuggestionsState) => string;
|
onWillApplySuggestion?: (suggestion: string, state: SuggestionsState) => string;
|
||||||
@ -171,11 +172,17 @@ export class QueryField extends React.PureComponent<QueryFieldProps, QueryFieldS
|
|||||||
* We need to handle blur events here mainly because of dashboard panels which expect to have query executed on blur.
|
* We need to handle blur events here mainly because of dashboard panels which expect to have query executed on blur.
|
||||||
*/
|
*/
|
||||||
handleBlur = (event: Event, editor: CoreEditor, next: Function) => {
|
handleBlur = (event: Event, editor: CoreEditor, next: Function) => {
|
||||||
const previousValue = this.lastExecutedValue ? Plain.serialize(this.lastExecutedValue) : null;
|
const { onBlur } = this.props;
|
||||||
const currentValue = Plain.serialize(editor.value);
|
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) {
|
if (previousValue !== currentValue) {
|
||||||
this.runOnChangeAndRunQuery();
|
this.runOnChangeAndRunQuery();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return next();
|
return next();
|
||||||
};
|
};
|
||||||
|
@ -57,6 +57,9 @@ interface QueryRowState {
|
|||||||
textEditModeEnabled: boolean;
|
textEditModeEnabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Empty function to override blur execution on query field
|
||||||
|
const noopOnBlur = () => {};
|
||||||
|
|
||||||
export class QueryRow extends PureComponent<QueryRowProps, QueryRowState> {
|
export class QueryRow extends PureComponent<QueryRowProps, QueryRowState> {
|
||||||
state: QueryRowState = {
|
state: QueryRowState = {
|
||||||
textEditModeEnabled: false,
|
textEditModeEnabled: false,
|
||||||
@ -159,6 +162,7 @@ export class QueryRow extends PureComponent<QueryRowProps, QueryRowState> {
|
|||||||
history={history}
|
history={history}
|
||||||
onRunQuery={this.onRunQuery}
|
onRunQuery={this.onRunQuery}
|
||||||
onHint={this.onClickHintFix}
|
onHint={this.onClickHintFix}
|
||||||
|
onBlur={noopOnBlur}
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
data={queryResponse}
|
data={queryResponse}
|
||||||
absoluteRange={absoluteRange}
|
absoluteRange={absoluteRange}
|
||||||
|
@ -172,6 +172,7 @@ export class LokiQueryFieldForm extends React.PureComponent<LokiQueryFieldFormPr
|
|||||||
onTypeahead={this.onTypeahead}
|
onTypeahead={this.onTypeahead}
|
||||||
onWillApplySuggestion={willApplySuggestion}
|
onWillApplySuggestion={willApplySuggestion}
|
||||||
onChange={this.onChangeQuery}
|
onChange={this.onChangeQuery}
|
||||||
|
onBlur={this.props.onBlur}
|
||||||
onRunQuery={this.props.onRunQuery}
|
onRunQuery={this.props.onRunQuery}
|
||||||
placeholder="Enter a Loki query"
|
placeholder="Enter a Loki query"
|
||||||
portalOrigin="loki"
|
portalOrigin="loki"
|
||||||
|
@ -297,6 +297,7 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
|
|||||||
query={query.expr}
|
query={query.expr}
|
||||||
onTypeahead={this.onTypeahead}
|
onTypeahead={this.onTypeahead}
|
||||||
onWillApplySuggestion={willApplySuggestion}
|
onWillApplySuggestion={willApplySuggestion}
|
||||||
|
onBlur={this.props.onBlur}
|
||||||
onChange={this.onChangeQuery}
|
onChange={this.onChangeQuery}
|
||||||
onRunQuery={this.props.onRunQuery}
|
onRunQuery={this.props.onRunQuery}
|
||||||
placeholder="Enter a PromQL query"
|
placeholder="Enter a PromQL query"
|
||||||
|
Loading…
Reference in New Issue
Block a user