Linted all, refactored VisualizeModal out

This commit is contained in:
Maxime Beauchemin
2016-08-06 23:08:24 -07:00
parent 07a6a0a630
commit dbef3543a9
15 changed files with 300 additions and 209 deletions

View File

@@ -1,33 +1,31 @@
import React from 'react';
import { Button, OverlayTrigger, Tooltip } from 'react-bootstrap';
class ButtonWithTooltip extends React.Component {
render() {
let tooltip = (
<Tooltip id="tooltip">
{this.props.tooltip}
</Tooltip>
);
return (
<OverlayTrigger
overlay={tooltip}
delayShow={300}
placement={this.props.placement}
delayHide={150}
const ButtonWithTooltip = (props) => {
let tooltip = (
<Tooltip id="tooltip">
{props.tooltip}
</Tooltip>
);
return (
<OverlayTrigger
overlay={tooltip}
delayShow={300}
placement={props.placement}
delayHide={150}
>
<Button
onClick={props.onClick}
bsStyle={props.bsStyle}
disabled={props.disabled}
className={props.className}
>
<Button
onClick={this.props.onClick}
bsStyle={this.props.bsStyle}
disabled={this.props.disabled}
className={this.props.className}
>
{this.props.children}
</Button>
</OverlayTrigger>
);
}
}
{props.children}
</Button>
</OverlayTrigger>
);
};
ButtonWithTooltip.defaultProps = {
onClick: () => {},
disabled: false,

View File

@@ -8,51 +8,52 @@ import QueryLink from './QueryLink';
// CSS
import 'react-select/dist/react-select.css';
class LeftPane extends React.Component {
render() {
let queryElements;
if (this.props.workspaceQueries.length > 0) {
queryElements = this.props.workspaceQueries.map((q) => <QueryLink query={q} />);
} else {
queryElements = (
<Alert bsStyle="info">
Use the save button on the SQL editor to save a query into this section for
future reference
</Alert>
);
}
return (
<div className="panel panel-default LeftPane">
<div className="panel-heading">
<h6 className="m-r-10">
<i className="fa fa-flask" />
SQL Lab <Label bsStyle="danger">ALPHA</Label>
</h6>
</div>
<div className="panel-body">
<div>
<h6>
<span className="fa-stack">
<i className="fa fa-database fa-stack-lg"></i>
<i className="fa fa-search fa-stack-1x"></i>
</span> Saved Queries
</h6>
<div>
{queryElements}
</div>
<hr />
<Button onClick={this.props.actions.resetState.bind(this)}>
Reset State
</Button>
</div>
</div>
</div>
const LeftPane = (props) => {
let queryElements;
if (props.workspaceQueries.length > 0) {
queryElements = props.workspaceQueries.map((q) => <QueryLink query={q} />);
} else {
queryElements = (
<Alert bsStyle="info">
Use the save button on the SQL editor to save a query into this section for
future reference
</Alert>
);
}
}
return (
<div className="panel panel-default LeftPane">
<div className="panel-heading">
<h6 className="m-r-10">
<i className="fa fa-flask" />
SQL Lab <Label bsStyle="danger">ALPHA</Label>
</h6>
</div>
<div className="panel-body">
<div>
<h6>
<span className="fa-stack">
<i className="fa fa-database fa-stack-lg"></i>
<i className="fa fa-search fa-stack-1x"></i>
</span> Saved Queries
</h6>
<div>
{queryElements}
</div>
<hr />
<Button onClick={props.actions.resetState.bind(this)}>
Reset State
</Button>
</div>
</div>
</div>
);
};
LeftPane.propTypes = {
workspaceQueries: React.PropTypes.array,
actions: React.PropTypes.object,
};
LeftPane.defaultProps = {
workspaceQueries: [],
};

View File

@@ -7,30 +7,29 @@ import * as Actions from '../actions';
import QueryTable from './QueryTable';
import { Alert } from 'react-bootstrap';
class QueryLog extends React.Component {
render() {
const activeQeId = this.props.tabHistory[this.props.tabHistory.length - 1];
const queries = this.props.queries.filter((q) => (q.sqlEditorId === activeQeId));
if (queries.length > 0) {
return (
<QueryTable
columns={['state', 'started', 'duration', 'rows', 'sql', 'actions']}
queries={queries}
/>
);
}
const QueryHistory = (props) => {
const activeQeId = props.tabHistory[props.tabHistory.length - 1];
const queries = props.queries.filter((q) => (q.sqlEditorId === activeQeId));
if (queries.length > 0) {
return (
<Alert bsStyle="info">
No query history yet...
</Alert>
<QueryTable
columns={['state', 'started', 'duration', 'rows', 'sql', 'actions']}
queries={queries}
/>
);
}
}
QueryLog.defaultProps = {
return (
<Alert bsStyle="info">
No query history yet...
</Alert>
);
};
QueryHistory.defaultProps = {
queries: [],
};
QueryLog.propTypes = {
QueryHistory.propTypes = {
queries: React.PropTypes.array,
tabHistory: React.PropTypes.array,
actions: React.PropTypes.object,
@@ -48,4 +47,4 @@ function mapDispatchToProps(dispatch) {
};
}
export default connect(mapStateToProps, mapDispatchToProps)(QueryLog);
export default connect(mapStateToProps, mapDispatchToProps)(QueryHistory);

View File

@@ -1,12 +1,9 @@
import React from 'react';
import { Alert, Modal } from 'react-bootstrap';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as Actions from '../actions';
import Select from 'react-select';
import moment from 'moment';
import { Table } from 'reactable';
@@ -14,6 +11,7 @@ import SyntaxHighlighter from 'react-syntax-highlighter';
import { github } from 'react-syntax-highlighter/dist/styles';
import Link from './Link';
import VisualizeModal from './VisualizeModal';
// TODO move to CSS
const STATE_COLOR_MAP = {
@@ -35,9 +33,7 @@ class QueryTable extends React.Component {
}
showVisualizeModal(query) {
this.setState({ showVisualizeModal: true });
this.state.activeQuery = query;
}
changeChartType(event) {
this.setState({ activeQuery: query });
}
render() {
const data = this.props.queries.map((query) => {
@@ -87,58 +83,13 @@ class QueryTable extends React.Component {
return q;
}).reverse();
let visualizeModalBody;
if (this.state.activeQuery) {
const cols = this.state.activeQuery.results.columns;
visualizeModalBody = (
<div>
<Select
name="select-chart-type"
placeholder="[Chart Type]"
options={[
{ value: 'line', label: 'Time Series - Line Chart' },
{ value: 'bar', label: 'Time Series - Bar Chart' },
{ value: 'bar_dist', label: 'Distribution - Bar Chart' },
{ value: 'pie', label: 'Pie Chart' },
]}
value={null}
autosize={false}
onChange={this.changeChartType.bind(this)}
/>
<Table
className="table table-condensed"
columns={['column', 'is_dimension', 'is_date', 'agg_func']}
data={cols.map((col) => ({
column: col,
is_dimension: <input type="checkbox" className="form-control" />,
is_date: <input type="checkbox" className="form-control" />,
agg_func: (
<Select
options={[
{ value: 'sum', label: 'SUM(x)' },
{ value: 'min', label: 'MIN(x)' },
{ value: 'max', label: 'MAX(x)' },
{ value: 'avg', label: 'AVG(x)' },
{ value: 'count_distinct', label: 'COUNT(DISTINCT x)' },
]}
/>
),
}))}
/>
</div>
);
}
return (
<div>
<Modal show={this.state.showVisualizeModal} onHide={this.hideVisualizeModal.bind(this)}>
<Modal.Header closeButton>
<Modal.Title>Visualize (mock)</Modal.Title>
</Modal.Header>
<Modal.Body>
<Alert bsStyle="danger">Not functional - Work in progress!</Alert>
{visualizeModalBody}
</Modal.Body>
</Modal>
<VisualizeModal
show={this.state.showVisualizeModal}
query={this.state.activeQuery}
onHide={this.hideVisualizeModal.bind(this)}
/>
<Table
columns={['state', 'started', 'duration', 'rows', 'sql', 'actions']}
className="table table-condensed"
@@ -158,9 +109,8 @@ QueryTable.defaultProps = {
queries: [],
};
function mapStateToProps(state) {
return {
};
function mapStateToProps() {
return {};
}
function mapDispatchToProps(dispatch) {
return {

View File

@@ -2,10 +2,31 @@ import React from 'react';
import { Alert, Button } from 'react-bootstrap';
import { Table } from 'reactable';
import VisualizeModal from './VisualizeModal';
class ResultSet extends React.Component {
shouldComponentUpdate() {
return false;
constructor(props) {
super(props);
this.state = {
searchText: '',
showModal: false,
};
}
shouldComponentUpdate(nextProps, nextState) {
return (
this.state.searchText !== nextState.searchText ||
this.state.showModal !== nextState.showModal
);
}
changeSearch(event) {
this.setState({ searchText: event.target.value });
}
showModal() {
this.setState({ showModal: true });
}
hideModal() {
this.setState({ showModal: false });
}
render() {
const results = this.props.query.results;
@@ -15,11 +36,18 @@ class ResultSet extends React.Component {
<div className="ResultSetControls">
<div className="clearfix">
<div className="pull-left">
<Button className="m-r-5"><i className="fa fa-line-chart" />Visualize</Button>
<Button className="m-r-5"><i className="fa fa-file-text-o" />.CSV</Button>
<Button className="m-r-5" onClick={this.showModal.bind(this)}>
<i className="fa fa-line-chart m-l-1" /> Visualize
</Button>
<Button className="m-r-5"><i className="fa fa-file-text-o" /> .CSV</Button>
</div>
<div className="pull-right">
<input type="text" className="form-control" placeholder="Search Results" />
<input
type="text"
onChange={this.changeSearch.bind(this)}
className="form-control"
placeholder="Search Results"
/>
</div>
</div>
</div>
@@ -27,14 +55,24 @@ class ResultSet extends React.Component {
}
if (results.data.length > 0) {
return (
<div className="ResultSet">
{controls}
<Table
data={results.data}
columns={results.columns}
sortable
className="table table-condensed table-bordered"
<div>
<VisualizeModal
show={this.state.showModal}
query={this.props.query}
onHide={this.hideModal.bind(this)}
/>
{controls}
<div className="ResultSet">
<Table
data={results.data}
columns={results.columns}
sortable
className="table table-condensed table-bordered"
filterBy={this.state.searchText}
filterable={results.columns}
hideFilterInput
/>
</div>
</div>
);
}
@@ -45,10 +83,12 @@ ResultSet.propTypes = {
query: React.PropTypes.object,
showControls: React.PropTypes.boolean,
search: React.PropTypes.boolean,
searchText: React.PropTypes.string,
};
ResultSet.defaultProps = {
showControls: true,
search: true,
searchText: '',
};
export default ResultSet;

View File

@@ -1,38 +1,36 @@
import { Tab, Tabs } from 'react-bootstrap';
import QueryLog from './QueryLog';
import QueryHistory from './QueryHistory';
import ResultSet from './ResultSet';
import React from 'react';
class SouthPane extends React.Component {
render() {
let results;
if (this.props.latestQuery) {
if (this.props.latestQuery.state === 'running') {
results = (
<img className="loading" alt="Loading.." src="/static/assets/images/loading.gif" />
);
} else if (this.props.latestQuery.state === 'failed') {
results = <div className="alert alert-danger">{this.props.latestQuery.msg}</div>;
} else if (this.props.latestQuery.state === 'success') {
results = <ResultSet showControls query={this.props.latestQuery} />;
}
} else {
results = <div className="alert alert-info">Run a query to display results here</div>;
const SouthPane = (props) => {
let results;
if (props.latestQuery) {
if (props.latestQuery.state === 'running') {
results = (
<img className="loading" alt="Loading.." src="/static/assets/images/loading.gif" />
);
} else if (props.latestQuery.state === 'failed') {
results = <div className="alert alert-danger">{props.latestQuery.msg}</div>;
} else if (props.latestQuery.state === 'success') {
results = <ResultSet showControls query={props.latestQuery} />;
}
return (
<Tabs bsStyle="pills">
<Tab title="Results" eventKey={1}>
<div style={{ overflow: 'auto' }}>
{results}
</div>
</Tab>
<Tab title="Query Log" eventKey={2}>
<QueryLog />
</Tab>
</Tabs>
);
} else {
results = <div className="alert alert-info">Run a query to display results here</div>;
}
}
return (
<Tabs bsStyle="pills">
<Tab title="Results" eventKey={1}>
<div style={{ overflow: 'auto' }}>
{results}
</div>
</Tab>
<Tab title="Query History" eventKey={2}>
<QueryHistory />
</Tab>
</Tabs>
);
};
SouthPane.propTypes = {
latestQuery: React.PropTypes.object,

View File

@@ -1,6 +1,14 @@
const $ = window.$ = require('jquery');
import React from 'react';
import { Button, ButtonGroup, DropdownButton, Label, MenuItem, OverlayTrigger, Tooltip } from 'react-bootstrap';
import {
Button,
ButtonGroup,
DropdownButton,
Label,
MenuItem,
OverlayTrigger,
Tooltip,
} from 'react-bootstrap';
import AceEditor from 'react-ace';
import 'brace/mode/sql';
@@ -29,6 +37,9 @@ class SqlEditor extends React.Component {
};
}
componentDidMount() {
this.onMount();
}
onMount() {
if (this.state.autorun) {
this.setState({ autorun: false });
this.props.actions.queryEditorSetAutorun(this.props.queryEditor, false);
@@ -94,12 +105,9 @@ class SqlEditor extends React.Component {
this.props.actions.stopQuery(this.props.latestQuery);
}
textChange(text) {
this.setState({ sql: text })
this.setState({ sql: text });
this.props.actions.queryEditorSetSql(this.props.queryEditor, text);
}
notImplemented() {
alert('Not implemented');
}
addWorkspaceQuery() {
this.props.actions.addWorkspaceQuery({
id: shortid.generate(),
@@ -152,12 +160,12 @@ class SqlEditor extends React.Component {
</ButtonGroup>
);
let limitWarning = null;
const row_limit = 1000;
if (this.props.latestQuery && this.props.latestQuery.rows === row_limit) {
const rowLimit = 1000;
if (this.props.latestQuery && this.props.latestQuery.rows === rowLimit) {
const tooltip = (
<Tooltip id="tooltip">
It appears that the number of rows in the query results displayed
was limited on the server side to the {row_limit} limit.
was limited on the server side to the {rowLimit} limit.
</Tooltip>
);
limitWarning = (
@@ -219,9 +227,8 @@ SqlEditor.propTypes = {
SqlEditor.defaultProps = {
};
function mapStateToProps(state) {
return {
};
function mapStateToProps() {
return {};
}
function mapDispatchToProps(dispatch) {

View File

@@ -123,9 +123,6 @@ class SqlEditorTopToolbar extends React.Component {
that.setState({ databaseLoading: false });
});
}
notImplemented() {
alert('Not implemented');
}
closePopover(ref) {
this.refs[ref].hide();
}

View File

@@ -35,10 +35,9 @@ class QueryEditors extends React.Component {
}
}
render() {
const that = this;
const editors = this.props.queryEditors.map((qe, i) => {
let latestQuery;
that.props.queries.forEach((q) => {
this.props.queries.forEach((q) => {
if (q.id === qe.latestQueryId) {
latestQuery = q;
}
@@ -53,10 +52,10 @@ class QueryEditors extends React.Component {
className="no-shadow"
id="bg-vertical-dropdown-1"
>
<MenuItem eventKey="1" onClick={that.props.actions.removeQueryEditor.bind(that, qe)}>
<MenuItem eventKey="1" onClick={this.props.actions.removeQueryEditor.bind(this, qe)}>
<i className="fa fa-close" /> close tab
</MenuItem>
<MenuItem eventKey="2" onClick={that.renameTab.bind(that, qe)}>
<MenuItem eventKey="2" onClick={this.renameTab.bind(this, qe)}>
<i className="fa fa-i-cursor" /> rename tab
</MenuItem>
</DropdownButton>
@@ -90,8 +89,9 @@ class QueryEditors extends React.Component {
}
QueryEditors.propTypes = {
actions: React.PropTypes.object,
tabHistory: React.PropTypes.array,
queries: React.PropTypes.array,
queryEditors: React.PropTypes.array,
tabHistory: React.PropTypes.array,
workspaceDatabase: React.PropTypes.object,
};
QueryEditors.defaultProps = {

View File

@@ -12,10 +12,9 @@ import 'react-select/dist/react-select.css';
class TableWorkspaceElement extends React.Component {
selectStar() {
let cols = '';
const that = this;
this.props.table.columns.forEach(function (col, i) {
this.props.table.columns.forEach((col, i) => {
cols += col.name;
if (i < that.props.table.columns.length - 1) {
if (i < this.props.table.columns.length - 1) {
cols += ', ';
}
});

View File

@@ -0,0 +1,93 @@
import React from 'react';
import { Alert, Modal } from 'react-bootstrap';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as Actions from '../actions';
import Select from 'react-select';
import { Table } from 'reactable';
class VisualizeModal extends React.Component {
constructor(props) {
super(props);
this.state = {
chartType: null,
};
}
changeChartType(event) {
this.setState({ chartType: event.target.value });
}
render() {
if (!(this.props.query)) {
return <div />;
}
const cols = this.props.query.results.columns;
const modal = (
<div className="VisualizeModal">
<Modal show={this.props.show} onHide={this.props.onHide}>
<Modal.Header closeButton>
<Modal.Title>Visualize (mock)</Modal.Title>
</Modal.Header>
<Modal.Body>
<Alert bsStyle="danger">Not functional - Work in progress!</Alert>
<div>
<Select
name="select-chart-type"
placeholder="[Chart Type]"
options={[
{ value: 'line', label: 'Time Series - Line Chart' },
{ value: 'bar', label: 'Time Series - Bar Chart' },
{ value: 'bar_dist', label: 'Distribution - Bar Chart' },
{ value: 'pie', label: 'Pie Chart' },
]}
value={this.state.chartType}
autosize={false}
onChange={this.changeChartType.bind(this)}
/>
<Table
className="table table-condensed"
columns={['column', 'is_dimension', 'is_date', 'agg_func']}
data={cols.map((col) => ({
column: col,
is_dimension: <input type="checkbox" className="form-control" />,
is_date: <input type="checkbox" className="form-control" />,
agg_func: (
<Select
options={[
{ value: 'sum', label: 'SUM(x)' },
{ value: 'min', label: 'MIN(x)' },
{ value: 'max', label: 'MAX(x)' },
{ value: 'avg', label: 'AVG(x)' },
{ value: 'count_distinct', label: 'COUNT(DISTINCT x)' },
]}
/>
),
}))}
/>
</div>
</Modal.Body>
</Modal>
</div>
);
return modal;
}
}
VisualizeModal.propTypes = {
query: React.PropTypes.object,
show: React.PropTypes.boolean,
onHide: React.PropTypes.function,
};
VisualizeModal.defaultProps = {
show: false,
};
function mapStateToProps() {
return {};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(VisualizeModal);