#14: reset captcha if there are errors in form

This commit is contained in:
SleepWalker 2016-08-14 13:10:59 +03:00
parent a91e2e99b3
commit 016cccf4c9
5 changed files with 60 additions and 21 deletions

View File

@ -18,10 +18,21 @@ export default class BaseAuthBody extends Component {
payload: PropTypes.object payload: PropTypes.object
})]), })]),
scopes: PropTypes.array scopes: PropTypes.array
}), }).isRequired,
user: userShape user: userShape
}; };
componentWillReceiveProps(nextProps, nextContext) {
// TODO: we must not access Form#fields. This is a temporary
// solution to reset Captcha, when the form does not handle errors
if (nextContext.auth.error
&& this.form.fields.captcha
&& nextContext.auth.error !== this.context.auth.error
) {
this.form.fields.captcha.reset();
}
}
renderErrors() { renderErrors() {
return this.context.auth.error return this.context.auth.error
? <AuthError error={this.context.auth.error} onClose={this.onClearErrors} /> ? <AuthError error={this.context.auth.error} onClose={this.onClearErrors} />

View File

@ -241,12 +241,12 @@ class PanelTransition extends Component {
willLeave = (config) => this.getTransitionStyles(config, {isLeave: true}); willLeave = (config) => this.getTransitionStyles(config, {isLeave: true});
/** /**
* @param {Object} config * @param {object} config
* @param {string} config.key * @param {string} config.key
* @param {Object} [options] * @param {object} [options]
* @param {Object} [options.isLeave=false] - true, if this is a leave transition * @param {object} [options.isLeave=false] - true, if this is a leave transition
* *
* @return {Object} * @return {object}
*/ */
getTransitionStyles({key}, options = {}) { getTransitionStyles({key}, options = {}) {
const {isLeave = false} = options; const {isLeave = false} = options;
@ -337,7 +337,11 @@ class PanelTransition extends Component {
}; };
const backButton = ( const backButton = (
<button style={sideScrollStyle} type="button" onClick={this.onGoBack} className={panelStyles.headerControl}> <button style={sideScrollStyle}
className={panelStyles.headerControl}
type="button"
onClick={this.onGoBack}
>
<span className={icons.arrowLeft} /> <span className={icons.arrowLeft} />
</button> </button>
); );
@ -413,10 +417,10 @@ class PanelTransition extends Component {
/** /**
* @param {string} key * @param {string} key
* @param {Object} style * @param {object} style
* @param {number} style.opacitySpring * @param {number} style.opacitySpring
* *
* @return {Object} * @return {object}
*/ */
getDefaultTransitionStyles(key, {opacitySpring}) { getDefaultTransitionStyles(key, {opacitySpring}) {
return { return {
@ -434,7 +438,7 @@ class PanelTransition extends Component {
* @param {string} direction='X' - X|Y * @param {string} direction='X' - X|Y
* @param {string} unit='%' - %|px etc * @param {string} unit='%' - %|px etc
* *
* @return {Object} * @return {object}
*/ */
translate(value, direction = 'X', unit = '%') { translate(value, direction = 'X', unit = '%') {
return { return {

View File

@ -23,10 +23,14 @@ export default class Captcha extends FormInputComponent {
}; };
componentDidMount() { componentDidMount() {
setTimeout(() => captcha.render(this.el, { setTimeout(() =>
captcha.render(this.el, {
skin: this.props.skin, skin: this.props.skin,
onSetCode: this.setCode onSetCode: this.setCode
}), this.props.delay); })
.then((captchaId) => this.captchaId = captchaId),
this.props.delay
);
} }
render() { render() {
@ -37,10 +41,12 @@ export default class Captcha extends FormInputComponent {
<div className={styles.captchaLoader}> <div className={styles.captchaLoader}>
<ComponentLoader /> <ComponentLoader />
</div> </div>
<div ref={this.setEl} className={classNames( <div ref={this.setEl} className={classNames(
styles.captcha, styles.captcha,
styles[`${skin}Captcha`] styles[`${skin}Captcha`]
)} /> )} />
{this.renderError()} {this.renderError()}
</div> </div>
); );
@ -50,5 +56,15 @@ export default class Captcha extends FormInputComponent {
return this.state && this.state.code; return this.state && this.state.code;
} }
reset() {
captcha.reset(this.captchaId);
}
setError(error) {
super.setError(error);
this.reset();
}
setCode = (code) => this.setState({code}); setCode = (code) => this.setState({code});
} }

View File

@ -12,7 +12,7 @@ export default {
* @param {function} options.onSetCode - the callback, that will be called with * @param {function} options.onSetCode - the callback, that will be called with
* captcha verification code, after user successfully solves captcha * captcha verification code, after user successfully solves captcha
* *
* @return {Promise} * @return {Promise} - resolves to captchaId
*/ */
render(el, {skin: theme, onSetCode: callback}) { render(el, {skin: theme, onSetCode: callback}) {
return this.loadApi().then(() => return this.loadApi().then(() =>
@ -24,6 +24,13 @@ export default {
); );
}, },
/**
* @param {string} captchaId - captcha id, returned from render promise
*/
reset(captchaId) {
this.loadApi().then(() => window.grecaptcha.reset(captchaId));
},
/** /**
* @param {stirng} newLang * @param {stirng} newLang
* *

View File

@ -77,6 +77,7 @@ const errorsMap = {
'error.account_already_activated': () => <Message {...messages.accountAlreadyActivated} />, 'error.account_already_activated': () => <Message {...messages.accountAlreadyActivated} />,
'error.captcha_required': () => <Message {...messages.captchaRequired} />, 'error.captcha_required': () => <Message {...messages.captchaRequired} />,
'error.captcha_invalid': () => errorsMap['error.captcha_required'](),
suggestResetPassword: () => ( suggestResetPassword: () => (
<span> <span>