mirror of
https://github.com/grafana/grafana.git
synced 2025-02-13 00:55:47 -06:00
Editor: New line on Enter, run query on Shift+Enter (#24654)
* Editor: New line on Enter, run query on Shift+Enter - default Enter behavior on query editor fields should be a new line - special behavior should require a special key: running a query is now done on Shift-Enter - Plugins order had to be changed because when typeahead is shown, Enter is accepting the suggestion * Run with ctrl-enter, hint in query placeholder * Fix Kusto field behavior for Enter * Fix Kusto field behavior for default suggestion
This commit is contained in:
parent
e11504dcd2
commit
01bbcf4eea
@ -69,10 +69,12 @@ export class QueryField extends React.PureComponent<QueryFieldProps, QueryFieldS
|
||||
|
||||
// Base plugins
|
||||
this.plugins = [
|
||||
NewlinePlugin(),
|
||||
// SuggestionsPlugin and RunnerPlugin need to be before NewlinePlugin
|
||||
// because they override Enter behavior
|
||||
SuggestionsPlugin({ onTypeahead, cleanText, portalOrigin, onWillApplySuggestion }),
|
||||
ClearPlugin(),
|
||||
RunnerPlugin({ handler: this.runOnChangeAndRunQuery }),
|
||||
NewlinePlugin(),
|
||||
ClearPlugin(),
|
||||
SelectionShortcutsPlugin(),
|
||||
IndentationPlugin(),
|
||||
ClipboardPlugin(),
|
||||
|
@ -23,7 +23,7 @@ export function NewlinePlugin(): Plugin {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (keyEvent.key === 'Enter' && keyEvent.shiftKey) {
|
||||
if (keyEvent.key === 'Enter') {
|
||||
keyEvent.preventDefault();
|
||||
|
||||
const { startBlock } = value;
|
||||
|
@ -8,10 +8,14 @@ describe('runner', () => {
|
||||
const mockHandler = jest.fn();
|
||||
const handler = RunnerPlugin({ handler: mockHandler }).onKeyDown!;
|
||||
|
||||
it('should execute query when enter is pressed and there are no suggestions visible', () => {
|
||||
it('should execute query when enter with shift is pressed', () => {
|
||||
const value = Plain.deserialize('');
|
||||
const editor = shallow<Editor>(<Editor value={value} />);
|
||||
handler({ key: 'Enter', preventDefault: () => {} } as KeyboardEvent, editor.instance() as any, () => {});
|
||||
handler(
|
||||
{ key: 'Enter', shiftKey: true, preventDefault: () => {} } as KeyboardEvent,
|
||||
editor.instance() as any,
|
||||
() => {}
|
||||
);
|
||||
expect(mockHandler).toBeCalled();
|
||||
});
|
||||
});
|
||||
|
@ -7,11 +7,11 @@ export function RunnerPlugin({ handler }: any): Plugin {
|
||||
const keyEvent = event as KeyboardEvent;
|
||||
|
||||
// Handle enter
|
||||
if (handler && keyEvent.key === 'Enter' && !keyEvent.shiftKey) {
|
||||
if (handler && keyEvent.key === 'Enter' && (keyEvent.shiftKey || keyEvent.ctrlKey)) {
|
||||
// Submit on Enter
|
||||
keyEvent.preventDefault();
|
||||
handler(keyEvent);
|
||||
return true;
|
||||
return editor;
|
||||
}
|
||||
|
||||
return next();
|
||||
|
@ -97,7 +97,15 @@ export function SuggestionsPlugin({
|
||||
|
||||
break;
|
||||
|
||||
case 'Enter':
|
||||
case 'Enter': {
|
||||
if (!(keyEvent.shiftKey || keyEvent.ctrlKey) && hasSuggestions) {
|
||||
keyEvent.preventDefault();
|
||||
return typeaheadRef.insertSuggestion();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 'Tab': {
|
||||
if (hasSuggestions) {
|
||||
keyEvent.preventDefault();
|
||||
@ -108,7 +116,10 @@ export function SuggestionsPlugin({
|
||||
}
|
||||
|
||||
default: {
|
||||
handleTypeaheadDebounced(editor, setState, onTypeahead, cleanText);
|
||||
// Don't react on meta keys
|
||||
if (keyEvent.key.length === 1) {
|
||||
handleTypeaheadDebounced(editor, setState, onTypeahead, cleanText);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -357,7 +357,7 @@ export class CloudWatchLogsQueryField extends React.PureComponent<CloudWatchLogs
|
||||
onRunQuery={this.props.onRunQuery}
|
||||
onTypeahead={this.onTypeahead}
|
||||
cleanText={cleanText}
|
||||
placeholder="Enter a CloudWatch Logs Insights query"
|
||||
placeholder="Enter a CloudWatch Logs Insights query (run with Shift+Enter)"
|
||||
portalOrigin="cloudwatch"
|
||||
syntaxLoaded={syntaxLoaded}
|
||||
disabled={loadingLogGroups || selectedLogGroups.length === 0}
|
||||
|
@ -71,7 +71,7 @@ class ElasticsearchQueryField extends React.PureComponent<Props, State> {
|
||||
query={query.query}
|
||||
onChange={this.onChangeQuery}
|
||||
onRunQuery={this.props.onRunQuery}
|
||||
placeholder="Enter a Lucene query"
|
||||
placeholder="Enter a Lucene query (run with Shift+Enter)"
|
||||
portalOrigin="elasticsearch"
|
||||
syntaxLoaded={syntaxLoaded}
|
||||
/>
|
||||
|
@ -73,7 +73,7 @@ class QueryField extends React.Component<any, any> {
|
||||
labelKeys: {},
|
||||
labelValues: {},
|
||||
suggestions: [],
|
||||
typeaheadIndex: 0,
|
||||
typeaheadIndex: null,
|
||||
typeaheadPrefix: '',
|
||||
value: getInitialValue(props.initialQuery || ''),
|
||||
};
|
||||
@ -144,10 +144,10 @@ class QueryField extends React.Component<any, any> {
|
||||
|
||||
case 'Tab':
|
||||
case 'Enter': {
|
||||
if (this.menuEl) {
|
||||
if (this.menuEl && typeaheadIndex !== null) {
|
||||
// Dont blur input
|
||||
keyboardEvent.preventDefault();
|
||||
if (!suggestions || !suggestions.length) {
|
||||
if (!suggestions || !suggestions.length || keyboardEvent.shiftKey || keyboardEvent.ctrlKey) {
|
||||
return next();
|
||||
}
|
||||
|
||||
@ -166,7 +166,7 @@ class QueryField extends React.Component<any, any> {
|
||||
if (this.menuEl) {
|
||||
// Select next suggestion
|
||||
keyboardEvent.preventDefault();
|
||||
this.setState({ typeaheadIndex: typeaheadIndex + 1 });
|
||||
this.setState({ typeaheadIndex: (typeaheadIndex || 0) + 1 });
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -175,7 +175,7 @@ class QueryField extends React.Component<any, any> {
|
||||
if (this.menuEl) {
|
||||
// Select previous suggestion
|
||||
keyboardEvent.preventDefault();
|
||||
this.setState({ typeaheadIndex: Math.max(0, typeaheadIndex - 1) });
|
||||
this.setState({ typeaheadIndex: Math.max(0, (typeaheadIndex || 0) - 1) });
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -203,7 +203,7 @@ class QueryField extends React.Component<any, any> {
|
||||
this.setState(
|
||||
{
|
||||
suggestions: [],
|
||||
typeaheadIndex: 0,
|
||||
typeaheadIndex: null,
|
||||
typeaheadPrefix: '',
|
||||
typeaheadContext: null,
|
||||
},
|
||||
@ -298,19 +298,20 @@ class QueryField extends React.Component<any, any> {
|
||||
|
||||
renderMenu = () => {
|
||||
const { portalPrefix } = this.props;
|
||||
const { suggestions } = this.state;
|
||||
const { suggestions, typeaheadIndex } = this.state;
|
||||
const hasSuggesstions = suggestions && suggestions.length > 0;
|
||||
if (!hasSuggesstions) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Guard selectedIndex to be within the length of the suggestions
|
||||
let selectedIndex = Math.max(this.state.typeaheadIndex, 0);
|
||||
let selectedIndex = Math.max(typeaheadIndex, 0);
|
||||
const flattenedSuggestions = flattenSuggestions(suggestions);
|
||||
selectedIndex = selectedIndex % flattenedSuggestions.length || 0;
|
||||
const selectedKeys = (flattenedSuggestions.length > 0 ? [flattenedSuggestions[selectedIndex]] : []).map(i =>
|
||||
typeof i === 'object' ? i.text : i
|
||||
);
|
||||
const selectedKeys = (typeaheadIndex !== null && flattenedSuggestions.length > 0
|
||||
? [flattenedSuggestions[selectedIndex]]
|
||||
: []
|
||||
).map(i => (typeof i === 'object' ? i.text : i));
|
||||
|
||||
// Create typeahead in DOM root so we can later position it absolutely
|
||||
return (
|
||||
|
@ -210,7 +210,7 @@
|
||||
<button class="btn btn-primary width-10" ng-click="ctrl.refresh()">Run</button>
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<label class="gf-form-label">(New Line: Shift+Enter, Run Query: Enter, Trigger Suggestion: Ctrl+Space)</label>
|
||||
<label class="gf-form-label">(Run Query: Shift+Enter, Trigger Suggestion: Ctrl+Space)</label>
|
||||
</div>
|
||||
<div class="gf-form gf-form--grow">
|
||||
<div class="gf-form-label gf-form-label--grow"></div>
|
||||
|
@ -174,7 +174,7 @@ export class LokiQueryFieldForm extends React.PureComponent<LokiQueryFieldFormPr
|
||||
onChange={this.onChangeQuery}
|
||||
onBlur={this.props.onBlur}
|
||||
onRunQuery={this.props.onRunQuery}
|
||||
placeholder="Enter a Loki query"
|
||||
placeholder="Enter a Loki query (run with Shift+Enter)"
|
||||
portalOrigin="loki"
|
||||
syntaxLoaded={syntaxLoaded}
|
||||
/>
|
||||
|
@ -340,7 +340,7 @@ class PromQueryField extends React.PureComponent<PromQueryFieldProps, PromQueryF
|
||||
onBlur={this.props.onBlur}
|
||||
onChange={this.onChangeQuery}
|
||||
onRunQuery={this.props.onRunQuery}
|
||||
placeholder="Enter a PromQL query"
|
||||
placeholder="Enter a PromQL query (run with Shift+Enter)"
|
||||
portalOrigin="prometheus"
|
||||
syntaxLoaded={syntaxLoaded}
|
||||
/>
|
||||
|
Loading…
Reference in New Issue
Block a user