Fix SQL Lab window resizing layout bug (#7615)

This commit is contained in:
Erik Ritter 2019-05-30 10:37:24 -07:00 committed by Grace Guo
parent 34407e8962
commit 145d72c52b
4 changed files with 80 additions and 16 deletions

View File

@ -20,10 +20,19 @@ import React from 'react';
import { shallow } from 'enzyme';
import { defaultQueryEditor, initialState, queries, table } from './fixtures';
import {
SQL_EDITOR_GUTTER_HEIGHT,
SQL_EDITOR_GUTTER_MARGIN,
SQL_TOOLBAR_HEIGHT,
} from '../../../src/SqlLab/constants';
import AceEditorWrapper from '../../../src/SqlLab/components/AceEditorWrapper';
import LimitControl from '../../../src/SqlLab/components/LimitControl';
import SouthPane from '../../../src/SqlLab/components/SouthPane';
import SqlEditor from '../../../src/SqlLab/components/SqlEditor';
import SqlEditorLeftBar from '../../../src/SqlLab/components/SqlEditorLeftBar';
const MOCKED_SQL_EDITOR_HEIGHT = 500;
describe('SqlEditor', () => {
const mockedProps = {
actions: {},
@ -40,7 +49,7 @@ describe('SqlEditor', () => {
};
beforeAll(() => {
jest.spyOn(SqlEditor.prototype, 'getSqlEditorHeight').mockImplementation(() => 500);
jest.spyOn(SqlEditor.prototype, 'getSqlEditorHeight').mockImplementation(() => MOCKED_SQL_EDITOR_HEIGHT);
});
it('is valid', () => {
@ -52,6 +61,33 @@ describe('SqlEditor', () => {
const wrapper = shallow(<SqlEditor {...mockedProps} />);
expect(wrapper.find(SqlEditorLeftBar)).toHaveLength(1);
});
it('render an AceEditorWrapper', () => {
const wrapper = shallow(<SqlEditor {...mockedProps} />);
expect(wrapper.find(AceEditorWrapper)).toHaveLength(1);
});
it('render an SouthPane', () => {
const wrapper = shallow(<SqlEditor {...mockedProps} />);
expect(wrapper.find(SouthPane)).toHaveLength(1);
});
it('does not overflow the editor window', () => {
const wrapper = shallow(<SqlEditor {...mockedProps} />);
const totalSize = parseFloat(wrapper.find(AceEditorWrapper).props().height)
+ wrapper.find(SouthPane).props().height
+ SQL_TOOLBAR_HEIGHT
+ (SQL_EDITOR_GUTTER_MARGIN * 2)
+ SQL_EDITOR_GUTTER_HEIGHT;
expect(totalSize).toEqual(MOCKED_SQL_EDITOR_HEIGHT);
});
it('does not overflow the editor window after resizing', () => {
const wrapper = shallow(<SqlEditor {...mockedProps} />);
wrapper.setState({ height: 450 });
const totalSize = parseFloat(wrapper.find(AceEditorWrapper).props().height)
+ wrapper.find(SouthPane).props().height
+ SQL_TOOLBAR_HEIGHT
+ (SQL_EDITOR_GUTTER_MARGIN * 2)
+ SQL_EDITOR_GUTTER_HEIGHT;
expect(totalSize).toEqual(450);
});
it('render a LimitControl with default limit', () => {
const defaultQueryLimit = 101;
const updatedProps = { ...mockedProps, defaultQueryLimit };

View File

@ -31,6 +31,7 @@ import {
import Split from 'react-split';
import { t } from '@superset-ui/translation';
import debounce from 'lodash/debounce';
import throttle from 'lodash/throttle';
import Button from '../../components/Button';
import LimitControl from './LimitControl';
@ -43,17 +44,20 @@ import Timer from '../../components/Timer';
import Hotkeys from '../../components/Hotkeys';
import SqlEditorLeftBar from './SqlEditorLeftBar';
import AceEditorWrapper from './AceEditorWrapper';
import { STATE_BSSTYLE_MAP } from '../constants';
import {
STATE_BSSTYLE_MAP,
SQL_EDITOR_GUTTER_HEIGHT,
SQL_EDITOR_GUTTER_MARGIN,
SQL_TOOLBAR_HEIGHT,
} from '../constants';
import RunQueryActionButton from './RunQueryActionButton';
import { FeatureFlag, isFeatureEnabled } from '../../featureFlags';
const SQL_EDITOR_PADDING = 10;
const SQL_TOOLBAR_HEIGHT = 51;
const GUTTER_HEIGHT = 5;
const GUTTER_MARGIN = 3;
const INITIAL_NORTH_PERCENT = 30;
const INITIAL_SOUTH_PERCENT = 70;
const VALIDATION_DEBOUNCE_MS = 600;
const WINDOW_RESIZE_THROTTLE_MS = 100;
const propTypes = {
actions: PropTypes.object.isRequired,
@ -83,6 +87,8 @@ class SqlEditor extends React.PureComponent {
this.state = {
autorun: props.queryEditor.autorun,
ctas: '',
northPercent: INITIAL_NORTH_PERCENT,
southPercent: INITIAL_SOUTH_PERCENT,
sql: props.queryEditor.sql,
};
this.sqlEditorRef = React.createRef();
@ -103,6 +109,10 @@ class SqlEditor extends React.PureComponent {
this.requestValidation.bind(this),
VALIDATION_DEBOUNCE_MS,
);
this.handleWindowResize = throttle(
this.handleWindowResize.bind(this),
WINDOW_RESIZE_THROTTLE_MS,
);
}
componentWillMount() {
if (this.state.autorun) {
@ -116,6 +126,11 @@ class SqlEditor extends React.PureComponent {
// the south pane so it gets rendered properly
// eslint-disable-next-line react/no-did-mount-set-state
this.setState({ height: this.getSqlEditorHeight() });
window.addEventListener('resize', this.handleWindowResize);
}
componentWillUnmount() {
window.removeEventListener('resize', this.handleWindowResize);
}
onResizeStart() {
// Set the heights on the ace editor and the ace content area after drag starts
@ -124,8 +139,7 @@ class SqlEditor extends React.PureComponent {
document.getElementsByClassName('ace_content')[0].style.height = '100%';
}
onResizeEnd([northPercent, southPercent]) {
this.setState(this.getAceEditorAndSouthPaneHeights(
this.state.height, northPercent, southPercent));
this.setState({ northPercent, southPercent });
if (this.northPaneRef.current && this.northPaneRef.current.clientHeight) {
this.props.actions.persistEditorHeight(this.props.queryEditor,
@ -149,9 +163,11 @@ class SqlEditor extends React.PureComponent {
// given the height of the sql editor, north pane percent and south pane percent.
getAceEditorAndSouthPaneHeights(height, northPercent, southPercent) {
return {
aceEditorHeight: height * northPercent / 100 - (GUTTER_HEIGHT / 2 + GUTTER_MARGIN)
aceEditorHeight: height * northPercent / 100
- (SQL_EDITOR_GUTTER_HEIGHT / 2 + SQL_EDITOR_GUTTER_MARGIN)
- SQL_TOOLBAR_HEIGHT,
southPaneHeight: height * southPercent / 100 - (GUTTER_HEIGHT / 2 + GUTTER_MARGIN),
southPaneHeight: height * southPercent / 100
- (SQL_EDITOR_GUTTER_HEIGHT / 2 + SQL_EDITOR_GUTTER_MARGIN),
};
}
getHotkeyConfig() {
@ -194,9 +210,12 @@ class SqlEditor extends React.PureComponent {
setQueryLimit(queryLimit) {
this.props.actions.queryEditorSetQueryLimit(this.props.queryEditor, queryLimit);
}
handleWindowResize() {
this.setState({ height: this.getSqlEditorHeight() });
}
elementStyle(dimension, elementSize, gutterSize) {
return {
[dimension]: `calc(${elementSize}% - ${gutterSize + GUTTER_MARGIN}px)`,
[dimension]: `calc(${elementSize}% - ${gutterSize + SQL_EDITOR_GUTTER_MARGIN}px)`,
};
}
requestValidation() {
@ -257,15 +276,18 @@ class SqlEditor extends React.PureComponent {
queryPane() {
const hotkeys = this.getHotkeyConfig();
const { aceEditorHeight, southPaneHeight } = this.getAceEditorAndSouthPaneHeights(
this.state.height, INITIAL_NORTH_PERCENT, INITIAL_SOUTH_PERCENT);
this.state.height,
this.state.northPercent,
this.state.southPercent,
);
return (
<Split
className="queryPane"
sizes={[INITIAL_NORTH_PERCENT, INITIAL_SOUTH_PERCENT]}
sizes={[this.state.northPercent, this.state.southPercent]}
elementStyle={this.elementStyle}
minSize={200}
direction="vertical"
gutterSize={GUTTER_HEIGHT}
gutterSize={SQL_EDITOR_GUTTER_HEIGHT}
onDragStart={this.onResizeStart}
onDragEnd={this.onResizeEnd}
>
@ -277,7 +299,7 @@ class SqlEditor extends React.PureComponent {
queryEditor={this.props.queryEditor}
sql={this.props.queryEditor.sql}
tables={this.props.tables}
height={`${this.state.aceEditorHeight || aceEditorHeight}px`}
height={`${aceEditorHeight}px`}
hotkeys={hotkeys}
/>
{this.renderEditorBottomBar(hotkeys)}
@ -286,7 +308,7 @@ class SqlEditor extends React.PureComponent {
editorQueries={this.props.editorQueries}
dataPreviewQueries={this.props.dataPreviewQueries}
actions={this.props.actions}
height={this.state.southPaneHeight || southPaneHeight}
height={southPaneHeight}
/>
</Split>
);

View File

@ -33,9 +33,10 @@ const propTypes = {
};
const defaultProps = {
tables: [],
actions: {},
height: 500,
offline: false,
tables: [],
};
export default class SqlEditorLeftBar extends React.PureComponent {

View File

@ -43,3 +43,8 @@ export const TIME_OPTIONS = [
'90 days ago',
'1 year ago',
];
// SqlEditor layout constants
export const SQL_EDITOR_GUTTER_HEIGHT = 5;
export const SQL_EDITOR_GUTTER_MARGIN = 3;
export const SQL_TOOLBAR_HEIGHT = 51;