feat: [SQLLAB] add checkbox to control autocomplete (#9338)

* [SQLLAB] add checkbox to control autocomplete

* autocomplete -> autocompleteEnabled

* fix defaultProps

* fix spec
This commit is contained in:
ʈᵃᵢ 2020-03-23 22:11:05 -07:00 committed by GitHub
parent 5d9857544a
commit 866f6f9330
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 80 additions and 39 deletions

View File

@ -18,6 +18,7 @@
*/
import React from 'react';
import { shallow } from 'enzyme';
import { Checkbox } from 'react-bootstrap';
import { defaultQueryEditor, initialState, queries, table } from './fixtures';
import {
@ -105,4 +106,13 @@ describe('SqlEditor', () => {
queryEditor.queryLimit,
);
});
it('allows toggling autocomplete', () => {
const wrapper = shallow(<SqlEditor {...mockedProps} />);
expect(wrapper.find(AceEditorWrapper).props().autocomplete).toBe(true);
wrapper
.find(Checkbox)
.props()
.onChange();
expect(wrapper.find(AceEditorWrapper).props().autocomplete).toBe(false);
});
});

View File

@ -17,7 +17,6 @@
* under the License.
*/
import React from 'react';
import PropTypes from 'prop-types';
import AceEditor from 'react-ace';
import 'brace/mode/sql';
import 'brace/theme/github';
@ -34,41 +33,53 @@ import {
const langTools = ace.acequire('ace/ext/language_tools');
const propTypes = {
actions: PropTypes.object.isRequired,
onBlur: PropTypes.func,
sql: PropTypes.string.isRequired,
schemas: PropTypes.array,
tables: PropTypes.array,
functionNames: PropTypes.array,
extendedTables: PropTypes.array,
queryEditor: PropTypes.object.isRequired,
height: PropTypes.string,
hotkeys: PropTypes.arrayOf(
PropTypes.shape({
key: PropTypes.string.isRequired,
descr: PropTypes.string.isRequired,
func: PropTypes.func.isRequired,
}),
),
onChange: PropTypes.func,
type HotKey = {
key: string;
descr: string;
name: string;
func: () => void;
};
const defaultProps = {
onBlur: () => {},
onChange: () => {},
schemas: [],
tables: [],
functionNames: [],
extendedTables: [],
};
interface Props {
actions: {
queryEditorSetSelectedText: (edit: any, text: null | string) => void;
addTable: (queryEditor: any, value: any, schema: any) => void;
};
autocomplete: boolean;
onBlur: (sql: string) => void;
sql: string;
schemas: any[];
tables: any[];
functionNames: string[];
extendedTables: Array<{ name: string; columns: any[] }>;
queryEditor: any;
height: string;
hotkeys: HotKey[];
onChange: (sql: string) => void;
}
class AceEditorWrapper extends React.PureComponent {
constructor(props) {
interface State {
sql: string;
selectedText: string;
words: any[];
}
class AceEditorWrapper extends React.PureComponent<Props, State> {
static defaultProps = {
onBlur: () => {},
onChange: () => {},
schemas: [],
tables: [],
functionNames: [],
extendedTables: [],
};
constructor(props: Props) {
super(props);
this.state = {
sql: props.sql,
selectedText: '',
words: [],
};
this.onChange = this.onChange.bind(this);
}
@ -77,7 +88,7 @@ class AceEditorWrapper extends React.PureComponent {
this.props.actions.queryEditorSetSelectedText(this.props.queryEditor, null);
this.setAutoCompleter(this.props);
}
UNSAFE_componentWillReceiveProps(nextProps) {
UNSAFE_componentWillReceiveProps(nextProps: Props) {
if (
!areArraysShallowEqual(this.props.tables, nextProps.tables) ||
!areArraysShallowEqual(this.props.schemas, nextProps.schemas) ||
@ -98,7 +109,7 @@ class AceEditorWrapper extends React.PureComponent {
onAltEnter() {
this.props.onBlur(this.state.sql);
}
onEditorLoad(editor) {
onEditorLoad(editor: any) {
editor.commands.addCommand({
name: 'runQuery',
bindKey: { win: 'Alt-enter', mac: 'Alt-enter' },
@ -129,18 +140,24 @@ class AceEditorWrapper extends React.PureComponent {
}
});
}
onChange(text) {
onChange(text: string) {
this.setState({ sql: text });
this.props.onChange(text);
}
getCompletions(aceEditor, session, pos, prefix, callback) {
getCompletions(
aceEditor: any,
session: any,
pos: any,
prefix: string,
callback: (p0: any, p1: any[]) => void,
) {
// If the prefix starts with a number, don't try to autocomplete with a
// table name or schema or anything else
if (!isNaN(parseInt(prefix, 10))) {
return;
}
const completer = {
insertMatch: (editor, data) => {
insertMatch: (editor: any, data: any) => {
if (data.meta === 'table') {
this.props.actions.addTable(
this.props.queryEditor,
@ -163,7 +180,7 @@ class AceEditorWrapper extends React.PureComponent {
});
callback(null, words);
}
setAutoCompleter(props) {
setAutoCompleter(props: Props) {
// Loading schema, table and column names as auto-completable words
const schemas = props.schemas || [];
const schemaWords = schemas.map(s => ({
@ -223,7 +240,7 @@ class AceEditorWrapper extends React.PureComponent {
const validationResult = this.props.queryEditor.validationResult;
const resultIsReady = validationResult && validationResult.completed;
if (resultIsReady && validationResult.errors.length > 0) {
const errors = validationResult.errors.map(err => ({
const errors = validationResult.errors.map((err: any) => ({
type: 'error',
row: err.line_number - 1,
column: err.start_column - 1,
@ -244,14 +261,12 @@ class AceEditorWrapper extends React.PureComponent {
onChange={this.onChange}
width="100%"
editorProps={{ $blockScrolling: true }}
enableLiveAutocompletion
enableLiveAutocompletion={this.props.autocomplete}
value={this.state.sql}
annotations={this.getAceAnnotations()}
/>
);
}
}
AceEditorWrapper.defaultProps = defaultProps;
AceEditorWrapper.propTypes = propTypes;
export default AceEditorWrapper;

View File

@ -20,6 +20,7 @@ import React from 'react';
import { CSSTransition } from 'react-transition-group';
import PropTypes from 'prop-types';
import {
Checkbox,
FormGroup,
InputGroup,
Form,
@ -93,6 +94,7 @@ class SqlEditor extends React.PureComponent {
northPercent: props.queryEditor.northPercent || INITIAL_NORTH_PERCENT,
southPercent: props.queryEditor.southPercent || INITIAL_SOUTH_PERCENT,
sql: props.queryEditor.sql,
autocompleteEnabled: true,
};
this.sqlEditorRef = React.createRef();
this.northPaneRef = React.createRef();
@ -245,6 +247,9 @@ class SqlEditor extends React.PureComponent {
handleWindowResize() {
this.setState({ height: this.getSqlEditorHeight() });
}
handleToggleAutocompleteEnabled = () => {
this.setState({ autocompleteEnabled: !this.state.autocompleteEnabled });
};
elementStyle(dimension, elementSize, gutterSize) {
return {
[dimension]: `calc(${elementSize}% - ${gutterSize +
@ -337,6 +342,7 @@ class SqlEditor extends React.PureComponent {
<div ref={this.northPaneRef} className="north-pane">
<AceEditorWrapper
actions={this.props.actions}
autocomplete={this.state.autocompleteEnabled}
onBlur={this.setQueryEditorSql}
onChange={this.onSqlChanged}
queryEditor={this.props.queryEditor}
@ -502,6 +508,16 @@ class SqlEditor extends React.PureComponent {
</Form>
</div>
<div className="rightItems">
<span>
<Checkbox
checked={this.state.autocompleteEnabled}
inline
title={t('Autocomplete')}
onChange={this.handleToggleAutocompleteEnabled}
>
{t('Autocomplete')}
</Checkbox>
</span>
<TemplateParamsEditor
language="json"
onChange={params => {