mirror of https://github.com/apache/superset.git
Added different Select Fields (#1583)
* Added different Select Fields - Switched FormGroup to react-select - Added multi and freeform to select, now it can take customized user input and insert it as options * Fixed tests * Small nit based on comments
This commit is contained in:
parent
4155a9d7f9
commit
2133056c04
|
@ -14,7 +14,7 @@ const propTypes = {
|
||||||
places: PropTypes.number,
|
places: PropTypes.number,
|
||||||
validators: PropTypes.any,
|
validators: PropTypes.any,
|
||||||
onChange: React.PropTypes.func,
|
onChange: React.PropTypes.func,
|
||||||
value: PropTypes.oneOf([PropTypes.string, PropTypes.bool, PropTypes.array]).isRequired,
|
value: PropTypes.oneOfType([PropTypes.string, PropTypes.bool, PropTypes.array]).isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
|
@ -40,10 +40,11 @@ export default class FieldSet extends React.Component {
|
||||||
/>);
|
/>);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderSelectField() {
|
renderSelectField(selectProps) {
|
||||||
return (
|
return (
|
||||||
<SelectField
|
<SelectField
|
||||||
{...this.props}
|
{...this.props}
|
||||||
|
{...selectProps}
|
||||||
/>);
|
/>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,18 +57,18 @@ export default class FieldSet extends React.Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const type = this.props.type;
|
const type = this.props.type;
|
||||||
const selectTypes = [
|
const selectProps = {
|
||||||
'SelectField',
|
SelectCustomMultiField: { multi: true, freeForm: true },
|
||||||
'SelectCustomMultiField',
|
SelectMultipleSortableField: { multi: true, freeForm: false },
|
||||||
'SelectMultipleSortableField',
|
SelectField: { multi: false, freeForm: false },
|
||||||
'FreeFormSelectField',
|
FreeFormSelectField: { multi: false, freeForm: true },
|
||||||
];
|
};
|
||||||
let field;
|
let field;
|
||||||
|
|
||||||
if (type === 'CheckboxField') {
|
if (type === 'CheckboxField') {
|
||||||
field = this.renderCheckBoxField();
|
field = this.renderCheckBoxField();
|
||||||
} else if (selectTypes.includes(type)) {
|
} else if (Object.keys(selectProps).includes(type)) {
|
||||||
field = this.renderSelectField();
|
field = this.renderSelectField(selectProps[type]);
|
||||||
} else if (['TextField', 'IntegerField'].includes(type)) {
|
} else if (['TextField', 'IntegerField'].includes(type)) {
|
||||||
field = this.renderTextField();
|
field = this.renderTextField();
|
||||||
} else if (type === 'TextAreaField') {
|
} else if (type === 'TextAreaField') {
|
||||||
|
|
|
@ -1,18 +1,23 @@
|
||||||
import React, { PropTypes } from 'react';
|
import React, { PropTypes } from 'react';
|
||||||
import { FormGroup, FormControl } from 'react-bootstrap';
|
|
||||||
import ControlLabelWithTooltip from './ControlLabelWithTooltip';
|
import ControlLabelWithTooltip from './ControlLabelWithTooltip';
|
||||||
import { slugify } from '../../modules/utils';
|
import { slugify } from '../../modules/utils';
|
||||||
|
import Select, { Creatable } from 'react-select';
|
||||||
|
|
||||||
|
|
||||||
const propTypes = {
|
const propTypes = {
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
choices: PropTypes.array,
|
choices: PropTypes.array,
|
||||||
value: PropTypes.string,
|
value: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
|
||||||
label: PropTypes.string,
|
label: PropTypes.string,
|
||||||
description: PropTypes.string,
|
description: PropTypes.string,
|
||||||
onChange: PropTypes.func,
|
onChange: PropTypes.func,
|
||||||
|
multi: PropTypes.bool,
|
||||||
|
freeForm: PropTypes.bool,
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultProps = {
|
const defaultProps = {
|
||||||
|
multi: false,
|
||||||
|
freeForm: false,
|
||||||
value: '',
|
value: '',
|
||||||
label: null,
|
label: null,
|
||||||
description: null,
|
description: null,
|
||||||
|
@ -21,25 +26,44 @@ const defaultProps = {
|
||||||
|
|
||||||
export default class SelectField extends React.Component {
|
export default class SelectField extends React.Component {
|
||||||
onChange(opt) {
|
onChange(opt) {
|
||||||
this.props.onChange(this.props.name, opt.target.value);
|
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(this.props.name, optionValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const options = this.props.choices.map((c) => ({ value: c[0], label: c[1] }));
|
||||||
|
if (this.props.freeForm) {
|
||||||
|
// For FreeFormSelect, insert value into options if not exist
|
||||||
|
const values = this.props.choices.map((c) => c[0]);
|
||||||
|
if (values.indexOf(this.props.value) === -1) {
|
||||||
|
options.push({ value: this.props.value, label: this.props.value });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectProps = {
|
||||||
|
multi: this.props.multi,
|
||||||
|
name: `select-${this.props.name}`,
|
||||||
|
placeholder: `Select (${this.props.choices.length})`,
|
||||||
|
options,
|
||||||
|
value: this.props.value,
|
||||||
|
autosize: false,
|
||||||
|
onChange: this.onChange.bind(this),
|
||||||
|
};
|
||||||
|
// Tab, comma or Enter will trigger a new option created for FreeFormSelect
|
||||||
|
const selectWrap = this.props.freeForm ?
|
||||||
|
(<Creatable {...selectProps} />) : (<Select {...selectProps} />);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormGroup controlId={`formControlsSelect-${slugify(this.props.label)}`}>
|
<div id={`formControlsSelect-${slugify(this.props.label)}`}>
|
||||||
<ControlLabelWithTooltip
|
<ControlLabelWithTooltip
|
||||||
label={this.props.label}
|
label={this.props.label}
|
||||||
description={this.props.description}
|
description={this.props.description}
|
||||||
/>
|
/>
|
||||||
<FormControl
|
{selectWrap}
|
||||||
componentClass="select"
|
</div>
|
||||||
placeholder="select"
|
|
||||||
onChange={this.onChange.bind(this)}
|
|
||||||
value={this.props.value}
|
|
||||||
>
|
|
||||||
{this.props.choices.map((c) => <option key={c[0]} value={c[0]}>{c[1]}</option>)}
|
|
||||||
</FormControl>
|
|
||||||
</FormGroup>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1278,7 +1278,7 @@ export const fields = {
|
||||||
},
|
},
|
||||||
|
|
||||||
series_height: {
|
series_height: {
|
||||||
type: 'SelectCustomMultiField',
|
type: 'FreeFormSelectField',
|
||||||
label: 'Series Height',
|
label: 'Series Height',
|
||||||
default: 25,
|
default: 25,
|
||||||
choices: formatSelectOptions([10, 25, 40, 50, 75, 100, 150, 200]),
|
choices: formatSelectOptions([10, 25, 40, 50, 75, 100, 150, 200]),
|
||||||
|
@ -1286,7 +1286,7 @@ export const fields = {
|
||||||
},
|
},
|
||||||
|
|
||||||
x_axis_format: {
|
x_axis_format: {
|
||||||
type: 'SelectCustomMultiField',
|
type: 'FreeFormSelectField',
|
||||||
label: 'X axis format',
|
label: 'X axis format',
|
||||||
default: 'smart_date',
|
default: 'smart_date',
|
||||||
choices: TIME_STAMP_OPTIONS,
|
choices: TIME_STAMP_OPTIONS,
|
||||||
|
@ -1294,7 +1294,7 @@ export const fields = {
|
||||||
},
|
},
|
||||||
|
|
||||||
y_axis_format: {
|
y_axis_format: {
|
||||||
type: 'SelectCustomMultiField',
|
type: 'FreeFormSelectField',
|
||||||
label: 'Y axis format',
|
label: 'Y axis format',
|
||||||
default: '.3s',
|
default: '.3s',
|
||||||
choices: D3_TIME_FORMAT_OPTIONS,
|
choices: D3_TIME_FORMAT_OPTIONS,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/* eslint-disable no-unused-expressions */
|
/* eslint-disable no-unused-expressions */
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { FormControl } from 'react-bootstrap';
|
import Select, { Creatable } from 'react-select';
|
||||||
import sinon from 'sinon';
|
import sinon from 'sinon';
|
||||||
import { expect } from 'chai';
|
import { expect } from 'chai';
|
||||||
import { describe, it, beforeEach } from 'mocha';
|
import { describe, it, beforeEach } from 'mocha';
|
||||||
|
@ -21,13 +21,18 @@ describe('SelectField', () => {
|
||||||
wrapper = shallow(<SelectField {...defaultProps} />);
|
wrapper = shallow(<SelectField {...defaultProps} />);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders a FormControl', () => {
|
it('renders a Select', () => {
|
||||||
expect(wrapper.find(FormControl)).to.have.lengthOf(1);
|
expect(wrapper.find(Select)).to.have.lengthOf(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('calls onChange when toggled', () => {
|
it('calls onChange when toggled', () => {
|
||||||
const select = wrapper.find(FormControl);
|
const select = wrapper.find(Select);
|
||||||
select.simulate('change', { target: { value: 50 } });
|
select.simulate('change', { value: 50 });
|
||||||
expect(defaultProps.onChange.calledWith('row_limit', 50)).to.be.true;
|
expect(defaultProps.onChange.calledWith('row_limit', 50)).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('renders a Creatable for freeform', () => {
|
||||||
|
wrapper = shallow(<SelectField {...defaultProps} freeForm />);
|
||||||
|
expect(wrapper.find(Creatable)).to.have.lengthOf(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue