Created
December 21, 2017 12:06
-
-
Save rstacruz/da1bb0be616065660732abf106ba723d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* @flow */ | |
/*:: | |
import type {Error, Model} from './Validate' | |
type Props = { | |
input: (Model) => React.Element<any> | |
} | |
*/ | |
import React from 'react' | |
import Validate from './Validate' | |
/* | |
* A required field. | |
* Basically a preset of <Validate /> with error display. | |
* | |
* <RequiredField input={({ ref, onChange, errors }) => | |
* <input type='text' name='email' {...{ref, onChange}} /> | |
* } /> | |
*/ | |
function RequiredField ({ input } /*: Props */) { | |
return <Validate rules={{ required: true }} input={m => | |
<span className='field'> | |
{input(m)} | |
<FieldError errors={m.errors} /> | |
</span> | |
} /> | |
} | |
function FieldError ({ errors } /*: { errors: ?Array<Error> } */) { | |
if (errors == null) return <noscript /> | |
const messages = errors.map(e => e.message) | |
return <div className='error-message'>{messages.join(', ')}</div> | |
} | |
export default RequiredField |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* @flow */ | |
/*:: | |
export type Error = { | |
error: string, | |
message: string | |
} | |
export type Model = { | |
validate: (any) => void, | |
onChange: (any) => void, | |
ref: (InputElement) => void, | |
errors: ?Array<Error> | |
} | |
export type Rules = { | |
required?: boolean | |
} | |
export type Props = { | |
input: (Model) => React.Element<any>, | |
rules: Rules | |
} | |
export type State = { | |
lastValue?: any, | |
errors: ?Array<Error> | |
} | |
export type InputElement = HTMLInputElement | HTMLSelectElement | |
*/ | |
import React from 'react' | |
/** | |
* Validates an input. | |
* | |
* @example | |
* <Validate | |
* rules={{required: true}} | |
* input={({ events, errors }) => | |
* <input type='text' name='email' {...events} /> | |
* { errors ? <span>Error: {errors.map(e => e.message).join(',' )}</span> : null } | |
* } /> | |
*/ | |
class Validate extends React.Component { | |
/*:: | |
state: State | |
props: Props | |
model: Model | |
el: InputElement | |
*/ | |
constructor () { | |
super() | |
this.state = { errors: null } | |
// Details to be passed onto input() | |
this.model = { | |
errors: null, | |
validate: this.validate.bind(this), | |
onChange: this.onChange.bind(this), | |
ref: this.onRef.bind(this) | |
} | |
} | |
componentDidMount () { | |
// Run the validation on the actual element value. This would account for | |
// updates that aren't initiated by the app nor the user, eg, browser | |
// autofill. | |
this.validate(this.el.value) | |
} | |
componentDidUpdate () { | |
// Run the validation when the input is changed programatically (eg, when | |
// updating the `value` prop from a parent component). | |
this.validate(this.el.value) | |
} | |
render () { | |
const { model } = this | |
const { input } = this.props | |
const { errors } = this.state | |
return input({ ...model, errors }) | |
} | |
onRef (el /*: InputElement */) { | |
this.el = el | |
} | |
onChange (event /*: SyntheticInputEvent */) { | |
// For some reason, Enzyme doesn't get to keep track of `el` properly, | |
// this "resyncs" this.el to the element we need | |
this.el = event.target | |
this.validate(event.target.value) | |
} | |
validate (value /*: any */) { | |
// Don't re-run validation if input hasn't changed. | |
const { lastValue } = this.state | |
if (lastValue === value) return | |
const errors = this.getErrors(value) | |
this.setState({ errors, lastValue: value }) | |
} | |
getErrors (value /*: any */) /*: ?Array<Error> */ { | |
const { rules } = this.props | |
if (rules.required) { | |
if (!value || value.trim().length === 0) { | |
return [{ error: 'required', message: 'Required' }] | |
} | |
} | |
return null | |
} | |
} | |
export default Validate |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment