From 0be655ff282d4752a9520df8ecb68b7fe8af44a1 Mon Sep 17 00:00:00 2001 From: E Liffft Date: Fri, 16 Dec 2016 14:58:29 -0800 Subject: [PATCH 01/22] added onBlur event --- src/components/Form.js | 88 +++++++++++----------- src/components/fields/StringField.js | 5 +- src/components/widgets/AltDateWidget.js | 7 ++ src/components/widgets/BaseInput.js | 5 +- src/components/widgets/CheckboxWidget.js | 5 +- src/components/widgets/CheckboxesWidget.js | 11 ++- src/components/widgets/DateTimeWidget.js | 6 +- src/components/widgets/DateWidget.js | 5 +- src/components/widgets/FileWidget.js | 7 ++ src/components/widgets/RadioWidget.js | 7 +- src/components/widgets/SelectWidget.js | 7 +- src/components/widgets/TextareaWidget.js | 7 +- src/index.js | 2 +- test/Form_test.js | 28 +++++++ 14 files changed, 131 insertions(+), 59 deletions(-) diff --git a/src/components/Form.js b/src/components/Form.js index 32819cc75b..8af2d48cff 100644 --- a/src/components/Form.js +++ b/src/components/Form.js @@ -1,15 +1,7 @@ -import React, {Component, PropTypes} from "react"; - -import ErrorList from "./ErrorList"; -import { - getDefaultFormState, - shouldRender, - toIdSchema, - setState, - getDefaultRegistry, -} from "../utils"; -import validateFormData from "../validate"; - +import React, { Component, PropTypes } from 'react'; +import ErrorList from './ErrorList'; +import { getDefaultFormState, shouldRender, toIdSchema, setState, getDefaultRegistry } from '../utils'; +import validateFormData from '../validate'; export default class Form extends Component { static defaultProps = { @@ -35,13 +27,13 @@ export default class Form extends Component { const edit = typeof props.formData !== "undefined"; const liveValidate = props.liveValidate || this.props.liveValidate; const mustValidate = edit && !props.noValidate && liveValidate; - const {definitions} = schema; + const { definitions } = schema; const formData = getDefaultFormState(schema, props.formData, definitions); - const {errors, errorSchema} = mustValidate ? + const { errors, errorSchema } = mustValidate ? this.validate(formData, schema) : { - errors: state.errors || [], - errorSchema: state.errorSchema || {} - }; + errors: state.errors || [], + errorSchema: state.errorSchema || {} + }; const idSchema = toIdSchema(schema, uiSchema["ui:rootFieldId"], definitions); return { status: "initial", @@ -60,26 +52,26 @@ export default class Form extends Component { } validate(formData, schema) { - const {validate} = this.props; + const { validate } = this.props; return validateFormData(formData, schema || this.props.schema, validate); } renderErrors() { - const {status, errors} = this.state; - const {showErrorList} = this.props; + const { status, errors } = this.state; + const { showErrorList } = this.props; if (status !== "editing" && errors.length && showErrorList != false) { - return ; + return ; } return null; } - onChange = (formData, options={validate: false}) => { + onChange = (formData, options = { validate: false }) => { const mustValidate = !this.props.noValidate && (this.props.liveValidate || options.validate); - let state = {status: "editing", formData}; + let state = { status: "editing", formData }; if (mustValidate) { - const {errors, errorSchema} = this.validate(formData); - state = {...state, errors, errorSchema}; + const { errors, errorSchema } = this.validate(formData); + state = { ...state, errors, errorSchema }; } setState(this, state, () => { if (this.props.onChange) { @@ -88,14 +80,22 @@ export default class Form extends Component { }); }; + onBlur = () => { + setState(this, state, () => { + if (this.props.onChange) { + this.props.onBlur(this.state); + } + }) + } + onSubmit = (event) => { event.preventDefault(); - this.setState({status: "submitted"}); + this.setState({ status: "submitted" }); if (!this.props.noValidate) { - const {errors, errorSchema} = this.validate(this.state.formData); + const { errors, errorSchema } = this.validate(this.state.formData); if (Object.keys(errors).length > 0) { - setState(this, {errors, errorSchema}, () => { + setState(this, { errors, errorSchema }, () => { if (this.props.onError) { this.props.onError(errors); } else { @@ -109,16 +109,16 @@ export default class Form extends Component { if (this.props.onSubmit) { this.props.onSubmit(this.state); } - this.setState({status: "initial", errors: [], errorSchema: {}}); + this.setState({ status: "initial", errors: [], errorSchema: {} }); }; getRegistry() { // For BC, accept passed SchemaField and TitleField props and pass them to // the "fields" registry one. - const {fields, widgets} = getDefaultRegistry(); + const { fields, widgets } = getDefaultRegistry(); return { - fields: {...fields, ...this.props.fields}, - widgets: {...widgets, ...this.props.widgets}, + fields: { ...fields, ...this.props.fields }, + widgets: { ...widgets, ...this.props.widgets }, FieldTemplate: this.props.FieldTemplate, definitions: this.props.schema.definitions || {}, formContext: this.props.formContext || {}, @@ -140,21 +140,21 @@ export default class Form extends Component { acceptcharset } = this.props; - const {schema, uiSchema, formData, errorSchema, idSchema} = this.state; + const { schema, uiSchema, formData, errorSchema, idSchema } = this.state; const registry = this.getRegistry(); const _SchemaField = registry.fields.SchemaField; return (
+ id={id} + name={name} + method={method} + target={target} + action={action} + autoComplete={autocomplete} + encType={enctype} + acceptCharset={acceptcharset} + onSubmit={this.onSubmit}> {this.renderErrors()} <_SchemaField schema={schema} @@ -163,8 +163,9 @@ export default class Form extends Component { idSchema={idSchema} formData={formData} onChange={this.onChange} + onBlur={this.onBlur} registry={registry} - safeRenderCompletion={safeRenderCompletion}/> + safeRenderCompletion={safeRenderCompletion} /> { children ? children :

@@ -186,6 +187,7 @@ if (process.env.NODE_ENV !== "production") { fields: PropTypes.objectOf(PropTypes.func), FieldTemplate: PropTypes.func, onChange: PropTypes.func, + onBlur: PropTypes.func, onError: PropTypes.func, showErrorList: PropTypes.bool, onSubmit: PropTypes.func, diff --git a/src/components/fields/StringField.js b/src/components/fields/StringField.js index f1ed3eb0f5..e6f6dbea2b 100644 --- a/src/components/fields/StringField.js +++ b/src/components/fields/StringField.js @@ -21,7 +21,8 @@ function StringField(props) { readonly, autofocus, registry, - onChange + onChange, + onBlur, } = props; const {title, format} = schema; const {widgets, formContext} = registry; @@ -37,6 +38,7 @@ function StringField(props) { label={title === undefined ? name : title} value={defaultFieldValue(formData, schema)} onChange={onChange} + onBlur={onBlur} required={required} disabled={disabled} readonly={readonly} @@ -52,6 +54,7 @@ if (process.env.NODE_ENV !== "production") { uiSchema: PropTypes.object.isRequired, idSchema: PropTypes.object, onChange: PropTypes.func.isRequired, + onBlur: PropTypes.func, formData: PropTypes.oneOfType([ React.PropTypes.string, React.PropTypes.number, diff --git a/src/components/widgets/AltDateWidget.js b/src/components/widgets/AltDateWidget.js index e228b71321..4ee61a79a6 100644 --- a/src/components/widgets/AltDateWidget.js +++ b/src/components/widgets/AltDateWidget.js @@ -63,6 +63,12 @@ class AltDateWidget extends Component { }); }; + onBlur = () => { + if(this.props.onBlur){ + this.props.onBlur(toDateString(this.state, this.props.time)); + } + }; + setNow = (event) => { event.preventDefault(); const {time, disabled, readonly, onChange} = this.props; @@ -140,6 +146,7 @@ if (process.env.NODE_ENV !== "production") { readonly: PropTypes.bool, autofocus: PropTypes.bool, onChange: PropTypes.func, + onBlur: PropTypes.func, time: PropTypes.bool, }; } diff --git a/src/components/widgets/BaseInput.js b/src/components/widgets/BaseInput.js index 4710a0c6ef..365211f27e 100644 --- a/src/components/widgets/BaseInput.js +++ b/src/components/widgets/BaseInput.js @@ -9,6 +9,7 @@ function BaseInput(props) { readonly, autofocus, onChange, + onBlur, options, // eslint-disable-line schema, // eslint-disable-line formContext, // eslint-disable-line @@ -22,7 +23,8 @@ function BaseInput(props) { readOnly={readonly} autoFocus={autofocus} value={typeof value === "undefined" ? "" : value} - onChange={(event) => onChange(event.target.value)}/> + onChange={(event) => onChange(event.target.value)} + onBlur={(event) => onBlur(event.target.value)}/> ); } @@ -44,6 +46,7 @@ if (process.env.NODE_ENV !== "production") { readonly: PropTypes.bool, autofocus: PropTypes.bool, onChange: PropTypes.func, + onBlur: PropTypes.func, }; } diff --git a/src/components/widgets/CheckboxWidget.js b/src/components/widgets/CheckboxWidget.js index d65c663559..51e81ddafa 100644 --- a/src/components/widgets/CheckboxWidget.js +++ b/src/components/widgets/CheckboxWidget.js @@ -10,6 +10,7 @@ function CheckboxWidget({ label, autofocus, onChange, + onBlur }) { return (

@@ -20,7 +21,8 @@ function CheckboxWidget({ required={required} disabled={disabled} autoFocus={autofocus} - onChange={(event) => onChange(event.target.checked)}/> + onChange={(event) => onChange(event.target.checked)} + onBlur={(event) => onBlur(event.target.checked)}/> {label}
@@ -39,6 +41,7 @@ if (process.env.NODE_ENV !== "production") { required: PropTypes.bool, autofocus: PropTypes.bool, onChange: PropTypes.func, + onBlur: PropTypes.func, }; } diff --git a/src/components/widgets/CheckboxesWidget.js b/src/components/widgets/CheckboxesWidget.js index d6471a409d..a5a9b5801f 100644 --- a/src/components/widgets/CheckboxesWidget.js +++ b/src/components/widgets/CheckboxesWidget.js @@ -14,7 +14,7 @@ function deselectValue(value, selected) { } function CheckboxesWidget(props) { - const {id, disabled, options, value, autofocus, onChange} = props; + const {id, disabled, options, value, autofocus, onChange, onBlur} = props; const {enumOptions, inline} = options; return (
{ @@ -35,7 +35,13 @@ function CheckboxesWidget(props) { } else { onChange(deselectValue(option.value, value)); } - }}/> + }} + onBlur={(event) => { + if(onBlur){ + this.props.onBlur(event); + } + }} + /> {option.label} ); @@ -76,6 +82,7 @@ if (process.env.NODE_ENV !== "production") { multiple: PropTypes.bool, autofocus: PropTypes.bool, onChange: PropTypes.func, + onBlur: PropTypes.func, }; } diff --git a/src/components/widgets/DateTimeWidget.js b/src/components/widgets/DateTimeWidget.js index 9f4ca1f45e..53cd145c12 100644 --- a/src/components/widgets/DateTimeWidget.js +++ b/src/components/widgets/DateTimeWidget.js @@ -14,13 +14,15 @@ function toJSONDate(dateString) { } function DateTimeWidget(props) { - const {value, onChange} = props; + const {value, onChange, onBlur} = props; return ( onChange(toJSONDate(value))}/> + onChange={(value) => onChange(toJSONDate(value))} + onBlur={(value) => onBlur(toJSONDate(value))} + /> ); } diff --git a/src/components/widgets/DateWidget.js b/src/components/widgets/DateWidget.js index 89b856e7a1..df4ad34485 100644 --- a/src/components/widgets/DateWidget.js +++ b/src/components/widgets/DateWidget.js @@ -4,12 +4,13 @@ import BaseInput from "./BaseInput"; function DateWidget(props) { - const {onChange} = props; + const {onChange, onBlur} = props; return ( onChange(value || undefined)}/> + onChange={(value) => onChange(value || undefined)} + onBlur={(value) => onBlur(value || undefined)}/> ); } diff --git a/src/components/widgets/FileWidget.js b/src/components/widgets/FileWidget.js index db53c88d1b..d1b585d126 100644 --- a/src/components/widgets/FileWidget.js +++ b/src/components/widgets/FileWidget.js @@ -93,6 +93,12 @@ class FileWidget extends Component { }); }; + onBlur = (event) => { + if(this.props.onBlur){ + this.props.onBlur(event); + } + }; + render() { const {multiple, id, readonly, disabled, autofocus} = this.props; const {filesInfo} = this.state; @@ -105,6 +111,7 @@ class FileWidget extends Component { type="file" disabled={readonly || disabled} onChange={this.onChange} + onBlur={this.onBlur} defaultValue="" autoFocus={autofocus} multiple={multiple}/> diff --git a/src/components/widgets/RadioWidget.js b/src/components/widgets/RadioWidget.js index 717bcd4e81..06d6c3e894 100644 --- a/src/components/widgets/RadioWidget.js +++ b/src/components/widgets/RadioWidget.js @@ -8,7 +8,8 @@ function RadioWidget({ required, disabled, autofocus, - onChange + onChange, + onBlur }) { // Generating a unique field name to identify this set of radio buttons const name = Math.random().toString(); @@ -28,7 +29,8 @@ function RadioWidget({ value={option.value} disabled={disabled} autoFocus={autofocus && i === 0} - onChange={_ => onChange(option.value)}/> + onChange={_ => onChange(option.value)} + onBlur={_ => onBlur(option.value)}/> {option.label} ); @@ -65,6 +67,7 @@ if (process.env.NODE_ENV !== "production") { required: PropTypes.bool, autofocus: PropTypes.bool, onChange: PropTypes.func, + onBlur: PropTypes.func, }; } export default RadioWidget; diff --git a/src/components/widgets/SelectWidget.js b/src/components/widgets/SelectWidget.js index 32f0545d5f..71a93a0c74 100644 --- a/src/components/widgets/SelectWidget.js +++ b/src/components/widgets/SelectWidget.js @@ -28,7 +28,8 @@ function SelectWidget({ readonly, multiple, autofocus, - onChange + onChange, + onBlur }) { const {enumOptions} = options; return ( @@ -50,7 +51,8 @@ function SelectWidget({ newValue = event.target.value; } onChange(processValue(schema, newValue)); - }}>{ + }} + onBlur={(event)=> onBlur(event)}>{ enumOptions.map(({value, label}, i) => { return ; }) @@ -74,6 +76,7 @@ if (process.env.NODE_ENV !== "production") { multiple: PropTypes.bool, autofocus: PropTypes.bool, onChange: PropTypes.func, + onBlur: PropTypes.func, }; } diff --git a/src/components/widgets/TextareaWidget.js b/src/components/widgets/TextareaWidget.js index d7be10236a..affe4cdb07 100644 --- a/src/components/widgets/TextareaWidget.js +++ b/src/components/widgets/TextareaWidget.js @@ -10,7 +10,8 @@ function TextareaWidget({ disabled, readonly, autofocus, - onChange + onChange, + onBlur, }) { return (