mirror of
https://github.com/apache/superset.git
synced 2026-04-19 08:04:53 +00:00
Enable freeform-select with fetched column values for filter values (#1697)
* Enable freeform-select with fetched column values for filter values - db migration to add filter_select_enabled - add freeform-multi option for Selectfield - modify formatFilter() function on query to accomodate filter-select * Fix js tests * Fix codeclimate issue * Changes based on comments * Add test for filter endpoint * Extract out renderFilterFormField function from render * Fix landscape issues
This commit is contained in:
@@ -93,6 +93,26 @@ export function changeFilter(filter, field, value) {
|
||||
return { type: CHANGE_FILTER, filter, field, value };
|
||||
}
|
||||
|
||||
export function fetchFilterValues(datasource_type, datasource_id, filter, col) {
|
||||
return function (dispatch) {
|
||||
$.ajax({
|
||||
type: 'GET',
|
||||
url: `/superset/filter/${datasource_type}/${datasource_id}/${col}/`,
|
||||
success: (data) => {
|
||||
dispatch(changeFilter(
|
||||
filter,
|
||||
'choices',
|
||||
Object.keys(data).map((k) => ([`'${data[k]}'`, `'${data[k]}'`]))
|
||||
)
|
||||
);
|
||||
},
|
||||
error() {
|
||||
dispatch(changeFilter(filter, 'choices', []));
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export const SET_FIELD_VALUE = 'SET_FIELD_VALUE';
|
||||
export function setFieldValue(datasource_type, key, value, label) {
|
||||
return { type: SET_FIELD_VALUE, datasource_type, key, value, label };
|
||||
|
||||
@@ -102,6 +102,7 @@ class ControlPanelsContainer extends React.Component {
|
||||
filters={this.props.form_data.filters}
|
||||
actions={this.props.actions}
|
||||
prefix={section.prefix}
|
||||
datasource_id={this.props.form_data.datasource}
|
||||
/>
|
||||
</ControlPanelSection>
|
||||
))}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
import React from 'react';
|
||||
// import { Tab, Row, Col, Nav, NavItem } from 'react-bootstrap';
|
||||
import Select from 'react-select';
|
||||
import { Button } from 'react-bootstrap';
|
||||
import SelectField from './SelectField';
|
||||
|
||||
const propTypes = {
|
||||
actions: React.PropTypes.object.isRequired,
|
||||
filterColumnOpts: React.PropTypes.array,
|
||||
prefix: React.PropTypes.string,
|
||||
filter: React.PropTypes.object.isRequired,
|
||||
renderFilterSelect: React.PropTypes.bool,
|
||||
datasource_type: React.PropTypes.string.isRequired,
|
||||
datasource_id: React.PropTypes.number.isRequired,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
@@ -24,9 +27,22 @@ export default class Filter extends React.Component {
|
||||
opChoices,
|
||||
};
|
||||
}
|
||||
componentWillMount() {
|
||||
if (this.props.filter.col) {
|
||||
this.props.actions.fetchFilterValues(
|
||||
this.props.datasource_type,
|
||||
this.props.datasource_id,
|
||||
this.props.filter,
|
||||
this.props.filter.col);
|
||||
}
|
||||
}
|
||||
changeCol(filter, colOpt) {
|
||||
const val = (colOpt) ? colOpt.value : null;
|
||||
this.props.actions.changeFilter(filter, 'col', val);
|
||||
if (val) {
|
||||
this.props.actions.fetchFilterValues(
|
||||
this.props.datasource_type, this.props.datasource_id, filter, val);
|
||||
}
|
||||
}
|
||||
changeOp(filter, opOpt) {
|
||||
const val = (opOpt) ? opOpt.value : null;
|
||||
@@ -35,9 +51,35 @@ export default class Filter extends React.Component {
|
||||
changeValue(filter, event) {
|
||||
this.props.actions.changeFilter(filter, 'value', event.target.value);
|
||||
}
|
||||
changeSelectValue(filter, name, value) {
|
||||
this.props.actions.changeFilter(filter, 'value', value);
|
||||
}
|
||||
removeFilter(filter) {
|
||||
this.props.actions.removeFilter(filter);
|
||||
}
|
||||
renderFilterFormField() {
|
||||
if (this.props.renderFilterSelect) {
|
||||
return (
|
||||
<SelectField
|
||||
multi
|
||||
freeForm
|
||||
name="filter-value"
|
||||
value={this.props.filter.value}
|
||||
choices={this.props.filter.choices ? this.props.filter.choices : []}
|
||||
onChange={this.changeSelectValue.bind(this, this.props.filter)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<input
|
||||
type="text"
|
||||
onChange={this.changeValue.bind(this, this.props.filter)}
|
||||
value={this.props.filter.value}
|
||||
className="form-control input-sm"
|
||||
placeholder="Filter value"
|
||||
/>
|
||||
);
|
||||
}
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
@@ -65,13 +107,7 @@ export default class Filter extends React.Component {
|
||||
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"
|
||||
/>
|
||||
{this.renderFilterFormField()}
|
||||
</div>
|
||||
<div className="col-lg-2">
|
||||
<Button
|
||||
|
||||
@@ -7,9 +7,12 @@ import shortid from 'shortid';
|
||||
|
||||
const propTypes = {
|
||||
actions: React.PropTypes.object.isRequired,
|
||||
datasource_type: React.PropTypes.string.isRequired,
|
||||
datasource_id: React.PropTypes.number.isRequired,
|
||||
filterColumnOpts: React.PropTypes.array,
|
||||
filters: React.PropTypes.array,
|
||||
prefix: React.PropTypes.string,
|
||||
renderFilterSelect: React.PropTypes.bool,
|
||||
};
|
||||
|
||||
const defaultProps = {
|
||||
@@ -42,6 +45,9 @@ class Filters extends React.Component {
|
||||
actions={this.props.actions}
|
||||
prefix={this.props.prefix}
|
||||
filter={filter}
|
||||
renderFilterSelect={this.props.renderFilterSelect}
|
||||
datasource_type={this.props.datasource_type}
|
||||
datasource_id={this.props.datasource_id}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -70,8 +76,10 @@ Filters.defaultProps = defaultProps;
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
datasource_type: state.datasource_type,
|
||||
filterColumnOpts: state.filterColumnOpts,
|
||||
filters: state.viz.form_data.filters,
|
||||
renderFilterSelect: state.filter_select,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -58,8 +58,18 @@ export default class SelectField extends React.Component {
|
||||
if (this.props.freeForm) {
|
||||
// For FreeFormSelect, insert value into options if not exist
|
||||
const values = choices.map((c) => c[0]);
|
||||
if (values.indexOf(this.props.value) === -1) {
|
||||
options.push({ value: this.props.value, label: this.props.value });
|
||||
if (this.props.value) {
|
||||
if (typeof this.props.value === 'object') {
|
||||
this.props.value.forEach((v) => {
|
||||
if (values.indexOf(v) === -1) {
|
||||
options.push({ value: v, label: v });
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (values.indexOf(this.props.value) === -1) {
|
||||
options.push({ value: this.props.value, label: this.props.value });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,13 +87,19 @@ export default class SelectField extends React.Component {
|
||||
// Tab, comma or Enter will trigger a new option created for FreeFormSelect
|
||||
const selectWrap = this.props.freeForm ?
|
||||
(<Creatable {...selectProps} />) : (<Select {...selectProps} />);
|
||||
|
||||
if (this.props.label) {
|
||||
return (
|
||||
<div id={`formControlsSelect-${slugify(this.props.label)}`}>
|
||||
<ControlLabelWithTooltip
|
||||
label={this.props.label}
|
||||
description={this.props.description}
|
||||
/>
|
||||
{selectWrap}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div id={`formControlsSelect-${slugify(this.props.label)}`}>
|
||||
<ControlLabelWithTooltip
|
||||
label={this.props.label}
|
||||
description={this.props.description}
|
||||
/>
|
||||
<div>
|
||||
{selectWrap}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -25,6 +25,7 @@ const bootstrappedState = Object.assign(
|
||||
initialState(bootstrapData.viz.form_data.viz_type, bootstrapData.datasource_type), {
|
||||
can_edit: bootstrapData.can_edit,
|
||||
can_download: bootstrapData.can_download,
|
||||
filter_select: bootstrapData.filter_select,
|
||||
datasources: bootstrapData.datasources,
|
||||
datasource_type: bootstrapData.datasource_type,
|
||||
viz: bootstrapData.viz,
|
||||
|
||||
@@ -43,6 +43,7 @@ export function initialState(vizType = 'table', datasourceType = 'table') {
|
||||
datasources: null,
|
||||
datasource_type: null,
|
||||
filterColumnOpts: [],
|
||||
filter_select: false,
|
||||
fields,
|
||||
viz: defaultViz(vizType, datasourceType),
|
||||
isStarred: false,
|
||||
|
||||
Reference in New Issue
Block a user