const bindAll = require('lodash.bindall'); const classNames = require('classnames'); const React = require('react'); const PropTypes = require('prop-types'); import {Formik} from 'formik'; const {injectIntl, intlShape} = require('react-intl'); const validate = require('../../lib/validate'); const FormikInput = require('../../components/formik-forms/formik-input.jsx'); const FormikCheckbox = require('../../components/formik-forms/formik-checkbox.jsx'); const JoinFlowStep = require('./join-flow-step.jsx'); require('./join-flow-steps.scss'); /* * Username step */ /* eslint-disable react/prefer-stateless-function, no-useless-constructor */ class UsernameStep extends React.Component { constructor (props) { super(props); bindAll(this, [ 'handleChangeShowPassword', 'handleFocused', 'handleSetUsernameRef', 'handleValidSubmit', 'validatePasswordIfPresent', 'validatePasswordConfirmIfPresent', 'validateUsernameIfPresent', 'validateForm' ]); this.state = { focused: null, showPassword: false }; } componentDidMount () { // automatically start with focus on username field if (this.usernameInput) this.usernameInput.focus(); } handleChangeShowPassword () { this.setState({showPassword: !this.state.showPassword}); } // track the currently focused input field, to determine whether each field should // display a tooltip. (We only display it if a field is focused and has never been touched.) handleFocused (fieldName) { this.setState({focused: fieldName}); } handleSetUsernameRef (usernameInputRef) { this.usernameInput = usernameInputRef; } // we allow username to be empty on blur, since you might not have typed anything yet validateUsernameIfPresent (username) { if (!username) return null; // skip validation if username is blank; null indicates valid // if username is not blank, run both local and remote validations const localResult = validate.validateUsernameLocally(username); return validate.validateUsernameRemotely(username).then( remoteResult => { // there may be multiple validation errors. Prioritize vulgarity, then // length, then having invalid chars, then all other remote reports if (remoteResult.valid === false && remoteResult.errMsgId === 'registration.validationUsernameVulgar') { return this.props.intl.formatMessage({id: remoteResult.errMsgId}); } else if (localResult.valid === false) { return this.props.intl.formatMessage({id: localResult.errMsgId}); } else if (remoteResult.valid === false) { return this.props.intl.formatMessage({id: remoteResult.errMsgId}); } return null; } ); } validatePasswordIfPresent (password, username) { if (!password) return null; // skip validation if password is blank; null indicates valid const localResult = validate.validatePassword(password, username); if (localResult.valid) return null; return this.props.intl.formatMessage({id: localResult.errMsgId}); } validatePasswordConfirmIfPresent (password, passwordConfirm) { if (!passwordConfirm) return null; // allow blank password if not submitting yet const localResult = validate.validatePasswordConfirm(password, passwordConfirm); if (localResult.valid) return null; return this.props.intl.formatMessage({id: localResult.errMsgId}); } // called asynchonously when form submit is initially requested, // along with all of the individual field validation functions validateForm (values) { // in addition to field-level username/password validations, we need to additionally // check that these values aren't blank. const errors = {}; const usernameResult = validate.validateUsernameLocally(values.username); if (!usernameResult.valid) { errors.username = this.props.intl.formatMessage({id: usernameResult.errMsgId}); } const passwordResult = validate.validatePassword(values.password, values.username); if (!passwordResult.valid) { errors.password = this.props.intl.formatMessage({id: passwordResult.errMsgId}); } const passwordConfirmResult = validate.validatePasswordConfirm(values.password, values.passwordConfirm); if (!passwordConfirmResult.valid) { errors.passwordConfirm = this.props.intl.formatMessage({id: passwordConfirmResult.errMsgId}); } return errors; } // called after all validations pass with no errors handleValidSubmit (formData, formikBag) { formikBag.setSubmitting(false); // formik makes us do this ourselves delete formData.showPassword; this.props.onNextStep(formData); } render () { return ( {props => { const { errors, handleSubmit, isSubmitting, setFieldError, setFieldTouched, setFieldValue, touched, validateField, values } = props; return (
{this.props.intl.formatMessage({id: 'registration.createUsername'})}
validateField('username')} onChange={e => { setFieldValue('username', e.target.value); setFieldTouched('username'); setFieldError('username', null); }} onFocus={() => this.handleFocused('username')} /* eslint-enable react/jsx-no-bind */ onSetRef={this.handleSetUsernameRef} />
{this.props.intl.formatMessage({id: 'registration.choosePasswordStepTitle'})}
0} )} error={errors.password} id="password" name="password" placeholder={this.props.intl.formatMessage({id: 'general.password'})} toolTip={this.state.focused === 'password' && !touched.password && this.props.intl.formatMessage({id: 'registration.passwordAdviceShort'})} type={values.showPassword ? 'text' : 'password'} /* eslint-disable react/jsx-no-bind */ validate={password => this.validatePasswordIfPresent(password, values.username)} validationClassName="validation-full-width-input" onBlur={() => validateField('password')} onChange={e => { setFieldValue('password', e.target.value); setFieldTouched('password'); setFieldError('password', null); }} onFocus={() => this.handleFocused('password')} /* eslint-enable react/jsx-no-bind */ /> 0, 'fail': errors.passwordConfirm } )} error={errors.passwordConfirm} id="passwordConfirm" name="passwordConfirm" placeholder={this.props.intl.formatMessage({ id: 'registration.confirmPasswordInstruction' })} toolTip={ this.state.focused === 'passwordConfirm' && !touched.passwordConfirm && this.props.intl.formatMessage({ id: 'registration.confirmPasswordInstruction' }) } type={values.showPassword ? 'text' : 'password'} /* eslint-disable react/jsx-no-bind */ validate={() => this.validatePasswordConfirmIfPresent(values.password, values.passwordConfirm) } validationClassName="validation-full-width-input" onBlur={() => validateField('passwordConfirm')} onChange={e => { setFieldValue('passwordConfirm', e.target.value); setFieldTouched('passwordConfirm'); setFieldError('passwordConfirm', null); }} onFocus={() => this.handleFocused('passwordConfirm')} /* eslint-enable react/jsx-no-bind */ />
); }}
); } } UsernameStep.propTypes = { intl: intlShape, onNextStep: PropTypes.func }; const IntlUsernameStep = injectIntl(UsernameStep); module.exports = IntlUsernameStep;