From 2133056c04d20807ea0c503d0fed235ee20e94bb Mon Sep 17 00:00:00 2001 From: vera-liu Date: Sun, 13 Nov 2016 14:11:52 -0800 Subject: [PATCH] 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 --- .../explorev2/components/FieldSet.jsx | 21 ++++---- .../explorev2/components/SelectField.jsx | 52 ++++++++++++++----- .../javascripts/explorev2/stores/store.js | 6 +-- .../explorev2/components/SelectField_spec.js | 15 ++++-- 4 files changed, 62 insertions(+), 32 deletions(-) diff --git a/superset/assets/javascripts/explorev2/components/FieldSet.jsx b/superset/assets/javascripts/explorev2/components/FieldSet.jsx index 0e9dea2fe8..03b6ded919 100644 --- a/superset/assets/javascripts/explorev2/components/FieldSet.jsx +++ b/superset/assets/javascripts/explorev2/components/FieldSet.jsx @@ -14,7 +14,7 @@ const propTypes = { places: PropTypes.number, validators: PropTypes.any, 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 = { @@ -40,10 +40,11 @@ export default class FieldSet extends React.Component { />); } - renderSelectField() { + renderSelectField(selectProps) { return ( ); } @@ -56,18 +57,18 @@ export default class FieldSet extends React.Component { render() { const type = this.props.type; - const selectTypes = [ - 'SelectField', - 'SelectCustomMultiField', - 'SelectMultipleSortableField', - 'FreeFormSelectField', - ]; + const selectProps = { + SelectCustomMultiField: { multi: true, freeForm: true }, + SelectMultipleSortableField: { multi: true, freeForm: false }, + SelectField: { multi: false, freeForm: false }, + FreeFormSelectField: { multi: false, freeForm: true }, + }; let field; if (type === 'CheckboxField') { field = this.renderCheckBoxField(); - } else if (selectTypes.includes(type)) { - field = this.renderSelectField(); + } else if (Object.keys(selectProps).includes(type)) { + field = this.renderSelectField(selectProps[type]); } else if (['TextField', 'IntegerField'].includes(type)) { field = this.renderTextField(); } else if (type === 'TextAreaField') { diff --git a/superset/assets/javascripts/explorev2/components/SelectField.jsx b/superset/assets/javascripts/explorev2/components/SelectField.jsx index 7310896748..aa9c809ab5 100644 --- a/superset/assets/javascripts/explorev2/components/SelectField.jsx +++ b/superset/assets/javascripts/explorev2/components/SelectField.jsx @@ -1,18 +1,23 @@ import React, { PropTypes } from 'react'; -import { FormGroup, FormControl } from 'react-bootstrap'; import ControlLabelWithTooltip from './ControlLabelWithTooltip'; import { slugify } from '../../modules/utils'; +import Select, { Creatable } from 'react-select'; + const propTypes = { name: PropTypes.string.isRequired, choices: PropTypes.array, - value: PropTypes.string, + value: PropTypes.oneOfType([PropTypes.string, PropTypes.array]), label: PropTypes.string, description: PropTypes.string, onChange: PropTypes.func, + multi: PropTypes.bool, + freeForm: PropTypes.bool, }; const defaultProps = { + multi: false, + freeForm: false, value: '', label: null, description: null, @@ -21,25 +26,44 @@ const defaultProps = { export default class SelectField extends React.Component { 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() { + 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 ? + () : (