[sqllab] some frontend tests (#1400)

* [sqllab] some frontend tests

* linting

* Addressing comments

* Addressing unaddressed comments

* Touchups
This commit is contained in:
Maxime Beauchemin
2016-10-25 16:44:32 -07:00
committed by GitHub
parent 7c5933732b
commit 940659bc14
44 changed files with 875 additions and 558 deletions

View File

@@ -1,8 +1,5 @@
import React from 'react';
import { Alert } from 'react-bootstrap';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as Actions from '../actions';
class Alerts extends React.Component {
removeAlert(alert) {
@@ -11,6 +8,7 @@ class Alerts extends React.Component {
render() {
const alerts = this.props.alerts.map((alert) =>
<Alert
key={alert.id}
bsStyle={alert.bsStyle}
style={{ width: '500px', textAlign: 'midddle', margin: '10px auto' }}
>
@@ -33,9 +31,4 @@ Alerts.propTypes = {
actions: React.PropTypes.object,
};
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch),
};
}
export default connect(null, mapDispatchToProps)(Alerts);
export default Alerts;

View File

@@ -48,7 +48,7 @@ class App extends React.Component {
}
return (
<div className="App SqlLab">
<Alerts alerts={this.props.alerts} />
<Alerts alerts={this.props.alerts} actions={this.props.actions} />
<DataPreviewModal />
<div className="container-fluid">
{content}
@@ -60,6 +60,7 @@ class App extends React.Component {
App.propTypes = {
alerts: React.PropTypes.array,
actions: React.PropTypes.object,
};
function mapStateToProps(state) {
@@ -73,4 +74,5 @@ function mapDispatchToProps(dispatch) {
};
}
export { App };
export default connect(mapStateToProps, mapDispatchToProps)(App);

View File

@@ -1,48 +0,0 @@
import React from 'react';
import { Button, OverlayTrigger, Tooltip } from 'react-bootstrap';
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}
bsSize={props.bsSize}
disabled={props.disabled}
className={props.className}
>
{props.children}
</Button>
</OverlayTrigger>
);
};
ButtonWithTooltip.defaultProps = {
onClick: () => {},
disabled: false,
placement: 'top',
bsStyle: 'default',
};
ButtonWithTooltip.propTypes = {
bsSize: React.PropTypes.string,
bsStyle: React.PropTypes.string,
children: React.PropTypes.element,
className: React.PropTypes.string,
disabled: React.PropTypes.bool,
onClick: React.PropTypes.func,
placement: React.PropTypes.string,
tooltip: React.PropTypes.string,
};
export default ButtonWithTooltip;

View File

@@ -3,11 +3,11 @@ import CopyToClipboard from '../../components/CopyToClipboard';
import { getShortUrl } from '../../../utils/common';
const propTypes = {
qe: React.PropTypes.object,
queryEditor: React.PropTypes.object,
};
const defaultProps = {
qe: null,
queryEditor: null,
};
export default class CopyQueryTabUrl extends React.Component {
@@ -20,7 +20,7 @@ export default class CopyQueryTabUrl extends React.Component {
componentWillMount() {
const params = [];
const qe = this.props.qe;
const qe = this.props.queryEditor;
if (qe.dbId) params.push('dbid=' + qe.dbId);
if (qe.title) params.push('title=' + encodeURIComponent(qe.title));
if (qe.schema) params.push('schema=' + encodeURIComponent(qe.schema));

View File

@@ -32,7 +32,7 @@ class DataPreviewModal extends React.Component {
</Modal.Title>
</Modal.Header>
<Modal.Body>
<ResultSet query={query} visualize={false} csv={false} />
<ResultSet query={query} visualize={false} csv={false} actions={this.props.actions} />
</Modal.Body>
</Modal>
);

View File

@@ -1,9 +1,6 @@
const $ = window.$ = require('jquery');
import React from 'react';
import { bindActionCreators } from 'redux';
import Select from 'react-select';
import { connect } from 'react-redux';
import * as Actions from '../actions';
class DatabaseSelect extends React.Component {
constructor(props) {
@@ -53,15 +50,4 @@ DatabaseSelect.propTypes = {
valueRenderer: React.PropTypes.func,
};
DatabaseSelect.defaultProps = {
onChange: () => {},
databaseId: null,
};
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch),
};
}
export default connect(null, mapDispatchToProps)(DatabaseSelect);
export default DatabaseSelect;

View File

@@ -1,6 +1,24 @@
import React from 'react';
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
const propTypes = {
children: React.PropTypes.node,
className: React.PropTypes.string,
href: React.PropTypes.string,
onClick: React.PropTypes.func,
placement: React.PropTypes.string,
style: React.PropTypes.object,
tooltip: React.PropTypes.string,
};
const defaultProps = {
className: '',
href: '#',
onClick: () => {},
placement: 'top',
style: {},
tooltip: null,
};
class Link extends React.Component {
render() {
@@ -34,21 +52,7 @@ class Link extends React.Component {
return link;
}
}
Link.propTypes = {
children: React.PropTypes.object,
className: React.PropTypes.string,
href: React.PropTypes.string,
onClick: React.PropTypes.func,
placement: React.PropTypes.string,
style: React.PropTypes.object,
tooltip: React.PropTypes.string,
};
Link.defaultProps = {
disabled: false,
href: '#',
tooltip: null,
placement: 'top',
onClick: () => {},
};
Link.propTypes = propTypes;
Link.defaultProps = defaultProps;
export default Link;

View File

@@ -30,14 +30,10 @@ class QueryAutoRefresh extends React.Component {
if (Object.keys(data).length > 0) {
this.props.actions.refreshQueries(data);
}
if (!this.props.networkOn) {
this.props.actions.setNetworkStatus(true);
}
this.props.actions.setNetworkStatus(true);
})
.fail(() => {
if (this.props.networkOn) {
this.props.actions.setNetworkStatus(false);
}
this.props.actions.setNetworkStatus(false);
});
}
render() {
@@ -47,7 +43,6 @@ class QueryAutoRefresh extends React.Component {
QueryAutoRefresh.propTypes = {
actions: React.PropTypes.object,
queriesLastUpdate: React.PropTypes.number,
networkOn: React.PropTypes.bool,
};
QueryAutoRefresh.defaultProps = {
// queries: null,
@@ -56,7 +51,6 @@ QueryAutoRefresh.defaultProps = {
function mapStateToProps(state) {
return {
queriesLastUpdate: state.queriesLastUpdate,
networkOn: state.networkOn,
};
}

View File

@@ -1,28 +1,23 @@
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as Actions from '../actions';
import QueryTable from './QueryTable';
import { Alert } from 'react-bootstrap';
const propTypes = {
queries: React.PropTypes.array.isRequired,
actions: React.PropTypes.object.isRequired,
};
const QueryHistory = (props) => {
const activeQeId = props.tabHistory[props.tabHistory.length - 1];
const queriesArray = [];
for (const id in props.queries) {
if (props.queries[id].sqlEditorId === activeQeId) {
queriesArray.push(props.queries[id]);
}
}
if (queriesArray.length > 0) {
if (props.queries.length > 0) {
return (
<QueryTable
columns={[
'state', 'started', 'duration', 'progress',
'rows', 'sql', 'output', 'actions',
]}
queries={queriesArray}
queries={props.queries}
actions={props.actions}
/>
);
}
@@ -32,27 +27,6 @@ const QueryHistory = (props) => {
</Alert>
);
};
QueryHistory.propTypes = propTypes;
QueryHistory.defaultProps = {
queries: {},
};
QueryHistory.propTypes = {
queries: React.PropTypes.object,
tabHistory: React.PropTypes.array,
actions: React.PropTypes.object,
};
function mapStateToProps(state) {
return {
queries: state.queries,
tabHistory: state.tabHistory,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(QueryHistory);
export default QueryHistory;

View File

@@ -1,62 +0,0 @@
import React from 'react';
import Link from './Link';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as Actions from '../actions';
import shortid from 'shortid';
class QueryLink extends React.Component {
popTab() {
const qe = {
id: shortid.generate(),
title: this.props.query.title,
dbId: this.props.query.dbId,
autorun: false,
sql: this.props.query.sql,
};
this.props.actions.addQueryEditor(qe);
}
render() {
return (
<div>
<div className="clearfix">
<div className="pull-left">
<a
href="#"
tooltip="Pop this query in a new tab"
onClick={this.popTab.bind(this)}
>
{this.props.query.title}
</a>
</div>
<div className="pull-right">
<Link
onClick={this.props.actions.removeWorkspaceQuery.bind(this, this.props.query)}
tooltip="Remove query from workspace"
href="#"
>
&times;
</Link>
</div>
</div>
<hr />
</div>
);
}
}
QueryLink.propTypes = {
query: React.PropTypes.object,
actions: React.PropTypes.object,
};
QueryLink.defaultProps = {
};
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch),
};
}
export default connect(null, mapDispatchToProps)(QueryLink);

View File

@@ -7,6 +7,10 @@ import QueryTable from './QueryTable';
import DatabaseSelect from './DatabaseSelect';
import { STATUS_OPTIONS } from '../common';
const propTypes = {
actions: React.PropTypes.object.isRequired,
};
class QuerySearch extends React.Component {
constructor(props) {
super(props);
@@ -132,11 +136,11 @@ class QuerySearch extends React.Component {
onUserClicked={this.onUserClicked.bind(this)}
onDbClicked={this.onDbClicked.bind(this)}
queries={this.state.queriesArray}
actions={this.props.actions}
/>
</div>
);
}
}
QuerySearch.propTypes = propTypes;
export default QuerySearch;

View File

@@ -1,9 +1,5 @@
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as Actions from '../actions';
import moment from 'moment';
import { Table } from 'reactable';
import { Label, ProgressBar } from 'react-bootstrap';
@@ -67,6 +63,9 @@ class QueryTable extends React.Component {
clearQueryResults(query) {
this.props.actions.clearQueryResults(query);
}
removeQuery(query) {
this.props.actions.removeQuery(query);
}
render() {
const data = this.props.queries.map((query) => {
@@ -111,7 +110,7 @@ class QueryTable extends React.Component {
modalTitle={'Data preview'}
beforeOpen={this.openAsyncResults.bind(this, query)}
onExit={this.clearQueryResults.bind(this, query)}
modalBody={<ResultSet showSql query={query} />}
modalBody={<ResultSet showSql query={query} actions={this.props.actions} />}
/>
);
} else {
@@ -163,7 +162,7 @@ class QueryTable extends React.Component {
<Link
className="fa fa-trash m-r-3"
tooltip="Remove query from log"
onClick={this.props.actions.removeQuery.bind(this, query)}
onClick={this.removeQuery.bind(this, query)}
/>
</div>
);
@@ -198,13 +197,4 @@ class QueryTable extends React.Component {
QueryTable.propTypes = propTypes;
QueryTable.defaultProps = defaultProps;
function mapStateToProps() {
return {};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch),
};
}
export { QueryTable };
export default connect(mapStateToProps, mapDispatchToProps)(QueryTable);
export default QueryTable;

View File

@@ -3,10 +3,6 @@ import { Alert, Button, ButtonGroup, ProgressBar } from 'react-bootstrap';
import { Table } from 'reactable';
import shortid from 'shortid';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as Actions from '../actions';
import VisualizeModal from './VisualizeModal';
import HighlightedSql from './HighlightedSql';
@@ -116,7 +112,7 @@ class ResultSet extends React.Component {
if (this.props.showSql) {
sql = <HighlightedSql sql={query.sql} />;
}
if (['running', 'pending', 'fetching'].includes(query.state)) {
if (['running', 'pending', 'fetching'].indexOf(query.state) > -1) {
let progressBar;
if (query.progress > 0 && query.state === 'running') {
progressBar = (
@@ -191,12 +187,4 @@ class ResultSet extends React.Component {
ResultSet.propTypes = propTypes;
ResultSet.defaultProps = defaultProps;
function mapStateToProps() {
return {};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(ResultSet);
export default ResultSet;

View File

@@ -3,16 +3,21 @@ import QueryHistory from './QueryHistory';
import ResultSet from './ResultSet';
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as Actions from '../actions';
import shortid from 'shortid';
const propTypes = {
queries: React.PropTypes.array.isRequired,
actions: React.PropTypes.object.isRequired,
};
const SouthPane = function (props) {
let results = <div />;
const latestQuery = props.latestQuery;
let latestQuery;
if (props.queries.length > 0) {
latestQuery = props.queries[props.queries.length - 1];
}
let results;
if (latestQuery) {
results = <ResultSet showControls search query={latestQuery} />;
results = <ResultSet showControls search query={latestQuery} actions={props.actions} />;
} else {
results = <Alert bsStyle="info">Run a query to display results here</Alert>;
}
@@ -25,24 +30,12 @@ const SouthPane = function (props) {
</div>
</Tab>
<Tab title="Query History" eventKey={2}>
<QueryHistory />
<QueryHistory queries={props.queries} actions={props.actions} />
</Tab>
</Tabs>
</div>
);
};
SouthPane.propTypes = propTypes;
SouthPane.propTypes = {
latestQuery: React.PropTypes.object,
actions: React.PropTypes.object,
};
SouthPane.defaultProps = {
};
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch),
};
}
export default connect(null, mapDispatchToProps)(SouthPane);
export default SouthPane;

View File

@@ -18,16 +18,29 @@ import 'brace/mode/sql';
import 'brace/theme/github';
import 'brace/ext/language_tools';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as Actions from '../actions';
import shortid from 'shortid';
import SouthPane from './SouthPane';
import Timer from './Timer';
import SqlEditorLeftBar from './SqlEditorLeftBar';
const propTypes = {
actions: React.PropTypes.object.isRequired,
database: React.PropTypes.object,
latestQuery: React.PropTypes.object,
networkOn: React.PropTypes.bool,
tables: React.PropTypes.array.isRequired,
queries: React.PropTypes.array.isRequired,
queryEditor: React.PropTypes.object.isRequired,
};
const defaultProps = {
networkOn: true,
database: null,
latestQuery: null,
};
class SqlEditor extends React.Component {
constructor(props) {
super(props);
@@ -72,15 +85,6 @@ class SqlEditor extends React.Component {
this.setState({ sql: text });
this.props.actions.queryEditorSetSql(this.props.queryEditor, text);
}
addWorkspaceQuery() {
this.props.actions.addWorkspaceQuery({
id: shortid.generate(),
sql: this.state.sql,
dbId: this.props.queryEditor.dbId,
schema: this.props.queryEditor.schema,
title: this.props.queryEditor.title,
});
}
ctasChange() {}
visualize() {}
ctasChanged(event) {
@@ -130,7 +134,9 @@ class SqlEditor extends React.Component {
{runButtons}
</ButtonGroup>
);
if (this.props.latestQuery && ['running', 'pending'].includes(this.props.latestQuery.state)) {
if (
this.props.latestQuery &&
['running', 'pending'].indexOf(this.props.latestQuery.state) > -1) {
runButtons = (
<ButtonGroup bsSize="small" className="inline m-r-5 pull-left">
<Button
@@ -202,7 +208,12 @@ class SqlEditor extends React.Component {
<div className="SqlEditor" style={{ minHeight: this.sqlEditorHeight() }}>
<Row>
<Col md={3}>
<SqlEditorLeftBar queryEditor={this.props.queryEditor} />
<SqlEditorLeftBar
queryEditor={this.props.queryEditor}
tables={this.props.tables}
networkOn={this.props.networkOn}
actions={this.props.actions}
/>
</Col>
<Col md={9}>
<AceEditor
@@ -220,32 +231,17 @@ class SqlEditor extends React.Component {
/>
{editorBottomBar}
<br />
<SouthPane latestQuery={this.props.latestQuery} sqlEditor={this} />
<SouthPane
queries={this.props.queries}
actions={this.props.actions}
/>
</Col>
</Row>
</div>
);
}
}
SqlEditor.defaultProps = defaultProps;
SqlEditor.propTypes = propTypes;
SqlEditor.propTypes = {
actions: React.PropTypes.object,
database: React.PropTypes.object,
latestQuery: React.PropTypes.object,
queryEditor: React.PropTypes.object,
};
SqlEditor.defaultProps = {
};
function mapStateToProps() {
return {};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(SqlEditor);
export default SqlEditor;

View File

@@ -1,14 +1,23 @@
const $ = window.$ = require('jquery');
import React from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as Actions from '../actions';
import Select from 'react-select';
import { Label, Button } from 'react-bootstrap';
import TableElement from './TableElement';
import DatabaseSelect from './DatabaseSelect';
const propTypes = {
queryEditor: React.PropTypes.object.isRequired,
tables: React.PropTypes.array,
actions: React.PropTypes.object,
networkOn: React.PropTypes.bool,
};
const defaultProps = {
tables: [],
networkOn: true,
actions: {},
};
class SqlEditorLeftBar extends React.Component {
constructor(props) {
@@ -115,7 +124,6 @@ class SqlEditorLeftBar extends React.Component {
if (!this.props.networkOn) {
networkAlert = <p><Label bsStyle="danger">OFFLINE</Label></p>;
}
const tables = this.props.tables.filter((t) => (t.queryEditorId === this.props.queryEditor.id));
const shouldShowReset = window.location.search === '?reset=1';
return (
<div className="clearfix sql-toolbar">
@@ -124,6 +132,7 @@ class SqlEditorLeftBar extends React.Component {
<DatabaseSelect
onChange={this.onChange.bind(this)}
databaseId={this.props.queryEditor.dbId}
actions={this.props.actions}
valueRenderer={(o) => (
<div>
<span className="text-muted">Database:</span> {o.label}
@@ -161,8 +170,13 @@ class SqlEditorLeftBar extends React.Component {
</div>
<hr />
<div className="m-t-5">
{tables.map((table) => (
<TableElement table={table} queryEditor={this.props.queryEditor} key={table.id} />
{this.props.tables.map((table) => (
<TableElement
table={table}
queryEditor={this.props.queryEditor}
key={table.id}
actions={this.props.actions}
/>
))}
</div>
{shouldShowReset &&
@@ -174,29 +188,7 @@ class SqlEditorLeftBar extends React.Component {
);
}
}
SqlEditorLeftBar.propTypes = propTypes;
SqlEditorLeftBar.defaultProps = defaultProps;
SqlEditorLeftBar.propTypes = {
queryEditor: React.PropTypes.object,
tables: React.PropTypes.array,
actions: React.PropTypes.object,
networkOn: React.PropTypes.bool,
};
SqlEditorLeftBar.defaultProps = {
tables: [],
};
function mapStateToProps(state) {
return {
tables: state.tables,
networkOn: state.networkOn,
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(SqlEditorLeftBar);
export default SqlEditorLeftBar;

View File

@@ -4,20 +4,21 @@ import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as Actions from '../actions';
import SqlEditor from './SqlEditor';
import shortid from 'shortid';
import { getParamFromQuery } from '../../../utils/common';
import CopyQueryTabUrl from './CopyQueryTabUrl';
const propTypes = {
actions: React.PropTypes.object,
databases: React.PropTypes.object,
queries: React.PropTypes.object,
actions: React.PropTypes.object.isRequired,
databases: React.PropTypes.object.isRequired,
queries: React.PropTypes.object.isRequired,
queryEditors: React.PropTypes.array,
tabHistory: React.PropTypes.array,
tabHistory: React.PropTypes.array.isRequired,
tables: React.PropTypes.array.isRequired,
networkOn: React.PropTypes.bool,
};
const defaultProps = {
tabHistory: [],
queryEditors: [],
networkOn: true,
};
let queryCount = 1;
@@ -39,7 +40,6 @@ class TabbedSqlEditors extends React.Component {
if (this.state.query) {
queryCount++;
const queryEditorProps = {
id: shortid.generate(),
title: getParamFromQuery(this.state.query, 'title'),
dbId: parseInt(getParamFromQuery(this.state.query, 'dbid'), 10),
schema: getParamFromQuery(this.state.query, 'schema'),
@@ -72,7 +72,6 @@ class TabbedSqlEditors extends React.Component {
queryCount++;
const activeQueryEditor = this.activeQueryEditor();
const qe = {
id: shortid.generate(),
title: `Untitled Query ${queryCount}`,
dbId: (activeQueryEditor) ? activeQueryEditor.dbId : null,
schema: (activeQueryEditor) ? activeQueryEditor.schema : null,
@@ -88,10 +87,26 @@ class TabbedSqlEditors extends React.Component {
this.props.actions.setActiveQueryEditor({ id: key });
}
}
removeQueryEditor(qe) {
this.props.actions.removeQueryEditor(qe);
}
render() {
const editors = this.props.queryEditors.map((qe, i) => {
let latestQuery = this.props.queries[qe.latestQueryId];
const database = this.props.databases[qe.dbId];
const isSelected = (qe.id === this.activeQueryEditor().id);
const queriesArray = [];
for (const id in this.props.queries) {
if (this.props.queries[id].sqlEditorId === qe.id) {
queriesArray.push(this.props.queries[id]);
}
}
let latestQuery;
if (qe.latestQueryId) {
latestQuery = this.props.queries[qe.latestQueryId];
}
let database;
if (qe.dbId) {
database = this.props.databases[qe.dbId];
}
const state = (latestQuery) ? latestQuery.state : '';
const tabTitle = (
<div>
@@ -101,14 +116,14 @@ class TabbedSqlEditors extends React.Component {
id={'ddbtn-tab-' + i}
title=""
>
<MenuItem eventKey="1" onClick={this.props.actions.removeQueryEditor.bind(this, qe)}>
<MenuItem eventKey="1" onClick={this.removeQueryEditor.bind(this, qe)}>
<i className="fa fa-close" /> close tab
</MenuItem>
<MenuItem eventKey="2" onClick={this.renameTab.bind(this, qe)}>
<i className="fa fa-i-cursor" /> rename tab
</MenuItem>
<MenuItem eventKey="3">
<i className="fa fa-clipboard" /> <CopyQueryTabUrl qe={qe} />
<i className="fa fa-clipboard" /> <CopyQueryTabUrl queryEditor={qe} />
</MenuItem>
</DropdownButton>
</div>
@@ -121,11 +136,17 @@ class TabbedSqlEditors extends React.Component {
>
<div className="panel panel-default">
<div className="panel-body">
<SqlEditor
queryEditor={qe}
latestQuery={latestQuery}
database={database}
/>
{isSelected &&
<SqlEditor
tables={this.props.tables.filter((t) => (t.queryEditorId === qe.id))}
queryEditor={qe}
queries={queriesArray}
latestQuery={latestQuery}
database={database}
actions={this.props.actions}
networkOn={this.props.networkOn}
/>
}
</div>
</div>
</Tab>);
@@ -138,7 +159,13 @@ class TabbedSqlEditors extends React.Component {
id="a11y-query-editor-tabs"
>
{editors}
<Tab title={<div><i className="fa fa-plus-circle" />&nbsp;</div>} eventKey="add_tab" />
<Tab
title={
<div>
<i className="fa fa-plus-circle" />&nbsp;
</div>}
eventKey="add_tab"
/>
</Tabs>
);
}
@@ -152,6 +179,8 @@ function mapStateToProps(state) {
queryEditors: state.queryEditors,
queries: state.queries,
tabHistory: state.tabHistory,
networkOn: state.networkOn,
tables: state.tables,
};
}
function mapDispatchToProps(dispatch) {
@@ -160,4 +189,5 @@ function mapDispatchToProps(dispatch) {
};
}
export { TabbedSqlEditors };
export default connect(mapStateToProps, mapDispatchToProps)(TabbedSqlEditors);

View File

@@ -1,7 +1,4 @@
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as Actions from '../actions';
import { ButtonGroup, Well } from 'react-bootstrap';
import shortid from 'shortid';
@@ -187,7 +184,7 @@ class TableElement extends React.Component {
<Link
className="fa fa-trash pull-left m-l-2"
onClick={this.removeTable.bind(this)}
tooltip="Remove from workspace"
tooltip="Remove from panel"
href="#"
/>
</ButtonGroup>
@@ -203,10 +200,4 @@ class TableElement extends React.Component {
TableElement.propTypes = propTypes;
TableElement.defaultProps = defaultProps;
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch),
};
}
export default connect(null, mapDispatchToProps)(TableElement);
export { TableElement };
export default TableElement;

View File

@@ -1,23 +0,0 @@
import React from 'react';
import { BootstrapTable, TableHeaderColumn } from 'react-bootstrap-table';
const TableMetadata = function (props) {
return (
<BootstrapTable
condensed
data={props.table.columns}
>
<TableHeaderColumn dataField="id" isKey hidden>
id
</TableHeaderColumn>
<TableHeaderColumn dataField="name">Name</TableHeaderColumn>
<TableHeaderColumn dataField="type">Type</TableHeaderColumn>
</BootstrapTable>
);
};
TableMetadata.propTypes = {
table: React.PropTypes.object,
};
export default TableMetadata;

View File

@@ -1,10 +1,6 @@
import React from 'react';
import { Alert, Button, Col, 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';
import shortid from 'shortid';
@@ -16,6 +12,17 @@ const CHART_TYPES = [
{ value: 'bar', label: 'Time Series - Bar Chart', requiresTime: true },
];
const propTypes = {
onHide: React.PropTypes.func,
query: React.PropTypes.object,
show: React.PropTypes.bool,
};
const defaultProps = {
show: false,
query: {},
onHide: () => {},
};
class VisualizeModal extends React.Component {
constructor(props) {
super(props);
@@ -34,7 +41,10 @@ class VisualizeModal extends React.Component {
this.validate();
}
setStateFromProps() {
if (!this.props.query || !this.props.query.results.columns) {
if (
!this.props.query ||
!this.props.query.results ||
!this.props.query.results.columns) {
return;
}
const columns = {};
@@ -204,22 +214,7 @@ class VisualizeModal extends React.Component {
return modal;
}
}
VisualizeModal.propTypes = {
query: React.PropTypes.object,
show: React.PropTypes.bool,
onHide: React.PropTypes.func,
};
VisualizeModal.defaultProps = {
show: false,
onHide: () => {},
};
VisualizeModal.propTypes = propTypes;
VisualizeModal.defaultProps = defaultProps;
function mapStateToProps() {
return {};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(Actions, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(VisualizeModal);
export default VisualizeModal;