Skip to content

Commit

Permalink
Added filter in ControlPanelsContainer for explore V2 (#1647)
Browse files Browse the repository at this point in the history
* Added filter in ControlPanelsContainer

* Move function for getting url params object to utils

* Fixed python test

* Move Filter to separate component

* Added specs and made changes based on comments

* Moved specs to right folder
  • Loading branch information
vera-liu authored Nov 23, 2016
1 parent cef4a82 commit 39ce4aa
Show file tree
Hide file tree
Showing 15 changed files with 354 additions and 133 deletions.
17 changes: 3 additions & 14 deletions superset/assets/javascripts/explorev2/actions/exploreActions.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/* eslint camelcase: 0 */
const $ = window.$ = require('jquery');

const FAVESTAR_BASE_URL = '/superset/favstar/slice';

export const SET_FIELD_OPTIONS = 'SET_FIELD_OPTIONS';
Expand Down Expand Up @@ -89,19 +88,9 @@ export function removeFilter(filter) {
return { type: REMOVE_FILTER, filter };
}

export const CHANGE_FILTER_FIELD = 'CHANGE_FILTER_FIELD';
export function changeFilterField(filter, field) {
return { type: CHANGE_FILTER_FIELD, filter, field };
}

export const CHANGE_FILTER_OP = 'CHANGE_FILTER_OP';
export function changeFilterOp(filter, op) {
return { type: CHANGE_FILTER_OP, filter, op };
}

export const CHANGE_FILTER_VALUE = 'CHANGE_FILTER_VALUE';
export function changeFilterValue(filter, value) {
return { type: CHANGE_FILTER_VALUE, filter, value };
export const CHANGE_FILTER = 'CHANGE_FILTER';
export function changeFilter(filter, field, value) {
return { type: CHANGE_FILTER, filter, field, value };
}

export const SET_FIELD_VALUE = 'SET_FIELD_VALUE';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import { bindActionCreators } from 'redux';
import * as actions from '../actions/exploreActions';
import { connect } from 'react-redux';
import { Panel, Alert } from 'react-bootstrap';
import { visTypes, sectionsToRender } from '../stores/store';
import { visTypes, sectionsToRender, commonControlPanelSections } from '../stores/store';
import ControlPanelSection from './ControlPanelSection';
import FieldSetRow from './FieldSetRow';
import Filters from './Filters';

const propTypes = {
datasource_type: PropTypes.string.isRequired,
Expand Down Expand Up @@ -44,6 +45,12 @@ class ControlPanelsContainer extends React.Component {
return sectionsToRender(this.props.form_data.viz_type, this.props.datasource_type);
}

filterSectionsToRender() {
const filterSections = this.props.datasource_type === 'table' ?
[commonControlPanelSections.filters[0]] : commonControlPanelSections.filters;
return filterSections;
}

fieldOverrides() {
const viz = visTypes[this.props.form_data.viz_type];
return viz.fieldOverrides;
Expand Down Expand Up @@ -86,7 +93,20 @@ class ControlPanelsContainer extends React.Component {
))}
</ControlPanelSection>
))}
{/* TODO: add filters section */}
{this.filterSectionsToRender().map((section) => (
<ControlPanelSection
key={section.label}
label={section.label}
tooltip={section.description}
>
<Filters
filterColumnOpts={[]}
filters={this.props.form_data.filters}
actions={this.props.actions}
prefix={section.prefix}
/>
</ControlPanelSection>
))}
</div>
</div>
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import ControlPanelsContainer from './ControlPanelsContainer';
import SaveModal from './SaveModal';
import QueryAndSaveBtns from '../../explore/components/QueryAndSaveBtns';
import { autoQueryFields } from '../stores/store';
import { getParamObject } from '../../modules/utils.js';

const $ = require('jquery');

const propTypes = {
Expand Down Expand Up @@ -47,18 +49,7 @@ class ExploreViewContainer extends React.Component {
}

onQuery(form_data) {
const data = {};
Object.keys(form_data).forEach((field) => {
// filter out null fields
if (form_data[field] !== null && field !== 'datasource') {
data[field] = form_data[field];
}
});
// V2 tag temporarily for updating url
// Todo: remove after launch
data.V2 = true;
data.datasource_id = this.props.form_data.datasource;
data.datasource_type = this.props.datasource_type;
const data = getParamObject(form_data, this.props.datasource_type);
this.queryFormData(data);

const params = $.param(data, true);
Expand Down
92 changes: 92 additions & 0 deletions superset/assets/javascripts/explorev2/components/Filter.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React from 'react';
// import { Tab, Row, Col, Nav, NavItem } from 'react-bootstrap';
import Select from 'react-select';
import { Button } from 'react-bootstrap';

const propTypes = {
actions: React.PropTypes.object.isRequired,
filterColumnOpts: React.PropTypes.array,
prefix: React.PropTypes.string,
filter: React.PropTypes.object.isRequired,
};

const defaultProps = {
filterColumnOpts: [],
prefix: 'flt',
};

export default class Filter extends React.Component {
constructor(props) {
super(props);
const opChoices = this.props.prefix === 'flt' ?
['in', 'not in'] : ['==', '!=', '>', '<', '>=', '<='];
this.state = {
opChoices,
};
}
changeCol(filter, colOpt) {
const val = (colOpt) ? colOpt.value : null;
this.props.actions.changeFilter(filter, 'col', val);
}
changeOp(filter, opOpt) {
const val = (opOpt) ? opOpt.value : null;
this.props.actions.changeFilter(filter, 'op', val);
}
changeValue(filter, event) {
this.props.actions.changeFilter(filter, 'value', event.target.value);
}
removeFilter(filter) {
this.props.actions.removeFilter(filter);
}
render() {
return (
<div>
<div className="row space-1">
<Select
className="col-lg-12"
multi={false}
name="select-column"
placeholder="Select column"
options={this.props.filterColumnOpts.map((o) => ({ value: o, label: o }))}
value={this.props.filter.col}
autosize={false}
onChange={this.changeCol.bind(this, this.props.filter)}
/>
</div>
<div className="row space-1">
<Select
className="col-lg-4"
multi={false}
name="select-op"
placeholder="Select operator"
options={this.state.opChoices.map((o) => ({ value: o, label: o }))}
value={this.props.filter.op}
autosize={false}
onChange={this.changeOp.bind(this, this.props.filter)}
/>
<div className="col-lg-6">
<input
type="text"
onChange={this.changeValue.bind(this, this.props.filter)}
value={this.props.filter.value}
className="form-control input-sm"
placeholder="Filter value"
/>
</div>
<div className="col-lg-2">
<Button
id="remove-button"
bsSize="small"
onClick={this.removeFilter.bind(this, this.props.filter)}
>
<i className="fa fa-minus" />
</Button>
</div>
</div>
</div>
);
}
}

Filter.propTypes = propTypes;
Filter.defaultProps = defaultProps;
113 changes: 31 additions & 82 deletions superset/assets/javascripts/explorev2/components/Filters.jsx
Original file line number Diff line number Diff line change
@@ -1,109 +1,63 @@
import React from 'react';
// import { Tab, Row, Col, Nav, NavItem } from 'react-bootstrap';
import Select from 'react-select';
import Filter from './Filter';
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,
actions: React.PropTypes.object.isRequired,
filterColumnOpts: React.PropTypes.array,
filters: React.PropTypes.array,
prefix: React.PropTypes.string,
};

const defaultProps = {
filterColumnOpts: [],
filters: [],
prefix: 'flt',
};

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,
prefix: this.props.prefix,
col: 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)}
const filters = [];
this.props.filters.forEach((filter) => {
// only display filters with current prefix
if (filter.prefix === this.props.prefix) {
filters.push(
<Filter
filterColumnOpts={this.props.filterColumnOpts}
actions={this.props.actions}
prefix={this.props.prefix}
filter={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">
);
}
});
return (
<div>
{filters}
<div className="row space-2">
<div className="col-lg-2">
<Button
bsStyle="primary"
onClick={this.removeFilter.bind(this, filter)}
id="add-button"
bsSize="sm"
onClick={this.addFilter.bind(this)}
>
<i className="fa fa-minus" />
<i className="fa fa-plus" /> &nbsp; Add Filter
</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>
);
}
}
Expand All @@ -114,14 +68,9 @@ Filters.defaultProps = defaultProps;
function mapStateToProps(state) {
return {
filterColumnOpts: state.filterColumnOpts,
filters: state.filters,
};
}

function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(actions, dispatch),
filters: state.viz.form_data.filters,
};
}

export default connect(mapStateToProps, mapDispatchToProps)(Filters);
export { Filters };
export default connect(mapStateToProps, () => ({}))(Filters);
12 changes: 2 additions & 10 deletions superset/assets/javascripts/explorev2/components/SaveModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import $ from 'jquery';
import { Modal, Alert, Button, Radio } from 'react-bootstrap';
import Select from 'react-select';
import { connect } from 'react-redux';
import { getParamObject } from '../../modules/utils.js';

const propTypes = {
can_edit: PropTypes.bool,
Expand Down Expand Up @@ -57,10 +58,8 @@ class SaveModal extends React.Component {
saveOrOverwrite(gotodash) {
this.setState({ alert: null });
this.props.actions.removeSaveModalAlert();
const params = {};
const params = getParamObject(this.props.form_data, this.props.datasource_type);
const sliceParams = {};
params.datasource_id = this.props.form_data.datasource;
params.datasource_type = this.props.datasource_type;
params.datasource_name = this.props.form_data.datasource_name;

let sliceName = null;
Expand All @@ -76,12 +75,6 @@ class SaveModal extends React.Component {
sliceParams.slice_name = this.props.form_data.slice_name;
}

Object.keys(this.props.form_data).forEach((field) => {
if (this.props.form_data[field] !== null && field !== 'slice_name') {
params[field] = this.props.form_data[field];
}
});

const addToDash = this.state.addToDash;
sliceParams.add_to_dash = addToDash;
let dashboard = null;
Expand All @@ -105,7 +98,6 @@ class SaveModal extends React.Component {
default:
dashboard = null;
}
params.V2 = true;
sliceParams.goto_dash = gotodash;
const baseUrl = '/superset/explore/' +
`${this.props.datasource_type}/${this.props.form_data.datasource}/`;
Expand Down
Loading

0 comments on commit 39ce4aa

Please sign in to comment.