-
Notifications
You must be signed in to change notification settings - Fork 13.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Explore control panel - Chart control, TimeFilter, GroupBy, Filters #1205
Changes from all commits
cf084e6
2353549
b75ee66
ee61d17
6ad3bf7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,192 @@ | ||
const $ = window.$ = require('jquery'); | ||
export const SET_DATASOURCE = 'SET_DATASOURCE'; | ||
export const SET_VIZTYPE = 'SET_VIZTYPE'; | ||
export const SET_TIME_COLUMN_OPTS = 'SET_TIME_COLUMN_OPTS'; | ||
export const SET_TIME_GRAIN_OPTS = 'SET_TIME_GRAIN_OPTS'; | ||
export const SET_TIME_COLUMN = 'SET_TIME_COLUMN'; | ||
export const SET_TIME_GRAIN = 'SET_TIME_GRAIN'; | ||
export const SET_SINCE = 'SET_SINCE'; | ||
export const SET_UNTIL = 'SET_UNTIL'; | ||
export const SET_GROUPBY_COLUMNS = 'SET_GROUPBY_COLUMNS'; | ||
export const SET_GROUPBY_COLUMN_OPTS = 'SET_GROUPBY_COLUMN_OPTS'; | ||
export const SET_METRICS = 'SET_METRICS'; | ||
export const SET_METRICS_OPTS = 'SET_METRICS_OPTS'; | ||
export const ADD_COLUMN = 'ADD_COLUMN'; | ||
export const REMOVE_COLUMN = 'REMOVE_COLUMN'; | ||
export const ADD_ORDERING = 'ADD_ORDERING'; | ||
export const REMOVE_ORDERING = 'REMOVE_ORDERING'; | ||
export const SET_TIME_STAMP = 'SET_TIME_STAMP'; | ||
export const SET_ROW_LIMIT = 'SET_ROW_LIMIT'; | ||
export const TOGGLE_SEARCHBOX = 'TOGGLE_SEARCHBOX'; | ||
export const SET_FILTER_COLUMN_OPTS = 'SET_FILTER_COLUMN_OPTS'; | ||
export const SET_WHERE_CLAUSE = 'SET_WHERE_CLAUSE'; | ||
export const SET_HAVING_CLAUSE = 'SET_HAVING_CLAUSE'; | ||
export const ADD_FILTER = 'ADD_FILTER'; | ||
export const SET_FILTER = 'SET_FILTER'; | ||
export const REMOVE_FILTER = 'REMOVE_FILTER'; | ||
export const CHANGE_FILTER_FIELD = 'CHANGE_FILTER_FIELD'; | ||
export const CHANGE_FILTER_OP = 'CHANGE_FILTER_OP'; | ||
export const CHANGE_FILTER_VALUE = 'CHANGE_FILTER_VALUE'; | ||
export const RESET_FORM_DATA = 'RESET_FORM_DATA'; | ||
export const CLEAR_ALL_OPTS = 'CLEAR_ALL_OPTS'; | ||
|
||
export function setTimeColumnOpts(timeColumnOpts) { | ||
return { type: SET_TIME_COLUMN_OPTS, timeColumnOpts }; | ||
} | ||
|
||
export function setTimeGrainOpts(timeGrainOpts) { | ||
return { type: SET_TIME_GRAIN_OPTS, timeGrainOpts }; | ||
} | ||
|
||
export function setGroupByColumnOpts(groupByColumnOpts) { | ||
return { type: SET_GROUPBY_COLUMN_OPTS, groupByColumnOpts }; | ||
} | ||
|
||
export function setMetricsOpts(metricsOpts) { | ||
return { type: SET_METRICS_OPTS, metricsOpts }; | ||
} | ||
|
||
export function setFilterColumnOpts(filterColumnOpts) { | ||
return { type: SET_FILTER_COLUMN_OPTS, filterColumnOpts }; | ||
} | ||
|
||
export function resetFormData() { | ||
// Clear all form data when switching datasource | ||
return { type: RESET_FORM_DATA }; | ||
} | ||
|
||
export function clearAllOpts() { | ||
return { type: CLEAR_ALL_OPTS }; | ||
} | ||
|
||
export function setFormOpts(datasourceId, datasourceType) { | ||
return function (dispatch) { | ||
const timeColumnOpts = []; | ||
const groupByColumnOpts = []; | ||
const metricsOpts = []; | ||
const filterColumnOpts = []; | ||
const timeGrainOpts = []; | ||
|
||
if (datasourceId) { | ||
const params = [`datasource_id=${datasourceId}`, `datasource_type=${datasourceType}`]; | ||
const url = '/caravel/fetch_datasource_metadata?' + params.join('&'); | ||
|
||
$.get(url, (data, status) => { | ||
if (status === 'success') { | ||
data.dttm_cols.forEach((d) => { | ||
if (d) timeColumnOpts.push({ value: d, label: d }); | ||
}); | ||
data.groupby_cols.forEach((d) => { | ||
if (d) groupByColumnOpts.push({ value: d, label: d }); | ||
}); | ||
data.metrics.forEach((d) => { | ||
if (d) metricsOpts.push({ value: d[1], label: d[0] }); | ||
}); | ||
data.filter_cols.forEach((d) => { | ||
if (d) filterColumnOpts.push({ value: d, label: d }); | ||
}); | ||
data.time_grains.forEach((d) => { | ||
if (d) timeGrainOpts.push({ value: d, label: d }); | ||
}); | ||
// Repopulate options for controls | ||
dispatch(setTimeColumnOpts(timeColumnOpts)); | ||
dispatch(setTimeGrainOpts(timeGrainOpts)); | ||
dispatch(setGroupByColumnOpts(groupByColumnOpts)); | ||
dispatch(setMetricsOpts(metricsOpts)); | ||
dispatch(setFilterColumnOpts(filterColumnOpts)); | ||
} | ||
}); | ||
} else { | ||
// Clear all Select options | ||
dispatch(clearAllOpts()); | ||
} | ||
}; | ||
} | ||
|
||
export function setDatasource(datasourceId) { | ||
return { type: SET_DATASOURCE, datasourceId }; | ||
} | ||
|
||
export function setVizType(vizType) { | ||
return { type: SET_VIZTYPE, vizType }; | ||
} | ||
|
||
export function setTimeColumn(timeColumn) { | ||
return { type: SET_TIME_COLUMN, timeColumn }; | ||
} | ||
|
||
export function setTimeGrain(timeGrain) { | ||
return { type: SET_TIME_GRAIN, timeGrain }; | ||
} | ||
|
||
export function setSince(since) { | ||
return { type: SET_SINCE, since }; | ||
} | ||
|
||
export function setUntil(until) { | ||
return { type: SET_UNTIL, until }; | ||
} | ||
|
||
export function setGroupByColumns(groupByColumns) { | ||
return { type: SET_GROUPBY_COLUMNS, groupByColumns }; | ||
} | ||
|
||
export function setMetrics(metrics) { | ||
return { type: SET_METRICS, metrics }; | ||
} | ||
|
||
export function addColumn(column) { | ||
return { type: ADD_COLUMN, column }; | ||
} | ||
|
||
export function removeColumn(column) { | ||
return { type: REMOVE_COLUMN, column }; | ||
} | ||
|
||
export function addOrdering(ordering) { | ||
return { type: ADD_ORDERING, ordering }; | ||
} | ||
|
||
export function removeOrdering(ordering) { | ||
return { type: REMOVE_ORDERING, ordering }; | ||
} | ||
|
||
export function setTimeStamp(timeStampFormat) { | ||
return { type: SET_TIME_STAMP, timeStampFormat }; | ||
} | ||
|
||
export function setRowLimit(rowLimit) { | ||
return { type: SET_ROW_LIMIT, rowLimit }; | ||
} | ||
|
||
export function toggleSearchBox(searchBox) { | ||
return { type: TOGGLE_SEARCHBOX, searchBox }; | ||
} | ||
|
||
export function setWhereClause(whereClause) { | ||
return { type: SET_WHERE_CLAUSE, whereClause }; | ||
} | ||
|
||
export function setHavingClause(havingClause) { | ||
return { type: SET_HAVING_CLAUSE, havingClause }; | ||
} | ||
|
||
export function addFilter(filter) { | ||
return { type: ADD_FILTER, filter }; | ||
} | ||
|
||
export function removeFilter(filter) { | ||
return { type: REMOVE_FILTER, filter }; | ||
} | ||
|
||
export function changeFilterField(filter, field) { | ||
return { type: CHANGE_FILTER_FIELD, filter, field }; | ||
} | ||
|
||
export function changeFilterOp(filter, op) { | ||
return { type: CHANGE_FILTER_OP, filter, op }; | ||
} | ||
|
||
export function changeFilterValue(filter, value) { | ||
return { type: CHANGE_FILTER_VALUE, filter, value }; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
import React from 'react'; | ||
import Select from 'react-select'; | ||
import { bindActionCreators } from 'redux'; | ||
import * as actions from '../actions/exploreActions'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. rather than importing all actions here i would suggest just importing the ones needed for this component... import { setFormOpts, setDatasource, resetFormData, setVizType }
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. once we import actions above we can use them like this: makes more sense to import the actions in this component rather than passing them in as props. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This didn't work.. Suggested by the redux doc here: http://redux.js.org/docs/basics/Actions.html#action-creators and then do this in mapDispatchToProps:
Is there a better way to do this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah ok, maybe i am carrying over some Alt patterns that don't work in redux. so when you call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The way I understand it is that bindActionCreators bind all action creators with dispatch function, so that we don't have to write redundant onClick() => dispatch(actionFunc()) statements. mapDispatchToProps() allows us to call dispatch function from the component's props, instead of accessing global store everytime we dispatch. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
} | ||
} | ||
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); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i think this function belongs in an
actionCreators
file but it we can change that in a subsequent pr