Skip to content

Commit

Permalink
[SQLLAB] add checkbox to control autocomplete
Browse files Browse the repository at this point in the history
  • Loading branch information
nytai committed Mar 23, 2020
1 parent 5e6662a commit 5d7c9a9
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 52 deletions.
10 changes: 10 additions & 0 deletions superset-frontend/spec/javascripts/sqllab/SqlEditor_spec.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -34,41 +33,44 @@ 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;
}

interface State {
sql: string;
selectedText: string;
words: any[];
}

class AceEditorWrapper extends React.PureComponent {
constructor(props) {
class AceEditorWrapper extends React.PureComponent<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
sql: props.sql,
selectedText: '',
words: [],
};
this.onChange = this.onChange.bind(this);
}
Expand All @@ -77,13 +79,16 @@ 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) ||
!areArraysShallowEqual(this.props.tables || [], nextProps.tables || []) ||
!areArraysShallowEqual(
this.props.schemas || [],
nextProps.schemas || [],
) ||
!areArraysShallowEqual(
this.props.extendedTables,
nextProps.extendedTables,
this.props.extendedTables || [],
nextProps.extendedTables || [],
)
) {
this.setAutoCompleter(nextProps);
Expand All @@ -93,12 +98,12 @@ class AceEditorWrapper extends React.PureComponent {
}
}
onBlur() {
this.props.onBlur(this.state.sql);
if (this.props.onBlur) this.props.onBlur(this.state.sql);
}
onAltEnter() {
this.props.onBlur(this.state.sql);
if (this.props.onBlur) this.props.onBlur(this.state.sql);
}
onEditorLoad(editor) {
onEditorLoad(editor: any) {
editor.commands.addCommand({
name: 'runQuery',
bindKey: { win: 'Alt-enter', mac: 'Alt-enter' },
Expand Down Expand Up @@ -129,18 +134,24 @@ class AceEditorWrapper extends React.PureComponent {
}
});
}
onChange(text) {
onChange(text: string) {
this.setState({ sql: text });
this.props.onChange(text);
if (this.props.onChange) 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,
Expand All @@ -163,7 +174,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 => ({
Expand Down Expand Up @@ -197,12 +208,14 @@ class AceEditorWrapper extends React.PureComponent {
meta: 'column',
}));

const functionWords = props.functionNames.map(func => ({
name: func,
value: func,
score: SQL_FUNCTIONS_AUTOCOMPLETE_SCORE,
meta: 'function',
}));
const functionWords = props.functionNames
? props.functionNames.map(func => ({
name: func,
value: func,
score: SQL_FUNCTIONS_AUTOCOMPLETE_SCORE,
meta: 'function',
}))
: [];

const words = schemaWords
.concat(tableWords)
Expand All @@ -223,7 +236,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,
Expand All @@ -244,14 +257,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;
16 changes: 16 additions & 0 deletions superset-frontend/src/SqlLab/components/SqlEditor.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import React from 'react';
import { CSSTransition } from 'react-transition-group';
import PropTypes from 'prop-types';
import {
Checkbox,
FormGroup,
InputGroup,
Form,
Expand Down Expand Up @@ -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,
autocomplete: true,
};
this.sqlEditorRef = React.createRef();
this.northPaneRef = React.createRef();
Expand Down Expand Up @@ -245,6 +247,9 @@ class SqlEditor extends React.PureComponent {
handleWindowResize() {
this.setState({ height: this.getSqlEditorHeight() });
}
handleToggleAutocomplete = () => {
this.setState({ autocomplete: !this.state.autocomplete });
};
elementStyle(dimension, elementSize, gutterSize) {
return {
[dimension]: `calc(${elementSize}% - ${gutterSize +
Expand Down Expand Up @@ -337,6 +342,7 @@ class SqlEditor extends React.PureComponent {
<div ref={this.northPaneRef} className="north-pane">
<AceEditorWrapper
actions={this.props.actions}
autocomplete={this.state.autocomplete}
onBlur={this.setQueryEditorSql}
onChange={this.onSqlChanged}
queryEditor={this.props.queryEditor}
Expand Down Expand Up @@ -502,6 +508,16 @@ class SqlEditor extends React.PureComponent {
</Form>
</div>
<div className="rightItems">
<span>
<Checkbox
checked={this.state.autocomplete}
inline
title={t('Autocomplete')}
onChange={this.handleToggleAutocomplete}
>
{t('Autocomplete')}
</Checkbox>
</span>
<TemplateParamsEditor
language="json"
onChange={params => {
Expand Down

0 comments on commit 5d7c9a9

Please sign in to comment.