Renaming field to control (#2210)

This commit is contained in:
Maxime Beauchemin
2017-02-22 08:36:47 -08:00
committed by GitHub
parent d5ba88b407
commit 1e47d6fb41
30 changed files with 415 additions and 411 deletions

View File

@@ -0,0 +1,32 @@
import React, { PropTypes } from 'react';
import { Checkbox } from 'react-bootstrap';
const propTypes = {
name: PropTypes.string.isRequired,
value: PropTypes.bool,
label: PropTypes.string,
description: PropTypes.string,
onChange: PropTypes.func,
};
const defaultProps = {
value: false,
onChange: () => {},
};
export default class CheckboxControl extends React.Component {
onToggle() {
this.props.onChange(!this.props.value);
}
render() {
return (
<Checkbox
checked={this.props.value}
onChange={this.onToggle.bind(this)}
/>
);
}
}
CheckboxControl.propTypes = propTypes;
CheckboxControl.defaultProps = defaultProps;

View File

@@ -0,0 +1,138 @@
const $ = window.$ = require('jquery');
import React, { PropTypes } from 'react';
import Select from 'react-select';
import { Button, Row, Col } from 'react-bootstrap';
import SelectControl from './SelectControl';
const propTypes = {
choices: PropTypes.array,
changeFilter: PropTypes.func,
removeFilter: PropTypes.func,
filter: PropTypes.object.isRequired,
datasource: PropTypes.object,
having: PropTypes.bool,
};
const defaultProps = {
having: false,
changeFilter: () => {},
removeFilter: () => {},
choices: [],
datasource: null,
};
export default class Filter extends React.Component {
constructor(props) {
super(props);
this.opChoices = this.props.having ? ['==', '!=', '>', '<', '>=', '<=']
: ['in', 'not in'];
}
fetchFilterValues(col) {
if (!this.props.datasource) {
return;
}
const datasource = this.props.datasource;
let choices = [];
if (col) {
$.ajax({
type: 'GET',
url: `/superset/filter/${datasource.type}/${datasource.id}/${col}/`,
success: (data) => {
choices = Object.keys(data).map((k) =>
([`'${data[k]}'`, `'${data[k]}'`]));
this.props.changeFilter('choices', choices);
},
});
}
}
changeFilter(control, event) {
let value = event;
if (event && event.target) {
value = event.target.value;
}
if (event && event.value) {
value = event.value;
}
this.props.changeFilter(control, value);
if (control === 'col' && value !== null && this.props.datasource.filter_select) {
this.fetchFilterValues(value);
}
}
removeFilter(filter) {
this.props.removeFilter(filter);
}
renderFilterFormControl(filter) {
const datasource = this.props.datasource;
if (datasource && datasource.filter_select) {
if (!filter.choices) {
this.fetchFilterValues(filter.col);
}
}
if (this.props.having) {
// druid having filter
return (
<input
type="text"
onChange={this.changeFilter.bind(this, 'val')}
value={filter.value}
className="form-control input-sm"
placeholder="Filter value"
/>
);
}
return (
<SelectControl
multi
freeForm
name="filter-value"
value={filter.val}
choices={filter.choices || []}
onChange={this.changeFilter.bind(this, 'val')}
/>
);
}
render() {
const filter = this.props.filter;
return (
<div>
<Row className="space-1">
<Col md={12}>
<Select
id="select-col"
placeholder="Select column"
options={this.props.choices.map((c) => ({ value: c[0], label: c[1] }))}
value={filter.col}
onChange={this.changeFilter.bind(this, 'col')}
/>
</Col>
</Row>
<Row className="space-1">
<Col md={3}>
<Select
id="select-op"
placeholder="Select operator"
options={this.opChoices.map((o) => ({ value: o, label: o }))}
value={filter.op}
onChange={this.changeFilter.bind(this, 'op')}
/>
</Col>
<Col md={7}>
{this.renderFilterFormControl(filter)}
</Col>
<Col md={2}>
<Button
id="remove-button"
bsSize="small"
onClick={this.removeFilter.bind(this)}
>
<i className="fa fa-minus" />
</Button>
</Col>
</Row>
</div>
);
}
}
Filter.propTypes = propTypes;
Filter.defaultProps = defaultProps;

View File

@@ -0,0 +1,76 @@
import React, { PropTypes } from 'react';
import { Button, Row, Col } from 'react-bootstrap';
import Filter from './Filter';
const propTypes = {
name: PropTypes.string,
choices: PropTypes.array,
onChange: PropTypes.func,
value: PropTypes.array,
datasource: PropTypes.object,
};
const defaultProps = {
choices: [],
onChange: () => {},
value: [],
};
export default class FilterControl extends React.Component {
addFilter() {
const newFilters = Object.assign([], this.props.value);
newFilters.push({
col: null,
op: 'in',
val: this.props.datasource.filter_select ? [] : '',
});
this.props.onChange(newFilters);
}
changeFilter(index, control, value) {
const newFilters = Object.assign([], this.props.value);
const modifiedFilter = Object.assign({}, newFilters[index]);
modifiedFilter[control] = value;
newFilters.splice(index, 1, modifiedFilter);
this.props.onChange(newFilters);
}
removeFilter(index) {
this.props.onChange(this.props.value.filter((f, i) => i !== index));
}
render() {
const filters = [];
this.props.value.forEach((filter, i) => {
const filterBox = (
<div key={i}>
<Filter
having={this.props.name === 'having_filters'}
filter={filter}
choices={this.props.choices}
datasource={this.props.datasource}
removeFilter={this.removeFilter.bind(this, i)}
changeFilter={this.changeFilter.bind(this, i)}
/>
</div>
);
filters.push(filterBox);
});
return (
<div>
{filters}
<Row className="space-2">
<Col md={2}>
<Button
id="add-button"
bsSize="sm"
onClick={this.addFilter.bind(this)}
>
<i className="fa fa-plus" /> &nbsp; Add Filter
</Button>
</Col>
</Row>
</div>
);
}
}
FilterControl.propTypes = propTypes;
FilterControl.defaultProps = defaultProps;

View File

@@ -0,0 +1,24 @@
import React, { PropTypes } from 'react';
import { FormControl } from 'react-bootstrap';
const propTypes = {
onChange: PropTypes.func,
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]),
};
const defaultProps = {
onChange: () => {},
};
export default class HiddenControl extends React.PureComponent {
render() {
// This wouldn't be necessary but might as well
return <FormControl type="hidden" value={this.props.value} />;
}
}
HiddenControl.propTypes = propTypes;
HiddenControl.defaultProps = defaultProps;

View File

@@ -0,0 +1,115 @@
import React, { PropTypes } from 'react';
import Select, { Creatable } from 'react-select';
const propTypes = {
choices: PropTypes.array,
clearable: PropTypes.bool,
description: PropTypes.string,
freeForm: PropTypes.bool,
isLoading: PropTypes.bool,
label: PropTypes.string,
multi: PropTypes.bool,
name: PropTypes.string.isRequired,
onChange: PropTypes.func,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.array]),
};
const defaultProps = {
choices: [],
clearable: true,
description: null,
freeForm: false,
isLoading: false,
label: null,
multi: false,
onChange: () => {},
};
export default class SelectControl extends React.PureComponent {
constructor(props) {
super(props);
this.state = { options: this.getOptions(props) };
this.onChange = this.onChange.bind(this);
this.renderOption = this.renderOption.bind(this);
}
componentWillReceiveProps(nextProps) {
if (nextProps.choices !== this.props.choices) {
const options = this.getOptions(nextProps);
this.setState({ options });
}
}
onChange(opt) {
let optionValue = opt ? opt.value : null;
// if multi, return options values as an array
if (this.props.multi) {
optionValue = opt ? opt.map((o) => o.value) : null;
}
this.props.onChange(optionValue);
}
getOptions(props) {
const options = props.choices.map((c) => {
const label = c.length > 1 ? c[1] : c[0];
const newOptions = {
value: c[0],
label,
};
if (c[2]) newOptions.imgSrc = c[2];
return newOptions;
});
if (props.freeForm) {
// For FreeFormSelect, insert value into options if not exist
const values = props.choices.map((c) => c[0]);
if (props.value) {
if (typeof props.value === 'object') {
props.value.forEach((v) => {
if (values.indexOf(v) === -1) {
options.push({ value: v, label: v });
}
});
} else {
if (values.indexOf(props.value) === -1) {
options.push({ value: props.value, label: props.value });
}
}
}
}
return options;
}
renderOption(opt) {
if (opt.imgSrc) {
return (
<div>
<img className="viz-thumb-option" src={opt.imgSrc} alt={opt.value} />
<span>{opt.label}</span>
</div>
);
}
return opt.label;
}
render() {
// Tab, comma or Enter will trigger a new option created for FreeFormSelect
const selectProps = {
multi: this.props.multi,
name: `select-${this.props.name}`,
placeholder: `Select (${this.state.options.length})`,
options: this.state.options,
value: this.props.value,
autosize: false,
clearable: this.props.clearable,
isLoading: this.props.isLoading,
onChange: this.onChange,
optionRenderer: this.renderOption,
};
// Tab, comma or Enter will trigger a new option created for FreeFormSelect
const selectWrap = this.props.freeForm ?
(<Creatable {...selectProps} />) : (<Select {...selectProps} />);
return (
<div>
{selectWrap}
</div>
);
}
}
SelectControl.propTypes = propTypes;
SelectControl.defaultProps = defaultProps;

View File

@@ -0,0 +1,38 @@
import React, { PropTypes } from 'react';
import { FormGroup, FormControl } from 'react-bootstrap';
const propTypes = {
name: PropTypes.string.isRequired,
label: PropTypes.string,
description: PropTypes.string,
onChange: PropTypes.func,
value: PropTypes.string,
};
const defaultProps = {
label: null,
description: null,
onChange: () => {},
value: '',
};
export default class TextAreaControl extends React.Component {
onChange(event) {
this.props.onChange(event.target.value);
}
render() {
return (
<FormGroup controlId="formControlsTextarea">
<FormControl
componentClass="textarea"
placeholder="textarea"
onChange={this.onChange.bind(this)}
value={this.props.value}
/>
</FormGroup>
);
}
}
TextAreaControl.propTypes = propTypes;
TextAreaControl.defaultProps = defaultProps;

View File

@@ -0,0 +1,73 @@
import React, { PropTypes } from 'react';
import { FormGroup, FormControl } from 'react-bootstrap';
import * as v from '../../validators';
const propTypes = {
name: PropTypes.string.isRequired,
label: PropTypes.string,
description: PropTypes.string,
onChange: PropTypes.func,
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]),
isFloat: PropTypes.bool,
isInt: PropTypes.bool,
};
const defaultProps = {
label: null,
description: null,
onChange: () => {},
value: '',
isInt: false,
isFloat: false,
};
export default class TextControl extends React.Component {
constructor(props) {
super(props);
const value = props.value ? props.value.toString() : '';
this.state = { value };
this.onChange = this.onChange.bind(this);
}
onChange(event) {
let value = event.target.value || '';
this.setState({ value });
// Validation & casting
const errors = [];
if (this.props.isFloat) {
const error = v.numeric(value);
if (error) {
errors.push(error);
} else {
value = parseFloat(value);
}
}
if (this.props.isInt) {
const error = v.integer(value);
if (error) {
errors.push(error);
} else {
value = parseInt(value, 10);
}
}
this.props.onChange(value, errors);
}
render() {
return (
<FormGroup controlId="formInlineName" bsSize="small">
<FormControl
type="text"
placeholder=""
onChange={this.onChange}
value={this.state.value}
/>
</FormGroup>
);
}
}
TextControl.propTypes = propTypes;
TextControl.defaultProps = defaultProps;