mirror of
https://github.com/apache/superset.git
synced 2026-04-21 17:14:57 +00:00
[explore-v2] setup, basic layout, control panels, v2 url (#1233)
* Explore control panel - Chart control, TimeFilter, GroupBy, Filters (#1205) * create structure for new forked explore view (#1099) * create structure for new forked explore view * update component name * add bootstrap data pattern * remove console.log * Associate version to entry files (#1060) * Associate version to entry files * Modified path joins for configs * Made changes based on comments * Created store and reducers (#1108) * Created store and reducers * Added spec * Modifications based on comments * Explore control panel components: Chart control, Time filter, SQL, GroupBy and Filters * Modifications based on comments * accommodate old and new explore urls * move bootstrap data up in scope * fix code climate issues * fix long lines * fix syntax error
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
import React from 'react';
|
||||
import { Panel } from 'react-bootstrap';
|
||||
|
||||
const ChartContainer = function () {
|
||||
return (
|
||||
<Panel header="Chart title">
|
||||
chart goes here
|
||||
</Panel>
|
||||
);
|
||||
};
|
||||
export default ChartContainer;
|
||||
@@ -0,0 +1,89 @@
|
||||
import React from 'react';
|
||||
import Select from 'react-select';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import * as actions from '../actions/exploreActions';
|
||||
import { connect } from 'react-redux';
|
||||
import { VIZ_TYPES } from '../constants';
|
||||
|
||||
const propTypes = {
|
||||
actions: React.PropTypes.object,
|
||||
datasources: React.PropTypes.array,
|
||||
datasourceId: React.PropTypes.number,
|
||||
datasourceType: React.PropTypes.string,
|
||||
vizType: React.PropTypes.string,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
datasources: [],
|
||||
datasourceId: null,
|
||||
datasourceType: null,
|
||||
vizType: null,
|
||||
};
|
||||
|
||||
class ChartControl extends React.Component {
|
||||
componentWillMount() {
|
||||
if (this.props.datasourceId) {
|
||||
this.props.actions.setFormOpts(this.props.datasourceId, this.props.datasourceType);
|
||||
}
|
||||
}
|
||||
changeDatasource(datasourceOpt) {
|
||||
const val = (datasourceOpt) ? datasourceOpt.value : null;
|
||||
this.props.actions.setDatasource(val);
|
||||
this.props.actions.resetFormData();
|
||||
this.props.actions.setFormOpts(val, this.props.datasourceType);
|
||||
}
|
||||
changeViz(vizOpt) {
|
||||
const val = (vizOpt) ? vizOpt.value : null;
|
||||
this.props.actions.setVizType(val);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div className="panel space-1">
|
||||
<div className="panel-header">Chart Options</div>
|
||||
<div className="panel-body">
|
||||
<h5 className="section-heading">Datasource</h5>
|
||||
<div className="row">
|
||||
<Select
|
||||
name="select-datasource"
|
||||
placeholder="Select a datasource"
|
||||
options={this.props.datasources.map((d) => ({ value: d[0], label: d[1] }))}
|
||||
value={this.props.datasourceId}
|
||||
autosize={false}
|
||||
onChange={this.changeDatasource.bind(this)}
|
||||
/>
|
||||
</div>
|
||||
<h5 className="section-heading">Viz Type</h5>
|
||||
<div className="row">
|
||||
<Select
|
||||
name="select-viztype"
|
||||
placeholder="Select a viz type"
|
||||
options={VIZ_TYPES}
|
||||
value={this.props.vizType}
|
||||
autosize={false}
|
||||
onChange={this.changeViz.bind(this)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ChartControl.propTypes = propTypes;
|
||||
ChartControl.defaultProps = defaultProps;
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
datasources: state.datasources,
|
||||
datasourceId: state.datasourceId,
|
||||
datasourceType: state.datasourceType,
|
||||
vizType: state.vizType,
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators(actions, dispatch),
|
||||
};
|
||||
}
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ChartControl);
|
||||
@@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
import { Panel } from 'react-bootstrap';
|
||||
import TimeFilter from './TimeFilter';
|
||||
import ChartControl from './ChartControl';
|
||||
import GroupBy from './GroupBy';
|
||||
import SqlClause from './SqlClause';
|
||||
import Filters from './Filters';
|
||||
|
||||
const ControlPanelsContainer = function () {
|
||||
return (
|
||||
<Panel>
|
||||
<ChartControl />
|
||||
<TimeFilter />
|
||||
<GroupBy />
|
||||
<SqlClause />
|
||||
<Filters />
|
||||
</Panel>
|
||||
);
|
||||
};
|
||||
export default ControlPanelsContainer;
|
||||
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import ChartContainer from './ChartContainer';
|
||||
import ControlPanelsContainer from './ControlPanelsContainer';
|
||||
import QueryAndSaveButtons from './QueryAndSaveButtons';
|
||||
|
||||
const ExploreViewContainer = function () {
|
||||
return (
|
||||
<div className="container-fluid">
|
||||
<div className="row">
|
||||
<div className="col-sm-3">
|
||||
<QueryAndSaveButtons
|
||||
canAdd="True"
|
||||
onQuery={() => { console.log('clicked query'); }}
|
||||
/>
|
||||
<br /><br />
|
||||
<ControlPanelsContainer />
|
||||
</div>
|
||||
<div className="col-sm-9">
|
||||
<ChartContainer />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ExploreViewContainer;
|
||||
128
caravel/assets/javascripts/explorev2/components/Filters.jsx
Normal file
128
caravel/assets/javascripts/explorev2/components/Filters.jsx
Normal file
@@ -0,0 +1,128 @@
|
||||
import React from 'react';
|
||||
// import { Tab, Row, Col, Nav, NavItem } from 'react-bootstrap';
|
||||
import Select from 'react-select';
|
||||
import { Button } from 'react-bootstrap';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import * as actions from '../actions/exploreActions';
|
||||
import shortid from 'shortid';
|
||||
|
||||
const propTypes = {
|
||||
actions: React.PropTypes.object,
|
||||
filterColumnOpts: React.PropTypes.array,
|
||||
filters: React.PropTypes.array,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
filterColumnOpts: [],
|
||||
filters: [],
|
||||
};
|
||||
|
||||
class Filters extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
opOpts: ['in', 'not in'],
|
||||
};
|
||||
}
|
||||
changeField(filter, fieldOpt) {
|
||||
const val = (fieldOpt) ? fieldOpt.value : null;
|
||||
this.props.actions.changeFilterField(filter, val);
|
||||
}
|
||||
changeOp(filter, opOpt) {
|
||||
const val = (opOpt) ? opOpt.value : null;
|
||||
this.props.actions.changeFilterOp(filter, val);
|
||||
}
|
||||
changeValue(filter, value) {
|
||||
this.props.actions.changeFilterValue(filter, value);
|
||||
}
|
||||
removeFilter(filter) {
|
||||
this.props.actions.removeFilter(filter);
|
||||
}
|
||||
addFilter() {
|
||||
this.props.actions.addFilter({
|
||||
id: shortid.generate(),
|
||||
field: null,
|
||||
op: null,
|
||||
value: null,
|
||||
});
|
||||
}
|
||||
render() {
|
||||
const filters = this.props.filters.map((filter) => (
|
||||
<div>
|
||||
<Select
|
||||
className="row"
|
||||
multi={false}
|
||||
name="select-column"
|
||||
placeholder="Select column"
|
||||
options={this.props.filterColumnOpts}
|
||||
value={filter.field}
|
||||
autosize={false}
|
||||
onChange={this.changeField.bind(this, filter)}
|
||||
/>
|
||||
<div className="row">
|
||||
<Select
|
||||
className="col-sm-3"
|
||||
multi={false}
|
||||
name="select-op"
|
||||
placeholder="Select operator"
|
||||
options={this.state.opOpts.map((o) => ({ value: o, label: o }))}
|
||||
value={filter.op}
|
||||
autosize={false}
|
||||
onChange={this.changeOp.bind(this, filter)}
|
||||
/>
|
||||
<div className="col-sm-6">
|
||||
<input
|
||||
type="text"
|
||||
onChange={this.changeValue.bind(this, filter)}
|
||||
className="form-control input-sm"
|
||||
placeholder="Filter value"
|
||||
/>
|
||||
</div>
|
||||
<div className="col-sm-3">
|
||||
<Button
|
||||
bsStyle="primary"
|
||||
onClick={this.removeFilter.bind(this, filter)}
|
||||
>
|
||||
<i className="fa fa-minus" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
return (
|
||||
<div className="panel space-1">
|
||||
<div className="panel-header">Filters</div>
|
||||
<div className="panel-body">
|
||||
{filters}
|
||||
<Button
|
||||
bsStyle="primary"
|
||||
onClick={this.addFilter.bind(this)}
|
||||
>
|
||||
<i className="fa fa-plus" />Add Filter
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Filters.propTypes = propTypes;
|
||||
|
||||
Filters.defaultProps = defaultProps;
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
filterColumnOpts: state.filterColumnOpts,
|
||||
filters: state.filters,
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators(actions, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Filters);
|
||||
82
caravel/assets/javascripts/explorev2/components/GroupBy.jsx
Normal file
82
caravel/assets/javascripts/explorev2/components/GroupBy.jsx
Normal file
@@ -0,0 +1,82 @@
|
||||
import React from 'react';
|
||||
import Select from 'react-select';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import * as actions from '../actions/exploreActions';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
const propTypes = {
|
||||
actions: React.PropTypes.object,
|
||||
metricsOpts: React.PropTypes.array,
|
||||
metrics: React.PropTypes.array,
|
||||
groupByColumnOpts: React.PropTypes.array,
|
||||
groupByColumns: React.PropTypes.array,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
metricsOpts: [],
|
||||
metrics: [],
|
||||
groupByColumnOpts: [],
|
||||
groupByColumns: [],
|
||||
};
|
||||
|
||||
class GroupBy extends React.Component {
|
||||
changeColumns(groupByColumnOpts) {
|
||||
this.props.actions.setGroupByColumns(groupByColumnOpts);
|
||||
}
|
||||
changeMetrics(metricsOpts) {
|
||||
this.props.actions.setMetrics(metricsOpts);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div className="panel space-1">
|
||||
<div className="panel-header">GroupBy</div>
|
||||
<div className="panel-body">
|
||||
<div className="row">
|
||||
<h5 className="section-heading">GroupBy Column</h5>
|
||||
<Select
|
||||
multi
|
||||
name="select-time-column"
|
||||
placeholder="Select groupby columns"
|
||||
options={this.props.groupByColumnOpts}
|
||||
value={this.props.groupByColumns}
|
||||
autosize={false}
|
||||
onChange={this.changeColumns.bind(this)}
|
||||
/>
|
||||
</div>
|
||||
<div className="row">
|
||||
<h5 className="section-heading">Metrics</h5>
|
||||
<Select
|
||||
multi
|
||||
name="select-since"
|
||||
placeholder="Select metrics"
|
||||
options={this.props.metricsOpts}
|
||||
value={this.props.metrics}
|
||||
autosize={false}
|
||||
onChange={this.changeMetrics.bind(this)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
GroupBy.propTypes = propTypes;
|
||||
GroupBy.defaultProps = defaultProps;
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
metricsOpts: state.metricsOpts,
|
||||
metrics: state.metrics,
|
||||
groupByColumnOpts: state.groupByColumnOpts,
|
||||
groupByColumns: state.groupByColumns,
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators(actions, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(GroupBy);
|
||||
@@ -0,0 +1,31 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
const propTypes = {
|
||||
canAdd: PropTypes.string.isRequired,
|
||||
onQuery: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default function QueryAndSaveBtns({ canAdd, onQuery }) {
|
||||
const saveClasses = classnames('btn btn-default btn-sm', {
|
||||
'disabled disabledButton': canAdd !== 'True',
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="btn-group query-and-save">
|
||||
<button type="button" className="btn btn-primary btn-sm" onClick={onQuery}>
|
||||
<i className="fa fa-bolt"></i> Query
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={saveClasses}
|
||||
data-target="#save_modal"
|
||||
data-toggle="modal"
|
||||
>
|
||||
<i className="fa fa-plus-circle"></i> Save as
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
QueryAndSaveBtns.propTypes = propTypes;
|
||||
@@ -0,0 +1,58 @@
|
||||
import React from 'react';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import * as actions from '../actions/exploreActions';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
const propTypes = {
|
||||
actions: React.PropTypes.object,
|
||||
};
|
||||
|
||||
class SqlClause extends React.Component {
|
||||
changeWhere(whereClause) {
|
||||
this.props.actions.setWhereClause(whereClause);
|
||||
}
|
||||
changeHaving(havingClause) {
|
||||
this.props.actions.setHavingClause(havingClause);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div className="panel space-1">
|
||||
<div className="panel-header">SQL</div>
|
||||
<div className="panel-body">
|
||||
<div className="row">
|
||||
<h5 className="section-heading">Where</h5>
|
||||
<input
|
||||
type="text"
|
||||
onChange={this.changeWhere.bind(this)}
|
||||
className="form-control input-sm"
|
||||
placeholder="Where Clause"
|
||||
/>
|
||||
</div>
|
||||
<div className="row">
|
||||
<h5 className="section-heading">Having</h5>
|
||||
<input
|
||||
type="text"
|
||||
onChange={this.changeHaving.bind(this)}
|
||||
className="form-control input-sm"
|
||||
placeholder="Having Clause"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SqlClause.propTypes = propTypes;
|
||||
|
||||
function mapStateToProps() {
|
||||
return {};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators(actions, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(SqlClause);
|
||||
117
caravel/assets/javascripts/explorev2/components/TimeFilter.jsx
Normal file
117
caravel/assets/javascripts/explorev2/components/TimeFilter.jsx
Normal file
@@ -0,0 +1,117 @@
|
||||
import React from 'react';
|
||||
import Select from 'react-select';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import * as actions from '../actions/exploreActions';
|
||||
import { connect } from 'react-redux';
|
||||
import { sinceOptions, untilOptions } from '../constants';
|
||||
|
||||
const propTypes = {
|
||||
actions: React.PropTypes.object,
|
||||
timeColumnOpts: React.PropTypes.array,
|
||||
timeColumn: React.PropTypes.string,
|
||||
timeGrainOpts: React.PropTypes.array,
|
||||
timeGrain: React.PropTypes.string,
|
||||
since: React.PropTypes.string,
|
||||
until: React.PropTypes.string,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
timeColumnOpts: [],
|
||||
timeColumn: null,
|
||||
timeGrainOpts: [],
|
||||
timeGrain: null,
|
||||
since: null,
|
||||
until: null,
|
||||
};
|
||||
|
||||
class TimeFilter extends React.Component {
|
||||
changeTimeColumn(timeColumnOpt) {
|
||||
const val = (timeColumnOpt) ? timeColumnOpt.value : null;
|
||||
this.props.actions.setTimeColumn(val);
|
||||
}
|
||||
changeTimeGrain(timeGrainOpt) {
|
||||
const val = (timeGrainOpt) ? timeGrainOpt.value : null;
|
||||
this.props.actions.setTimeGrain(val);
|
||||
}
|
||||
changeSince(sinceOpt) {
|
||||
const val = (sinceOpt) ? sinceOpt.value : null;
|
||||
this.props.actions.setSince(val);
|
||||
}
|
||||
changeUntil(untilOpt) {
|
||||
const val = (untilOpt) ? untilOpt.value : null;
|
||||
this.props.actions.setUntil(val);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div className="panel space-1">
|
||||
<div className="panel-header">Time Filter</div>
|
||||
<div className="panel-body">
|
||||
<div className="row">
|
||||
<h5 className="section-heading">Time Column & Grain</h5>
|
||||
<Select
|
||||
className="col-sm-6"
|
||||
name="select-time-column"
|
||||
placeholder="Select a time column"
|
||||
options={this.props.timeColumnOpts}
|
||||
value={this.props.timeColumn}
|
||||
autosize={false}
|
||||
onChange={this.changeTimeColumn.bind(this)}
|
||||
/>
|
||||
<Select
|
||||
className="col-sm-6"
|
||||
name="select-time-grain"
|
||||
placeholder="Select a time grain"
|
||||
options={this.props.timeGrainOpts}
|
||||
value={this.props.timeGrain}
|
||||
autosize={false}
|
||||
onChange={this.changeTimeGrain.bind(this)}
|
||||
/>
|
||||
</div>
|
||||
<div className="row">
|
||||
<h5 className="section-heading">Since - Until</h5>
|
||||
<Select
|
||||
className="col-sm-6"
|
||||
name="select-since"
|
||||
placeholder="Select Since Time"
|
||||
options={sinceOptions.map((s) => ({ value: s, label: s }))}
|
||||
value={this.props.since}
|
||||
autosize={false}
|
||||
onChange={this.changeSince.bind(this)}
|
||||
/>
|
||||
<Select
|
||||
className="col-sm-6"
|
||||
name="select-until"
|
||||
placeholder="Select Until Time"
|
||||
options={untilOptions.map((u) => ({ value: u, label: u }))}
|
||||
value={this.props.until}
|
||||
autosize={false}
|
||||
onChange={this.changeUntil.bind(this)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TimeFilter.propTypes = propTypes;
|
||||
TimeFilter.defaultProps = defaultProps;
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
timeColumnOpts: state.timeColumnOpts,
|
||||
timeColumn: state.timeColumn,
|
||||
timeGrainOpts: state.timeGrainOpts,
|
||||
timeGrain: state.timeGrain,
|
||||
since: state.since,
|
||||
until: state.until,
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators(actions, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(TimeFilter);
|
||||
Reference in New Issue
Block a user