diff --git a/superset/assets/javascripts/explorev2/components/FieldSet.jsx b/superset/assets/javascripts/explorev2/components/FieldSet.jsx index 16c10a240f18..cf02acfcbb22 100644 --- a/superset/assets/javascripts/explorev2/components/FieldSet.jsx +++ b/superset/assets/javascripts/explorev2/components/FieldSet.jsx @@ -42,8 +42,11 @@ export default class FieldSet extends React.PureComponent { this.validate = this.validate.bind(this); this.onChange = this.onChange.bind(this); } - onChange(value) { - const validationErrors = this.validate(value); + onChange(value, errors) { + let validationErrors = this.validate(value); + if (errors && errors.length > 0) { + validationErrors = validationErrors.concat(errors); + } this.props.actions.setFieldValue(this.props.name, value, validationErrors); } validate(value) { diff --git a/superset/assets/javascripts/explorev2/components/TextField.jsx b/superset/assets/javascripts/explorev2/components/TextField.jsx index 04f559f6f43c..d1b7e943465a 100644 --- a/superset/assets/javascripts/explorev2/components/TextField.jsx +++ b/superset/assets/javascripts/explorev2/components/TextField.jsx @@ -1,12 +1,18 @@ 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.string, + value: PropTypes.oneOf([ + PropTypes.string, + PropTypes.number, + ]), + isFloat: PropTypes.bool, + isInt: PropTypes.bool, }; const defaultProps = { @@ -14,21 +20,49 @@ const defaultProps = { description: null, onChange: () => {}, value: '', + isInt: false, + isFloat: false, }; export default class TextField 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) { - this.props.onChange(event.target.value); + 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() { - const value = this.props.value || ''; return ( ); diff --git a/superset/assets/javascripts/explorev2/stores/fields.js b/superset/assets/javascripts/explorev2/stores/fields.js index 744b70e3279c..b447a98b6a9a 100644 --- a/superset/assets/javascripts/explorev2/stores/fields.js +++ b/superset/assets/javascripts/explorev2/stores/fields.js @@ -514,7 +514,7 @@ export const fields = { treemap_ratio: { type: 'TextField', label: 'Ratio', - validators: [v.numeric], + isFloat: true, default: 0.5 * (1 + Math.sqrt(5)), // d3 default, golden ratio description: 'Target aspect ratio for treemap tiles.', }, @@ -567,7 +567,7 @@ export const fields = { rolling_periods: { type: 'TextField', label: 'Periods', - validators: [v.integer], + isInt: true, description: 'Defines the size of the rolling window function, ' + 'relative to the time granularity selected', }, @@ -666,7 +666,7 @@ export const fields = { compare_lag: { type: 'TextField', label: 'Comparison Period Lag', - validators: [v.integer], + isInt: true, description: 'Based on granularity, number of time periods to compare against', }, @@ -793,7 +793,7 @@ export const fields = { size_from: { type: 'TextField', - validators: [v.integer], + isInt: true, label: 'Font Size From', default: '20', description: 'Font size for the smallest value in the list', @@ -801,7 +801,7 @@ export const fields = { size_to: { type: 'TextField', - validators: [v.integer], + isInt: true, label: 'Font Size To', default: '150', description: 'Font size for the biggest value in the list', @@ -917,7 +917,7 @@ export const fields = { type: 'TextField', label: 'Period Ratio', default: '', - validators: [v.integer], + isInt: true, description: '[integer] Number of period to compare against, ' + 'this is relative to the granularity selected', }, @@ -934,7 +934,7 @@ export const fields = { time_compare: { type: 'TextField', label: 'Time Shift', - validators: [v.integer], + isInt: true, default: null, description: 'Overlay a timeseries from a ' + 'relative time period. Expects relative time delta ' + @@ -1022,7 +1022,7 @@ export const fields = { type: 'TextField', label: 'Opacity', default: 1, - validators: [v.numeric], + isFloat: true, description: 'Opacity of all clusters, points, and labels. ' + 'Between 0 and 1.', }, @@ -1030,7 +1030,7 @@ export const fields = { viewport_zoom: { type: 'TextField', label: 'Zoom', - validators: [v.numeric], + isFloat: true, default: 11, description: 'Zoom level of the map', places: 8, @@ -1040,7 +1040,7 @@ export const fields = { type: 'TextField', label: 'Default latitude', default: 37.772123, - validators: [v.numeric], + isFloat: true, description: 'Latitude of default viewport', places: 8, }, @@ -1049,7 +1049,7 @@ export const fields = { type: 'TextField', label: 'Default longitude', default: -122.405293, - validators: [v.numeric], + isFloat: true, description: 'Longitude of default viewport', places: 8, },