From 66b498de25b6bb3145eb441f64ef7fdaffc5a747 Mon Sep 17 00:00:00 2001 From: vera-liu Date: Wed, 5 Oct 2016 14:53:51 -0700 Subject: [PATCH] Added controls for Table Viz (#1253) * Added controls for Table Viz * Change control panel container to stateless * Changed specs * Resolved conflicts --- .../explorev2/actions/exploreActions.js | 49 ++++++----- .../components/ControlPanelsContainer.jsx | 42 +++++++--- .../explorev2/components/Filters.jsx | 1 - .../explorev2/components/GroupBy.jsx | 8 +- .../explorev2/components/NotGroupBy.jsx | 82 +++++++++++++++++++ .../explorev2/components/Options.jsx | 76 +++++++++++++++++ .../assets/javascripts/explorev2/constants.js | 41 ++++++++++ .../explorev2/reducers/exploreReducer.js | 30 ++----- .../javascripts/explorev2/stores/store.js | 12 ++- .../explore/components/actions_spec.js | 30 ++----- caravel/views.py | 8 +- 11 files changed, 294 insertions(+), 85 deletions(-) create mode 100644 caravel/assets/javascripts/explorev2/components/NotGroupBy.jsx create mode 100644 caravel/assets/javascripts/explorev2/components/Options.jsx diff --git a/caravel/assets/javascripts/explorev2/actions/exploreActions.js b/caravel/assets/javascripts/explorev2/actions/exploreActions.js index 32268f7d03..6468c6ac39 100644 --- a/caravel/assets/javascripts/explorev2/actions/exploreActions.js +++ b/caravel/assets/javascripts/explorev2/actions/exploreActions.js @@ -11,11 +11,11 @@ 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_COLUMN_OPTS = 'SET_COLUMN_OPTS'; +export const SET_NOT_GROUPBY_COLUMNS = 'SET_NOT_GROUPBY_COLUMNS'; +export const SET_ORDERING_OPTS = 'SET_ORDERING_OPTS'; +export const SET_ORDERINGS = 'SET_ORDERINGS'; +export const SET_TIME_STAMP_FORMAT = 'SET_TIME_STAMP_FORMAT'; export const SET_ROW_LIMIT = 'SET_ROW_LIMIT'; export const TOGGLE_SEARCHBOX = 'TOGGLE_SEARCHBOX'; export const SET_FILTER_COLUMN_OPTS = 'SET_FILTER_COLUMN_OPTS'; @@ -47,6 +47,14 @@ export function setMetricsOpts(metricsOpts) { return { type: SET_METRICS_OPTS, metricsOpts }; } +export function setColumnOpts(columnOpts) { + return { type: SET_COLUMN_OPTS, columnOpts }; +} + +export function setOrderingOpts(orderingOpts) { + return { type: SET_ORDERING_OPTS, orderingOpts }; +} + export function setFilterColumnOpts(filterColumnOpts) { return { type: SET_FILTER_COLUMN_OPTS, filterColumnOpts }; } @@ -71,6 +79,8 @@ export function setFormOpts(datasourceId, datasourceType) { const metricsOpts = []; const filterColumnOpts = []; const timeGrainOpts = []; + const columnOpts = []; + const orderingOpts = []; if (datasourceId) { const params = [`datasource_id=${datasourceId}`, `datasource_type=${datasourceType}`]; @@ -93,12 +103,21 @@ export function setFormOpts(datasourceId, datasourceType) { data.time_grains.forEach((d) => { if (d) timeGrainOpts.push({ value: d, label: d }); }); + data.columns.forEach((d) => { + if (d) columnOpts.push({ value: d, label: d }); + }); + data.ordering_cols.forEach((d) => { + if (d) orderingOpts.push({ value: d, label: d }); + }); + // Repopulate options for controls dispatch(setTimeColumnOpts(timeColumnOpts)); dispatch(setTimeGrainOpts(timeGrainOpts)); dispatch(setGroupByColumnOpts(groupByColumnOpts)); dispatch(setMetricsOpts(metricsOpts)); dispatch(setFilterColumnOpts(filterColumnOpts)); + dispatch(setColumnOpts(columnOpts)); + dispatch(setOrderingOpts(orderingOpts)); } }); } else { @@ -140,24 +159,16 @@ export function setMetrics(metrics) { return { type: SET_METRICS, metrics }; } -export function addColumn(column) { - return { type: ADD_COLUMN, column }; +export function setNotGroupByColumns(columns) { + return { type: SET_NOT_GROUPBY_COLUMNS, columns }; } -export function removeColumn(column) { - return { type: REMOVE_COLUMN, column }; +export function setOrderings(orderings) { + return { type: SET_ORDERINGS, orderings }; } -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 setTimeStampFormat(timeStampFormat) { + return { type: SET_TIME_STAMP_FORMAT, timeStampFormat }; } export function setRowLimit(rowLimit) { diff --git a/caravel/assets/javascripts/explorev2/components/ControlPanelsContainer.jsx b/caravel/assets/javascripts/explorev2/components/ControlPanelsContainer.jsx index c3f70e9532..a3a0ccbcd2 100644 --- a/caravel/assets/javascripts/explorev2/components/ControlPanelsContainer.jsx +++ b/caravel/assets/javascripts/explorev2/components/ControlPanelsContainer.jsx @@ -1,20 +1,36 @@ import React from 'react'; +import { connect } from 'react-redux'; 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'; +import { DefaultControls, VIZ_CONTROL_MAPPING } from '../constants'; -const ControlPanelsContainer = function () { +const propTypes = { + vizType: React.PropTypes.string, +}; + +const defaultProps = { + vizType: null, +}; + +function ControlPanelsContainer(props) { return ( - - - - - + {DefaultControls} + {VIZ_CONTROL_MAPPING[props.vizType]} ); -}; -export default ControlPanelsContainer; +} + +ControlPanelsContainer.propTypes = propTypes; +ControlPanelsContainer.defaultProps = defaultProps; + +function mapStateToProps(state) { + return { + vizType: state.vizType, + }; +} + +function mapDispatchToProps() { + return {}; +} + +export default connect(mapStateToProps, mapDispatchToProps)(ControlPanelsContainer); diff --git a/caravel/assets/javascripts/explorev2/components/Filters.jsx b/caravel/assets/javascripts/explorev2/components/Filters.jsx index 49e384157f..13e465c610 100644 --- a/caravel/assets/javascripts/explorev2/components/Filters.jsx +++ b/caravel/assets/javascripts/explorev2/components/Filters.jsx @@ -109,7 +109,6 @@ class Filters extends React.Component { } Filters.propTypes = propTypes; - Filters.defaultProps = defaultProps; function mapStateToProps(state) { diff --git a/caravel/assets/javascripts/explorev2/components/GroupBy.jsx b/caravel/assets/javascripts/explorev2/components/GroupBy.jsx index cbf10fdfe5..2d7756c8df 100644 --- a/caravel/assets/javascripts/explorev2/components/GroupBy.jsx +++ b/caravel/assets/javascripts/explorev2/components/GroupBy.jsx @@ -20,11 +20,11 @@ const defaultProps = { }; class GroupBy extends React.Component { - changeColumns(groupByColumnOpts) { - this.props.actions.setGroupByColumns(groupByColumnOpts); + changeColumns(groupByColumns) { + this.props.actions.setGroupByColumns(groupByColumns); } - changeMetrics(metricsOpts) { - this.props.actions.setMetrics(metricsOpts); + changeMetrics(metrics) { + this.props.actions.setMetrics(metrics); } render() { return ( diff --git a/caravel/assets/javascripts/explorev2/components/NotGroupBy.jsx b/caravel/assets/javascripts/explorev2/components/NotGroupBy.jsx new file mode 100644 index 0000000000..ece64525e2 --- /dev/null +++ b/caravel/assets/javascripts/explorev2/components/NotGroupBy.jsx @@ -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, + columnOpts: React.PropTypes.array, + columns: React.PropTypes.array, + orderingOpts: React.PropTypes.array, + orderings: React.PropTypes.array, +}; + +const defaultProps = { + columnOpts: [], + columns: [], + orderingOpts: [], + orderings: [], +}; + +class NotGroupBy extends React.Component { + changeColumns(columns) { + this.props.actions.setNotGroupByColumns(columns); + } + changeOrderings(orderings) { + this.props.actions.setOrderings(orderings); + } + render() { + return ( +
+
Not GroupBy
+
+
+
Columns
+ +
+
+
+ ); + } +} + +NotGroupBy.propTypes = propTypes; +NotGroupBy.defaultProps = defaultProps; + +function mapStateToProps(state) { + return { + columnOpts: state.columnOpts, + columns: state.columns, + orderingOpts: state.orderingOpts, + orderings: state.orderings, + }; +} + +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators(actions, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(NotGroupBy); diff --git a/caravel/assets/javascripts/explorev2/components/Options.jsx b/caravel/assets/javascripts/explorev2/components/Options.jsx new file mode 100644 index 0000000000..687c197fb5 --- /dev/null +++ b/caravel/assets/javascripts/explorev2/components/Options.jsx @@ -0,0 +1,76 @@ +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 { timestampOptions, rowLimitOptions } from '../constants'; + +const propTypes = { + actions: React.PropTypes.object, + timeStampFormat: React.PropTypes.string, + rowLimit: React.PropTypes.number, +}; + +const defaultProps = { + timeStampFormat: null, + rowLimit: null, +}; + +class Options extends React.Component { + changeTimeStampFormat(timeStampFormat) { + const val = (timeStampFormat) ? timeStampFormat.value : null; + this.props.actions.setTimeStampFormat(val); + } + changeRowLimit(rowLimit) { + this.props.actions.setRowLimit(rowLimit); + } + render() { + return ( +
+
Options
+
+
+
Table Timestamp Format
+ ({ value: r, label: r }))} + value={this.props.rowLimit} + autosize={false} + onChange={this.changeRowLimit.bind(this)} + /> +
+
+
+ ); + } +} + +Options.propTypes = propTypes; +Options.defaultProps = defaultProps; + +function mapStateToProps(state) { + return { + timeStampFormat: state.timeStampFormat, + rowLimit: state.rowLimit, + }; +} + +function mapDispatchToProps(dispatch) { + return { + actions: bindActionCreators(actions, dispatch), + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(Options); diff --git a/caravel/assets/javascripts/explorev2/constants.js b/caravel/assets/javascripts/explorev2/constants.js index 2415eaa43e..384abe30be 100644 --- a/caravel/assets/javascripts/explorev2/constants.js +++ b/caravel/assets/javascripts/explorev2/constants.js @@ -1,3 +1,12 @@ +import React from 'react'; +import TimeFilter from './components/TimeFilter'; +import ChartControl from './components/ChartControl'; +import GroupBy from './components/GroupBy'; +import SqlClause from './components/SqlClause'; +import Filters from './components/Filters'; +import NotGroupBy from './components/NotGroupBy'; +import Options from './components/Options'; + export const VIZ_TYPES = [ { value: 'dist_bar', label: 'Distribution - Bar Chart', requiresTime: false }, { value: 'pie', label: 'Pie Chart', requiresTime: false }, @@ -33,3 +42,35 @@ export const sinceOptions = ['1 hour ago', '12 hours ago', '1 day ago', '7 days ago', '28 days ago', '90 days ago', '1 year ago']; export const untilOptions = ['now', '1 day ago', '7 days ago', '28 days ago', '90 days ago', '1 year ago']; + +export const timestampOptions = [ + ['smart_date', 'Adaptative formating'], + ['%m/%d/%Y', '"%m/%d/%Y" | 01/14/2019'], + ['%Y-%m-%d', '"%Y-%m-%d" | 2019-01-14'], + ['%Y-%m-%d %H:%M:%S', + '"%Y-%m-%d %H:%M:%S" | 2019-01-14 01:32:10'], + ['%H:%M:%S', '"%H:%M:%S" | 01:32:10'], +]; + +export const rowLimitOptions = [10, 50, 100, 250, 500, 1000, 5000, 10000, 50000]; + +export const DefaultControls = ( +
+ + + + + +
+); + +export const TableVizControls = ( +
+ + +
+); + +export const VIZ_CONTROL_MAPPING = { + table: TableVizControls, +}; diff --git a/caravel/assets/javascripts/explorev2/reducers/exploreReducer.js b/caravel/assets/javascripts/explorev2/reducers/exploreReducer.js index da650d120a..9e5d9d94f4 100644 --- a/caravel/assets/javascripts/explorev2/reducers/exploreReducer.js +++ b/caravel/assets/javascripts/explorev2/reducers/exploreReducer.js @@ -40,31 +40,19 @@ export const exploreReducer = function (state, action) { [actions.SET_METRICS]() { return Object.assign({}, state, { metrics: action.metrics }); }, - [actions.ADD_COLUMN]() { - return Object.assign({}, state, { columns: [...state.columns, action.column] }); + [actions.SET_COLUMN_OPTS]() { + return Object.assign({}, state, { columnOpts: action.columnOpts }); }, - [actions.REMOVE_COLUMN]() { - const newColumns = []; - state.columns.forEach((c) => { - if (c !== action.column) { - newColumns.push(c); - } - }); - return Object.assign({}, state, { columns: newColumns }); + [actions.SET_NOT_GROUPBY_COLUMNS]() { + return Object.assign({}, state, { columns: action.columns }); }, - [actions.ADD_ORDERING]() { - return Object.assign({}, state, { orderings: [...state.orderings, action.ordering] }); + [actions.SET_ORDERING_OPTS]() { + return Object.assign({}, state, { orderingOpts: action.orderingOpts }); }, - [actions.REMOVE_ORDERING]() { - const newOrderings = []; - state.orderings.forEach((o) => { - if (o !== action.ordering) { - newOrderings.push(o); - } - }); - return Object.assign({}, state, { orderings: newOrderings }); + [actions.SET_ORDERINGS]() { + return Object.assign({}, state, { orderings: action.orderings }); }, - [actions.SET_TIME_STAMP]() { + [actions.SET_TIME_STAMP_FORMAT]() { return Object.assign({}, state, { timeStampFormat: action.timeStampFormat }); }, [actions.SET_ROW_LIMIT]() { diff --git a/caravel/assets/javascripts/explorev2/stores/store.js b/caravel/assets/javascripts/explorev2/stores/store.js index 80d1bd7296..236013c1da 100644 --- a/caravel/assets/javascripts/explorev2/stores/store.js +++ b/caravel/assets/javascripts/explorev2/stores/store.js @@ -13,10 +13,12 @@ export const initialState = { groupByColumns: [], metricsOpts: [], metrics: [], + columnOpts: [], columns: [], + orderingOpts: [], orderings: [], - timeStampFormat: null, - rowLimit: null, + timeStampFormat: 'smart_date', + rowLimit: 50000, searchBox: false, whereClause: '', havingClause: '', @@ -35,8 +37,8 @@ export const defaultFormData = { metrics: [], columns: [], orderings: [], - timeStampFormat: null, - rowLimit: null, + timeStampFormat: 'smart_date', + rowLimit: 50000, searchBox: false, whereClause: '', havingClause: '', @@ -49,4 +51,6 @@ export const defaultOpts = { groupByColumnOpts: [], metricsOpts: [], filterColumnOpts: [], + columnOpts: [], + orderingOpts: [], }; diff --git a/caravel/assets/spec/javascripts/explore/components/actions_spec.js b/caravel/assets/spec/javascripts/explore/components/actions_spec.js index 06cb77e4d9..6c18b2a0ee 100644 --- a/caravel/assets/spec/javascripts/explore/components/actions_spec.js +++ b/caravel/assets/spec/javascripts/explore/components/actions_spec.js @@ -16,34 +16,20 @@ describe('reducers', () => { expect(newState.vizType).to.equal('bar'); }); - it('should return new state with added column', () => { - const newColumn = 'col'; - const newState = exploreReducer(initialState, actions.addColumn(newColumn)); - expect(newState.columns).to.deep.equal([newColumn]); + it('should return new state with not groupby columns', () => { + const newColumn = ['col']; + const newState = exploreReducer(initialState, actions.setNotGroupByColumns(newColumn)); + expect(newState.columns).to.deep.equal(['col']); }); - it('should return new state with removed column', () => { - const testState = { initialState, columns: ['col1', 'col2'] }; - const remColumn = 'col1'; - const newState = exploreReducer(testState, actions.removeColumn(remColumn)); - expect(newState.columns).to.deep.equal(['col2']); - }); - - it('should return new state with added ordering', () => { - const newOrdering = 'ord'; - const newState = exploreReducer(initialState, actions.addOrdering(newOrdering)); + it('should return new state with orderings', () => { + const newOrdering = ['ord']; + const newState = exploreReducer(initialState, actions.setOrderings(newOrdering)); expect(newState.orderings).to.deep.equal(['ord']); }); - it('should return new state with removed ordering', () => { - const testState = { initialState, orderings: ['ord1', 'ord2'] }; - const remOrdering = 'ord1'; - const newState = exploreReducer(testState, actions.removeOrdering(remOrdering)); - expect(newState.orderings).to.deep.equal(['ord2']); - }); - it('should return new state with time stamp', () => { - const newState = exploreReducer(initialState, actions.setTimeStamp(1)); + const newState = exploreReducer(initialState, actions.setTimeStampFormat(1)); expect(newState.timeStampFormat).to.equal(1); }); diff --git a/caravel/views.py b/caravel/views.py index 0124f788f6..c9574d2980 100755 --- a/caravel/views.py +++ b/caravel/views.py @@ -1958,10 +1958,16 @@ class Caravel(BaseCaravelView): if not self.datasource_access(datasource): return json_error_response(DATASOURCE_ACCESS_ERR) + order_by_choices = [] + for s in sorted(datasource.num_cols): + order_by_choices.append(s + ' [asc]') + order_by_choices.append(s + ' [desc]') column_opts = { "groupby_cols": datasource.groupby_column_names, "metrics": datasource.metrics_combo, - "filter_cols": datasource.filterable_column_names + "filter_cols": datasource.filterable_column_names, + "columns": datasource.column_names, + "ordering_cols": order_by_choices } form_data = dict( column_opts.items() + datasource.time_column_grains.items()