diff --git a/packages/grafana-ui/src/components/QueryField/QueryField.tsx b/packages/grafana-ui/src/components/QueryField/QueryField.tsx index dc9de22cb93..221fba39ecf 100644 --- a/packages/grafana-ui/src/components/QueryField/QueryField.tsx +++ b/packages/grafana-ui/src/components/QueryField/QueryField.tsx @@ -69,10 +69,12 @@ export class QueryField extends React.PureComponent { 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(); - 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(); }); }); diff --git a/packages/grafana-ui/src/slate-plugins/runner.ts b/packages/grafana-ui/src/slate-plugins/runner.ts index e00ee25b756..a326af8d252 100644 --- a/packages/grafana-ui/src/slate-plugins/runner.ts +++ b/packages/grafana-ui/src/slate-plugins/runner.ts @@ -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(); diff --git a/packages/grafana-ui/src/slate-plugins/suggestions.tsx b/packages/grafana-ui/src/slate-plugins/suggestions.tsx index c11b6ffde95..21d18bf4e70 100644 --- a/packages/grafana-ui/src/slate-plugins/suggestions.tsx +++ b/packages/grafana-ui/src/slate-plugins/suggestions.tsx @@ -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; } } diff --git a/public/app/plugins/datasource/cloudwatch/components/LogsQueryField.tsx b/public/app/plugins/datasource/cloudwatch/components/LogsQueryField.tsx index a1d36871c50..e047ef0a3e8 100644 --- a/public/app/plugins/datasource/cloudwatch/components/LogsQueryField.tsx +++ b/public/app/plugins/datasource/cloudwatch/components/LogsQueryField.tsx @@ -357,7 +357,7 @@ export class CloudWatchLogsQueryField extends React.PureComponent { 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} /> diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/query_field.tsx b/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/query_field.tsx index fd05ef7172f..7e27238f809 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/query_field.tsx +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/editor/query_field.tsx @@ -73,7 +73,7 @@ class QueryField extends React.Component { labelKeys: {}, labelValues: {}, suggestions: [], - typeaheadIndex: 0, + typeaheadIndex: null, typeaheadPrefix: '', value: getInitialValue(props.initialQuery || ''), }; @@ -144,10 +144,10 @@ class QueryField extends React.Component { 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 { 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 { 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 { this.setState( { suggestions: [], - typeaheadIndex: 0, + typeaheadIndex: null, typeaheadPrefix: '', typeaheadContext: null, }, @@ -298,19 +298,20 @@ class QueryField extends React.Component { 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 ( diff --git a/public/app/plugins/datasource/grafana-azure-monitor-datasource/partials/query.editor.html b/public/app/plugins/datasource/grafana-azure-monitor-datasource/partials/query.editor.html index 9f62c8470f7..35b154c51b6 100644 --- a/public/app/plugins/datasource/grafana-azure-monitor-datasource/partials/query.editor.html +++ b/public/app/plugins/datasource/grafana-azure-monitor-datasource/partials/query.editor.html @@ -210,7 +210,7 @@
- +
diff --git a/public/app/plugins/datasource/loki/components/LokiQueryFieldForm.tsx b/public/app/plugins/datasource/loki/components/LokiQueryFieldForm.tsx index 878ce7541df..755bbbd0d14 100644 --- a/public/app/plugins/datasource/loki/components/LokiQueryFieldForm.tsx +++ b/public/app/plugins/datasource/loki/components/LokiQueryFieldForm.tsx @@ -174,7 +174,7 @@ export class LokiQueryFieldForm extends React.PureComponent diff --git a/public/app/plugins/datasource/prometheus/components/PromQueryField.tsx b/public/app/plugins/datasource/prometheus/components/PromQueryField.tsx index 0b25385c068..f56686f96da 100644 --- a/public/app/plugins/datasource/prometheus/components/PromQueryField.tsx +++ b/public/app/plugins/datasource/prometheus/components/PromQueryField.tsx @@ -340,7 +340,7 @@ class PromQueryField extends React.PureComponent