Skip to content

Commit 0c234f5

Browse files
authored
Merge pull request #80 from quickwit-oss/ddelemeny/improve-editor-a11y
Improve LuceneQueryEditor keyboard a11y, fixes #76
2 parents 0afa5cb + d63aae3 commit 0c234f5

File tree

4 files changed

+57
-15
lines changed

4 files changed

+57
-15
lines changed

src/LogContext/components/LogContextUI.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,13 @@ export function LogContextUI(props: LogContextUIProps ){
6464
<LogContextQueryBuilderSidebar {...props} builder={builder} updateQuery={setParsedQuery} searchableFields={fields}/>
6565
<div className={css`width:100%; display:flex; flex-direction:column; gap:0.5rem; min-width:0;`}>
6666
{ActionBar}
67-
<LuceneQueryEditor value={builder.query} autocompleter={getSuggestions} onChange={builder.setQuery}/>
67+
<LuceneQueryEditor
68+
placeholder="Shift-Enter to run the query, Ctrl-Space to autocomplete"
69+
value={builder.query}
70+
autocompleter={getSuggestions}
71+
onChange={builder.setQuery}
72+
onSubmit={runQuery}
73+
/>
6874
</div>
6975
</DatasourceContext.Provider>
7076
</div>

src/components/LuceneQueryEditor.tsx

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import React, { useRef, useCallback } from "react";
2-
import { debounceTime } from 'rxjs';
3-
import { useObservableCallback, useSubscription } from 'observable-hooks'
42
import { css } from "@emotion/css";
53

64

7-
import CodeMirror, { ReactCodeMirrorRef } from '@uiw/react-codemirror';
5+
import CodeMirror, { ReactCodeMirrorRef, keymap } from '@uiw/react-codemirror';
86
import {linter, Diagnostic, lintGutter} from "@codemirror/lint"
97
import {autocompletion, CompletionContext} from "@codemirror/autocomplete"
108
import { LuceneQuery } from "utils/lucene";
@@ -15,6 +13,7 @@ export type LuceneQueryEditorProps = {
1513
value: string,
1614
autocompleter: (word: string) => any,
1715
onChange: (query: string) => void
16+
onSubmit: (query: string) => void
1817
}
1918

2019
export function LuceneQueryEditor(props: LuceneQueryEditorProps){
@@ -49,18 +48,22 @@ export function LuceneQueryEditor(props: LuceneQueryEditorProps){
4948

5049
const autocomplete = autocompletion({ override: [datasourceCompletions] })
5150

52-
const [onChange, textChanged$] = useObservableCallback<string>(event$ => event$.pipe(debounceTime(1000)))
53-
54-
useSubscription(textChanged$, props.onChange)
55-
5651
return (<CodeMirror
5752
ref={editorRef}
5853
className={css`height:100%`} // XXX : need to set height for both wrapper elements
5954
height="100%"
6055
theme={'dark'}
6156
placeholder={props.placeholder}
6257
value={props.value}
63-
onChange={onChange}
64-
extensions={[queryLinter, lintGutter(), autocomplete]}
58+
onChange={props.onChange}
59+
indentWithTab={false}
60+
extensions={[
61+
queryLinter, lintGutter(),
62+
autocomplete,
63+
keymap.of([{key:'Shift-Enter', run:(target)=>{
64+
props.onSubmit(target.state.doc.toString())
65+
return true;
66+
}}])
67+
]}
6568
/>);
6669
}

src/components/QueryEditor/AnnotationQueryEditor.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ export function ElasticsearchAnnotationsQueryEditor(props: Props) {
3434
target: newTarget,
3535
});
3636
}}
37+
// XXX : ain't used at the moment, fix the build
38+
onSubmit={()=>null}
3739
/>
3840
</div>
3941

src/components/QueryEditor/index.tsx

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { css } from '@emotion/css';
22

33
import React, { createContext } from 'react';
4+
import { debounceTime, throttleTime } from 'rxjs';
5+
import { useObservableCallback, useSubscription } from 'observable-hooks'
46

57
import { CoreApp, Field, getDefaultTimeRange, GrafanaTheme2, QueryEditorProps } from '@grafana/data';
68
import { InlineLabel, useStyles2 } from '@grafana/ui';
@@ -34,7 +36,7 @@ export const QueryEditor = ({ query, onChange, onRunQuery, datasource, range, ap
3436
query={query}
3537
range={range || getDefaultTimeRange()}
3638
>
37-
<QueryEditorForm value={query} />
39+
<QueryEditorForm value={query} onRunQuery={onRunQuery} />
3840
</ElasticsearchProvider>
3941
);
4042
};
@@ -54,21 +56,33 @@ export const useSearchableFields = getHook(SearchableFieldsContext)
5456

5557
interface Props {
5658
value: ElasticsearchQuery;
59+
onRunQuery: () => void
5760
}
5861

59-
export const ElasticSearchQueryField = ({ value, onChange }: { value?: string; onChange: (v: string) => void }) => {
62+
type ElasticSearchQueryFieldProps = {
63+
value?: string;
64+
onChange: (v: string) => void
65+
onSubmit: (v: string) => void
66+
}
67+
export const ElasticSearchQueryField = ({ value, onChange, onSubmit }: ElasticSearchQueryFieldProps) => {
6068
const styles = useStyles2(getStyles);
6169
const datasource = useDatasource()
6270
const { getSuggestions } = useDatasourceFields(datasource);
6371

6472
return (
6573
<div className={styles.queryItem}>
66-
<LuceneQueryEditor placeholder="Enter a lucene query" value={value || ''} autocompleter={getSuggestions} onChange={onChange}/>
74+
<LuceneQueryEditor
75+
placeholder="Enter a lucene query - Type Shift-Enter to run query, Ctrl-Space to autocomplete"
76+
value={value || ''}
77+
autocompleter={getSuggestions}
78+
onChange={onChange}
79+
onSubmit={onSubmit}
80+
/>
6781
</div>
6882
);
6983
};
7084

71-
const QueryEditorForm = ({ value }: Props) => {
85+
const QueryEditorForm = ({ value, onRunQuery }: Props) => {
7286
const dispatch = useDispatch();
7387
const nextId = useNextId();
7488
const styles = useStyles2(getStyles);
@@ -77,6 +91,20 @@ const QueryEditorForm = ({ value }: Props) => {
7791
(metric) => metricAggregationConfig[metric.type].impliedQueryType === 'metrics'
7892
);
7993

94+
const onChange = (query: string) => {
95+
dispatch(changeQuery(query))
96+
}
97+
const onSubmit = (query: string) => {
98+
onChange(query)
99+
onRunQuery()
100+
}
101+
102+
const [onChangeCB, textChanged$] = useObservableCallback<string>(event$ => event$.pipe(debounceTime(1000)))
103+
const [onSubmitCB, submitted$] = useObservableCallback<string>(event$=>event$.pipe(throttleTime(500)))
104+
105+
useSubscription(textChanged$, onChange)
106+
useSubscription(submitted$, onSubmit)
107+
80108
return (
81109
<>
82110
<div className={styles.root}>
@@ -87,7 +115,10 @@ const QueryEditorForm = ({ value }: Props) => {
87115
</div>
88116
<div className={styles.root}>
89117
<InlineLabel width={17}>Lucene Query</InlineLabel>
90-
<ElasticSearchQueryField onChange={(query) => dispatch(changeQuery(query))} value={value?.query} />
118+
<ElasticSearchQueryField
119+
onChange={onChangeCB}
120+
value={value?.query}
121+
onSubmit={onSubmitCB}/>
91122
</div>
92123

93124
<MetricAggregationsEditor nextId={nextId} />

0 commit comments

Comments
 (0)