mirror of https://github.com/apache/superset.git
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:
parent
5d9857544a
commit
866f6f9330
|
@ -18,6 +18,7 @@
|
||||||
*/
|
*/
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { shallow } from 'enzyme';
|
import { shallow } from 'enzyme';
|
||||||
|
import { Checkbox } from 'react-bootstrap';
|
||||||
|
|
||||||
import { defaultQueryEditor, initialState, queries, table } from './fixtures';
|
import { defaultQueryEditor, initialState, queries, table } from './fixtures';
|
||||||
import {
|
import {
|
||||||
|
@ -105,4 +106,13 @@ describe('SqlEditor', () => {
|
||||||
queryEditor.queryLimit,
|
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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import PropTypes from 'prop-types';
|
|
||||||
import AceEditor from 'react-ace';
|
import AceEditor from 'react-ace';
|
||||||
import 'brace/mode/sql';
|
import 'brace/mode/sql';
|
||||||
import 'brace/theme/github';
|
import 'brace/theme/github';
|
||||||
|
@ -34,41 +33,53 @@ import {
|
||||||
|
|
||||||
const langTools = ace.acequire('ace/ext/language_tools');
|
const langTools = ace.acequire('ace/ext/language_tools');
|
||||||
|
|
||||||
const propTypes = {
|
type HotKey = {
|
||||||
actions: PropTypes.object.isRequired,
|
key: string;
|
||||||
onBlur: PropTypes.func,
|
descr: string;
|
||||||
sql: PropTypes.string.isRequired,
|
name: string;
|
||||||
schemas: PropTypes.array,
|
func: () => void;
|
||||||
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,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultProps = {
|
interface Props {
|
||||||
onBlur: () => {},
|
actions: {
|
||||||
onChange: () => {},
|
queryEditorSetSelectedText: (edit: any, text: null | string) => void;
|
||||||
schemas: [],
|
addTable: (queryEditor: any, value: any, schema: any) => void;
|
||||||
tables: [],
|
};
|
||||||
functionNames: [],
|
autocomplete: boolean;
|
||||||
extendedTables: [],
|
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 {
|
interface State {
|
||||||
constructor(props) {
|
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);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
sql: props.sql,
|
sql: props.sql,
|
||||||
selectedText: '',
|
selectedText: '',
|
||||||
|
words: [],
|
||||||
};
|
};
|
||||||
this.onChange = this.onChange.bind(this);
|
this.onChange = this.onChange.bind(this);
|
||||||
}
|
}
|
||||||
|
@ -77,7 +88,7 @@ class AceEditorWrapper extends React.PureComponent {
|
||||||
this.props.actions.queryEditorSetSelectedText(this.props.queryEditor, null);
|
this.props.actions.queryEditorSetSelectedText(this.props.queryEditor, null);
|
||||||
this.setAutoCompleter(this.props);
|
this.setAutoCompleter(this.props);
|
||||||
}
|
}
|
||||||
UNSAFE_componentWillReceiveProps(nextProps) {
|
UNSAFE_componentWillReceiveProps(nextProps: Props) {
|
||||||
if (
|
if (
|
||||||
!areArraysShallowEqual(this.props.tables, nextProps.tables) ||
|
!areArraysShallowEqual(this.props.tables, nextProps.tables) ||
|
||||||
!areArraysShallowEqual(this.props.schemas, nextProps.schemas) ||
|
!areArraysShallowEqual(this.props.schemas, nextProps.schemas) ||
|
||||||
|
@ -98,7 +109,7 @@ class AceEditorWrapper extends React.PureComponent {
|
||||||
onAltEnter() {
|
onAltEnter() {
|
||||||
this.props.onBlur(this.state.sql);
|
this.props.onBlur(this.state.sql);
|
||||||
}
|
}
|
||||||
onEditorLoad(editor) {
|
onEditorLoad(editor: any) {
|
||||||
editor.commands.addCommand({
|
editor.commands.addCommand({
|
||||||
name: 'runQuery',
|
name: 'runQuery',
|
||||||
bindKey: { win: 'Alt-enter', mac: 'Alt-enter' },
|
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.setState({ sql: text });
|
||||||
this.props.onChange(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
|
// If the prefix starts with a number, don't try to autocomplete with a
|
||||||
// table name or schema or anything else
|
// table name or schema or anything else
|
||||||
if (!isNaN(parseInt(prefix, 10))) {
|
if (!isNaN(parseInt(prefix, 10))) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const completer = {
|
const completer = {
|
||||||
insertMatch: (editor, data) => {
|
insertMatch: (editor: any, data: any) => {
|
||||||
if (data.meta === 'table') {
|
if (data.meta === 'table') {
|
||||||
this.props.actions.addTable(
|
this.props.actions.addTable(
|
||||||
this.props.queryEditor,
|
this.props.queryEditor,
|
||||||
|
@ -163,7 +180,7 @@ class AceEditorWrapper extends React.PureComponent {
|
||||||
});
|
});
|
||||||
callback(null, words);
|
callback(null, words);
|
||||||
}
|
}
|
||||||
setAutoCompleter(props) {
|
setAutoCompleter(props: Props) {
|
||||||
// Loading schema, table and column names as auto-completable words
|
// Loading schema, table and column names as auto-completable words
|
||||||
const schemas = props.schemas || [];
|
const schemas = props.schemas || [];
|
||||||
const schemaWords = schemas.map(s => ({
|
const schemaWords = schemas.map(s => ({
|
||||||
|
@ -223,7 +240,7 @@ class AceEditorWrapper extends React.PureComponent {
|
||||||
const validationResult = this.props.queryEditor.validationResult;
|
const validationResult = this.props.queryEditor.validationResult;
|
||||||
const resultIsReady = validationResult && validationResult.completed;
|
const resultIsReady = validationResult && validationResult.completed;
|
||||||
if (resultIsReady && validationResult.errors.length > 0) {
|
if (resultIsReady && validationResult.errors.length > 0) {
|
||||||
const errors = validationResult.errors.map(err => ({
|
const errors = validationResult.errors.map((err: any) => ({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
row: err.line_number - 1,
|
row: err.line_number - 1,
|
||||||
column: err.start_column - 1,
|
column: err.start_column - 1,
|
||||||
|
@ -244,14 +261,12 @@ class AceEditorWrapper extends React.PureComponent {
|
||||||
onChange={this.onChange}
|
onChange={this.onChange}
|
||||||
width="100%"
|
width="100%"
|
||||||
editorProps={{ $blockScrolling: true }}
|
editorProps={{ $blockScrolling: true }}
|
||||||
enableLiveAutocompletion
|
enableLiveAutocompletion={this.props.autocomplete}
|
||||||
value={this.state.sql}
|
value={this.state.sql}
|
||||||
annotations={this.getAceAnnotations()}
|
annotations={this.getAceAnnotations()}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AceEditorWrapper.defaultProps = defaultProps;
|
|
||||||
AceEditorWrapper.propTypes = propTypes;
|
|
||||||
|
|
||||||
export default AceEditorWrapper;
|
export default AceEditorWrapper;
|
|
@ -20,6 +20,7 @@ import React from 'react';
|
||||||
import { CSSTransition } from 'react-transition-group';
|
import { CSSTransition } from 'react-transition-group';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import {
|
import {
|
||||||
|
Checkbox,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
InputGroup,
|
InputGroup,
|
||||||
Form,
|
Form,
|
||||||
|
@ -93,6 +94,7 @@ class SqlEditor extends React.PureComponent {
|
||||||
northPercent: props.queryEditor.northPercent || INITIAL_NORTH_PERCENT,
|
northPercent: props.queryEditor.northPercent || INITIAL_NORTH_PERCENT,
|
||||||
southPercent: props.queryEditor.southPercent || INITIAL_SOUTH_PERCENT,
|
southPercent: props.queryEditor.southPercent || INITIAL_SOUTH_PERCENT,
|
||||||
sql: props.queryEditor.sql,
|
sql: props.queryEditor.sql,
|
||||||
|
autocompleteEnabled: true,
|
||||||
};
|
};
|
||||||
this.sqlEditorRef = React.createRef();
|
this.sqlEditorRef = React.createRef();
|
||||||
this.northPaneRef = React.createRef();
|
this.northPaneRef = React.createRef();
|
||||||
|
@ -245,6 +247,9 @@ class SqlEditor extends React.PureComponent {
|
||||||
handleWindowResize() {
|
handleWindowResize() {
|
||||||
this.setState({ height: this.getSqlEditorHeight() });
|
this.setState({ height: this.getSqlEditorHeight() });
|
||||||
}
|
}
|
||||||
|
handleToggleAutocompleteEnabled = () => {
|
||||||
|
this.setState({ autocompleteEnabled: !this.state.autocompleteEnabled });
|
||||||
|
};
|
||||||
elementStyle(dimension, elementSize, gutterSize) {
|
elementStyle(dimension, elementSize, gutterSize) {
|
||||||
return {
|
return {
|
||||||
[dimension]: `calc(${elementSize}% - ${gutterSize +
|
[dimension]: `calc(${elementSize}% - ${gutterSize +
|
||||||
|
@ -337,6 +342,7 @@ class SqlEditor extends React.PureComponent {
|
||||||
<div ref={this.northPaneRef} className="north-pane">
|
<div ref={this.northPaneRef} className="north-pane">
|
||||||
<AceEditorWrapper
|
<AceEditorWrapper
|
||||||
actions={this.props.actions}
|
actions={this.props.actions}
|
||||||
|
autocomplete={this.state.autocompleteEnabled}
|
||||||
onBlur={this.setQueryEditorSql}
|
onBlur={this.setQueryEditorSql}
|
||||||
onChange={this.onSqlChanged}
|
onChange={this.onSqlChanged}
|
||||||
queryEditor={this.props.queryEditor}
|
queryEditor={this.props.queryEditor}
|
||||||
|
@ -502,6 +508,16 @@ class SqlEditor extends React.PureComponent {
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
<div className="rightItems">
|
<div className="rightItems">
|
||||||
|
<span>
|
||||||
|
<Checkbox
|
||||||
|
checked={this.state.autocompleteEnabled}
|
||||||
|
inline
|
||||||
|
title={t('Autocomplete')}
|
||||||
|
onChange={this.handleToggleAutocompleteEnabled}
|
||||||
|
>
|
||||||
|
{t('Autocomplete')}
|
||||||
|
</Checkbox>
|
||||||
|
</span>
|
||||||
<TemplateParamsEditor
|
<TemplateParamsEditor
|
||||||
language="json"
|
language="json"
|
||||||
onChange={params => {
|
onChange={params => {
|
||||||
|
|
Loading…
Reference in New Issue