mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-26 06:47:17 +10:00
Form is responsible to change customErrorText
FieldTextInput: touch flag couldn't be tracked reliably in responses with errors
This commit is contained in:
parent
a7b9a08a86
commit
7c58442aae
5 changed files with 143 additions and 96 deletions
|
|
@ -41,9 +41,9 @@ class FieldTextInputComponent extends Component {
|
|||
|
||||
// Error message and input error styles are only shown if the
|
||||
// field has been touched and the validation has failed.
|
||||
const hasError = touched && invalid && errorText;
|
||||
const hasError = !!customErrorText || !!(touched && invalid && error);
|
||||
|
||||
const fieldMeta = { touched, error: errorText };
|
||||
const fieldMeta = { touched: hasError, error: errorText };
|
||||
|
||||
const inputClasses =
|
||||
inputRootClass ||
|
||||
|
|
@ -116,6 +116,6 @@ class FieldTextInput extends Component {
|
|||
render() {
|
||||
return <Field component={FieldTextInputComponent} {...this.props} />;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default FieldTextInput;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
|||
import { compose } from 'redux';
|
||||
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
|
||||
import { Form as FinalForm } from 'react-final-form';
|
||||
import { isEqual } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import { propTypes } from '../../util/types';
|
||||
import * as validators from '../../util/validators';
|
||||
|
|
@ -24,6 +25,7 @@ class ContactDetailsFormComponent extends Component {
|
|||
this.state = { showVerificationEmailSentMessage: false };
|
||||
this.emailSentTimeoutId = null;
|
||||
this.handleResendVerificationEmail = this.handleResendVerificationEmail.bind(this);
|
||||
this.submittedValues = {};
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
|
@ -54,7 +56,6 @@ class ContactDetailsFormComponent extends Component {
|
|||
currentUser,
|
||||
formId,
|
||||
handleSubmit,
|
||||
submitting,
|
||||
inProgress,
|
||||
intl,
|
||||
invalid,
|
||||
|
|
@ -97,6 +98,7 @@ class ContactDetailsFormComponent extends Component {
|
|||
sendVerificationEmailError
|
||||
);
|
||||
|
||||
const emailTouched = this.submittedValues.email !== values.email;
|
||||
const emailTakenErrorText = isChangeEmailTakenError(saveEmailError)
|
||||
? intl.formatMessage({ id: 'ContactDetailsForm.emailTakenError' })
|
||||
: null;
|
||||
|
|
@ -221,6 +223,7 @@ class ContactDetailsFormComponent extends Component {
|
|||
const passwordFailedMessage = intl.formatMessage({
|
||||
id: 'ContactDetailsForm.passwordFailed',
|
||||
});
|
||||
const passwordTouched = this.submittedValues.currentPassword !== values.currentPassword;
|
||||
const passwordErrorText = isChangeEmailWrongPassword(saveEmailError)
|
||||
? passwordFailedMessage
|
||||
: null;
|
||||
|
|
@ -255,11 +258,22 @@ class ContactDetailsFormComponent extends Component {
|
|||
}
|
||||
|
||||
const classes = classNames(rootClassName || css.root, className);
|
||||
const submittedOnce = Object.keys(this.submittedValues).length > 0;
|
||||
const pristineSinceLastSubmit = submittedOnce && isEqual(values, this.submittedValues);
|
||||
const submitDisabled =
|
||||
invalid || submitting || inProgress || !(emailChanged || phoneNumberChanged);
|
||||
invalid ||
|
||||
pristineSinceLastSubmit ||
|
||||
inProgress ||
|
||||
!(emailChanged || phoneNumberChanged);
|
||||
|
||||
return (
|
||||
<Form className={classes} onSubmit={handleSubmit}>
|
||||
<Form
|
||||
className={classes}
|
||||
onSubmit={e => {
|
||||
this.submittedValues = values;
|
||||
handleSubmit(e);
|
||||
}}
|
||||
>
|
||||
<div className={css.contactDetailsSection}>
|
||||
<FieldTextInput
|
||||
type="email"
|
||||
|
|
@ -268,7 +282,7 @@ class ContactDetailsFormComponent extends Component {
|
|||
label={emailLabel}
|
||||
placeholder={emailPlaceholder}
|
||||
validate={validators.composeValidators(emailRequired, emailValid)}
|
||||
customErrorText={emailTakenErrorText}
|
||||
customErrorText={emailTouched ? null : emailTakenErrorText}
|
||||
/>
|
||||
{emailVerifiedInfo}
|
||||
<FieldPhoneNumberInput
|
||||
|
|
@ -297,7 +311,7 @@ class ContactDetailsFormComponent extends Component {
|
|||
label={passwordLabel}
|
||||
placeholder={passwordPlaceholder}
|
||||
validate={passwordValidators}
|
||||
customErrorText={passwordErrorText}
|
||||
customErrorText={passwordTouched ? null : passwordErrorText}
|
||||
/>
|
||||
</div>
|
||||
<div className={css.bottomWrapper}>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
|
|||
import { compose } from 'redux';
|
||||
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
|
||||
import { Form as FinalForm } from 'react-final-form';
|
||||
import { isEqual } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import { propTypes } from '../../util/types';
|
||||
import * as validators from '../../util/validators';
|
||||
|
|
@ -18,6 +19,7 @@ class PasswordChangeFormComponent extends Component {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
this.resetTimeoutId = null;
|
||||
this.submittedValues = {};
|
||||
}
|
||||
componentWillUnmount() {
|
||||
window.clearTimeout(this.resetTimeoutId);
|
||||
|
|
@ -34,13 +36,13 @@ class PasswordChangeFormComponent extends Component {
|
|||
changePasswordError,
|
||||
currentUser,
|
||||
handleSubmit,
|
||||
submitting,
|
||||
inProgress,
|
||||
intl,
|
||||
invalid,
|
||||
pristine,
|
||||
ready,
|
||||
reset,
|
||||
form,
|
||||
values,
|
||||
} = fieldRenderProps;
|
||||
|
||||
const user = ensureCurrentUser(currentUser);
|
||||
|
|
@ -103,6 +105,7 @@ class PasswordChangeFormComponent extends Component {
|
|||
const passwordFailedMessage = intl.formatMessage({
|
||||
id: 'PasswordChangeForm.passwordFailed',
|
||||
});
|
||||
const passwordTouched = this.submittedValues.currentPassword !== values.currentPassword;
|
||||
const passwordErrorText = isChangePasswordWrongPassword(changePasswordError)
|
||||
? passwordFailedMessage
|
||||
: null;
|
||||
|
|
@ -118,16 +121,19 @@ class PasswordChangeFormComponent extends Component {
|
|||
</span>
|
||||
) : null;
|
||||
|
||||
const submittedOnce = Object.keys(this.submittedValues).length > 0;
|
||||
const pristineSinceLastSubmit = submittedOnce && isEqual(values, this.submittedValues);
|
||||
const classes = classNames(rootClassName || css.root, className);
|
||||
const submitDisabled = invalid || submitting || inProgress;
|
||||
const submitDisabled = invalid || pristineSinceLastSubmit || inProgress;
|
||||
|
||||
return (
|
||||
<Form
|
||||
className={classes}
|
||||
onSubmit={values => {
|
||||
handleSubmit(values)
|
||||
onSubmit={e => {
|
||||
this.submittedValues = values;
|
||||
handleSubmit(e)
|
||||
.then(() => {
|
||||
this.resetTimeoutId = window.setTimeout(reset, RESET_TIMEOUT);
|
||||
this.resetTimeoutId = window.setTimeout(form.reset, RESET_TIMEOUT);
|
||||
})
|
||||
.catch(() => {
|
||||
// Error is handled in duck file already.
|
||||
|
|
@ -171,7 +177,7 @@ class PasswordChangeFormComponent extends Component {
|
|||
passwordMinLength,
|
||||
passwordMaxLength
|
||||
)}
|
||||
customErrorText={passwordErrorText}
|
||||
customErrorText={passwordTouched ? null : passwordErrorText}
|
||||
/>
|
||||
</div>
|
||||
<div className={css.bottomWrapper}>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import React from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { compose } from 'redux';
|
||||
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
|
||||
import { Form as FinalForm } from 'react-final-form';
|
||||
import { isEqual } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import { propTypes } from '../../util/types';
|
||||
import * as validators from '../../util/validators';
|
||||
|
|
@ -11,90 +12,116 @@ import { Form, PrimaryButton, FieldTextInput, NamedLink } from '../../components
|
|||
|
||||
import css from './PasswordRecoveryForm.css';
|
||||
|
||||
const PasswordRecoveryFormComponent = props => (
|
||||
<FinalForm
|
||||
{...props}
|
||||
render={fieldRenderProps => {
|
||||
const {
|
||||
rootClassName,
|
||||
className,
|
||||
formId,
|
||||
handleSubmit,
|
||||
pristine,
|
||||
submitting,
|
||||
initialValues,
|
||||
intl,
|
||||
inProgress,
|
||||
recoveryError,
|
||||
} = fieldRenderProps;
|
||||
class PasswordRecoveryFormComponent extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.submittedValues = {};
|
||||
}
|
||||
|
||||
// email
|
||||
const emailLabel = intl.formatMessage({
|
||||
id: 'PasswordRecoveryForm.emailLabel',
|
||||
});
|
||||
const emailPlaceholder = intl.formatMessage({
|
||||
id: 'PasswordRecoveryForm.emailPlaceholder',
|
||||
});
|
||||
const emailRequiredMessage = intl.formatMessage({
|
||||
id: 'PasswordRecoveryForm.emailRequired',
|
||||
});
|
||||
const emailNotFoundMessage = intl.formatMessage({
|
||||
id: 'PasswordRecoveryForm.emailNotFound',
|
||||
});
|
||||
const emailInvalidMessage = intl.formatMessage({
|
||||
id: 'PasswordRecoveryForm.emailInvalid',
|
||||
});
|
||||
render() {
|
||||
return (
|
||||
<FinalForm
|
||||
{...this.props}
|
||||
render={fieldRenderProps => {
|
||||
const {
|
||||
rootClassName,
|
||||
className,
|
||||
formId,
|
||||
handleSubmit,
|
||||
pristine,
|
||||
initialValues,
|
||||
intl,
|
||||
inProgress,
|
||||
recoveryError,
|
||||
values,
|
||||
} = fieldRenderProps;
|
||||
|
||||
const emailRequired = validators.required(emailRequiredMessage);
|
||||
const emailValid = validators.emailFormatValid(emailInvalidMessage);
|
||||
// email
|
||||
const emailLabel = intl.formatMessage({
|
||||
id: 'PasswordRecoveryForm.emailLabel',
|
||||
});
|
||||
const emailPlaceholder = intl.formatMessage({
|
||||
id: 'PasswordRecoveryForm.emailPlaceholder',
|
||||
});
|
||||
const emailRequiredMessage = intl.formatMessage({
|
||||
id: 'PasswordRecoveryForm.emailRequired',
|
||||
});
|
||||
const emailNotFoundMessage = intl.formatMessage({
|
||||
id: 'PasswordRecoveryForm.emailNotFound',
|
||||
});
|
||||
const emailInvalidMessage = intl.formatMessage({
|
||||
id: 'PasswordRecoveryForm.emailInvalid',
|
||||
});
|
||||
|
||||
// In case a given email is not found, pass a custom error message
|
||||
// to be rendered with the input component
|
||||
const customErrorText = isPasswordRecoveryEmailNotFoundError(recoveryError)
|
||||
? emailNotFoundMessage
|
||||
: null;
|
||||
const initialEmail = initialValues ? initialValues.email : null;
|
||||
const emailRequired = validators.required(emailRequiredMessage);
|
||||
const emailValid = validators.emailFormatValid(emailInvalidMessage);
|
||||
|
||||
const classes = classNames(rootClassName || css.root, className);
|
||||
const submitInProgress = submitting || inProgress;
|
||||
const submitDisabled = (pristine && !initialEmail) || submitInProgress;
|
||||
// In case a given email is not found, pass a custom error message
|
||||
// to be rendered with the input component
|
||||
const customErrorText = isPasswordRecoveryEmailNotFoundError(recoveryError)
|
||||
? emailNotFoundMessage
|
||||
: null;
|
||||
const initialEmail = initialValues ? initialValues.email : null;
|
||||
const emailTouched = values.email !== this.submittedValues.email;
|
||||
|
||||
const loginLink = (
|
||||
<NamedLink name="LoginPage" className={css.modalHelperLink}>
|
||||
<FormattedMessage id="PasswordRecoveryForm.loginLinkText" />
|
||||
</NamedLink>
|
||||
);
|
||||
const classes = classNames(rootClassName || css.root, className);
|
||||
const submitInProgress = inProgress;
|
||||
const submittedOnce = Object.keys(this.submittedValues).length > 0;
|
||||
const pristineSinceLastSubmit = submittedOnce && isEqual(values, this.submittedValues);
|
||||
const submitDisabled =
|
||||
(pristine && !initialEmail) || submitInProgress || pristineSinceLastSubmit;
|
||||
|
||||
return (
|
||||
<Form className={classes} onSubmit={handleSubmit}>
|
||||
<FieldTextInput
|
||||
className={css.email}
|
||||
type="email"
|
||||
id={formId ? `${formId}.email` : 'email'}
|
||||
name="email"
|
||||
autoComplete="email"
|
||||
label={emailLabel}
|
||||
placeholder={emailPlaceholder}
|
||||
validate={validators.composeValidators(emailRequired, emailValid)}
|
||||
customErrorText={customErrorText}
|
||||
/>
|
||||
const loginLink = (
|
||||
<NamedLink name="LoginPage" className={css.modalHelperLink}>
|
||||
<FormattedMessage id="PasswordRecoveryForm.loginLinkText" />
|
||||
</NamedLink>
|
||||
);
|
||||
|
||||
<div className={css.bottomWrapper}>
|
||||
<p className={css.bottomWrapperText}>
|
||||
<span className={css.modalHelperText}>
|
||||
<FormattedMessage id="PasswordRecoveryForm.loginLinkInfo" values={{ loginLink }} />
|
||||
</span>
|
||||
</p>
|
||||
return (
|
||||
<Form
|
||||
className={classes}
|
||||
onSubmit={e => {
|
||||
this.submittedValues = values;
|
||||
handleSubmit(e);
|
||||
}}
|
||||
>
|
||||
<FieldTextInput
|
||||
className={css.email}
|
||||
type="email"
|
||||
id={formId ? `${formId}.email` : 'email'}
|
||||
name="email"
|
||||
autoComplete="email"
|
||||
label={emailLabel}
|
||||
placeholder={emailPlaceholder}
|
||||
validate={validators.composeValidators(emailRequired, emailValid)}
|
||||
customErrorText={emailTouched ? null : customErrorText}
|
||||
/>
|
||||
|
||||
<PrimaryButton type="submit" inProgress={submitInProgress} disabled={submitDisabled}>
|
||||
<FormattedMessage id="PasswordRecoveryForm.sendInstructions" />
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
<div className={css.bottomWrapper}>
|
||||
<p className={css.bottomWrapperText}>
|
||||
<span className={css.modalHelperText}>
|
||||
<FormattedMessage
|
||||
id="PasswordRecoveryForm.loginLinkInfo"
|
||||
values={{ loginLink }}
|
||||
/>
|
||||
</span>
|
||||
</p>
|
||||
|
||||
<PrimaryButton
|
||||
type="submit"
|
||||
inProgress={submitInProgress}
|
||||
disabled={submitDisabled}
|
||||
>
|
||||
<FormattedMessage id="PasswordRecoveryForm.sendInstructions" />
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</Form>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
PasswordRecoveryFormComponent.defaultProps = {
|
||||
rootClassName: null,
|
||||
|
|
|
|||
|
|
@ -3193,8 +3193,8 @@ final-form-arrays@^1.0.4:
|
|||
resolved "https://registry.yarnpkg.com/final-form-arrays/-/final-form-arrays-1.0.4.tgz#74330cb4d64ce07bb1c4a9084d0b65afdac6bf72"
|
||||
|
||||
final-form@^4.4.0:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.yarnpkg.com/final-form/-/final-form-4.4.0.tgz#3eaa1305d7b03d1e5698ace5ff361d4d20d44ac0"
|
||||
version "4.5.2"
|
||||
resolved "https://registry.yarnpkg.com/final-form/-/final-form-4.5.2.tgz#f8a9518784bdc2f72e71548a42b04c640963ca49"
|
||||
|
||||
finalhandler@1.1.1:
|
||||
version "1.1.1"
|
||||
|
|
@ -6889,8 +6889,8 @@ react-final-form-arrays@^1.0.4:
|
|||
resolved "https://registry.yarnpkg.com/react-final-form-arrays/-/react-final-form-arrays-1.0.4.tgz#888b9b81a806844cf66d73f4e8b60e5f18fa2a11"
|
||||
|
||||
react-final-form@^3.1.5:
|
||||
version "3.1.5"
|
||||
resolved "https://registry.yarnpkg.com/react-final-form/-/react-final-form-3.1.5.tgz#6fa0e4c61e49b691b679ef4c8a5ad3fe92df0956"
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/react-final-form/-/react-final-form-3.3.1.tgz#dbe40eec853a9c451e5a8deb3b02d948a2fb7e3c"
|
||||
|
||||
react-google-maps@^9.4.5:
|
||||
version "9.4.5"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue