feat: VAN-1056 - remove formik (#621)

This commit is contained in:
Zainab Amir
2022-08-31 16:04:09 +05:00
committed by GitHub
parent 1cb0cfa113
commit 7ac8f6a40a
8 changed files with 100 additions and 205 deletions

View File

@@ -12,14 +12,16 @@ export const ENTERPRISE_LOGIN_URL = '/enterprise/login';
export const SUPPORTED_ICON_CLASSES = ['apple', 'facebook', 'google', 'microsoft'];
// Error Codes
export const FORM_SUBMISSION_ERROR = 'form-submission-error';
export const INTERNAL_SERVER_ERROR = 'internal-server-error';
export const API_RATELIMIT_ERROR = 'api-ratelimit-error';
// States
// Common States
export const DEFAULT_STATE = 'default';
export const PENDING_STATE = 'pending';
export const COMPLETE_STATE = 'complete';
export const FAILURE_STATE = 'failure';
export const FORBIDDEN_STATE = 'forbidden';
// Regex
export const VALID_EMAIL_REGEX = '(^[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+(\\.[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+)*'

View File

@@ -74,7 +74,7 @@ const FormFieldRenderer = (props) => {
}
case 'text': {
formField = (
<Form.Group controlId={fieldData.name} isInvalid={isRequired && errorMessage}>
<Form.Group controlId={fieldData.name} isInvalid={!!(isRequired && errorMessage)}>
<Form.Control
name={fieldData.name}
value={value}

View File

@@ -6,23 +6,24 @@ import { Alert } from '@edx/paragon';
import { CheckCircle, Error } from '@edx/paragon/icons';
import PropTypes from 'prop-types';
import { INTERNAL_SERVER_ERROR } from '../data/constants';
import {
COMPLETE_STATE, FORBIDDEN_STATE, FORM_SUBMISSION_ERROR, INTERNAL_SERVER_ERROR,
} from '../data/constants';
import { PASSWORD_RESET } from '../reset-password/data/constants';
import messages from './messages';
const ForgotPasswordAlert = (props) => {
const { email, emailError, intl } = props;
let message = '';
let heading = intl.formatMessage(messages['forgot.password.error.alert.title']);
let { status } = props;
if (emailError) {
status = 'form-submission-error';
status = FORM_SUBMISSION_ERROR;
}
let message = '';
let heading = intl.formatMessage(messages['forgot.password.error.alert.title']);
const supportUrl = getConfig().PASSWORD_RESET_SUPPORT_LINK;
switch (status) {
case 'complete':
case COMPLETE_STATE:
heading = intl.formatMessage(messages['confirmation.message.title']);
message = (
<FormattedMessage
@@ -34,14 +35,7 @@ const ForgotPasswordAlert = (props) => {
values={{
email: <span className="data-hj-suppress">{email}</span>,
supportLink: (
<Alert.Link
className="alert-link"
href={supportUrl}
onClick={e => {
e.preventDefault();
window.open(supportUrl, '_blank');
}}
>
<Alert.Link href={getConfig().PASSWORD_RESET_SUPPORT_LINK} target="_blank">
{intl.formatMessage(messages['confirmation.support.link'])}
</Alert.Link>
),
@@ -52,11 +46,11 @@ const ForgotPasswordAlert = (props) => {
case INTERNAL_SERVER_ERROR:
message = intl.formatMessage(messages['internal.server.error']);
break;
case 'forbidden':
case FORBIDDEN_STATE:
heading = intl.formatMessage(messages['forgot.password.error.message.title']);
message = intl.formatMessage(messages['forgot.password.request.in.progress.message']);
break;
case 'form-submission-error':
case FORM_SUBMISSION_ERROR:
message = intl.formatMessage(messages['extend.field.errors'], { emailError });
break;
case PASSWORD_RESET.INVALID_TOKEN:
@@ -79,7 +73,7 @@ const ForgotPasswordAlert = (props) => {
return (
<Alert
id="validation-errors"
className="mb-5"
className="mb-4"
variant={`${status === 'complete' ? 'success' : 'danger'}`}
icon={status === 'complete' ? CheckCircle : Error}
>

View File

@@ -13,7 +13,6 @@ import {
Tabs,
} from '@edx/paragon';
import { ChevronLeft } from '@edx/paragon/icons';
import { Formik } from 'formik';
import PropTypes from 'prop-types';
import { Helmet } from 'react-helmet';
import { Redirect } from 'react-router-dom';
@@ -28,16 +27,15 @@ import ForgotPasswordAlert from './ForgotPasswordAlert';
import messages from './messages';
const ForgotPasswordPage = (props) => {
const platformName = getConfig().SITE_NAME;
const emailRegex = new RegExp(VALID_EMAIL_REGEX, 'i');
const {
intl, status, submitState, emailValidationError,
} = props;
const platformName = getConfig().SITE_NAME;
const supportUrl = getConfig().LOGIN_ISSUE_SUPPORT_LINK;
const regex = new RegExp(VALID_EMAIL_REGEX, 'i');
const [bannerEmail, setBannerEmail] = useState('');
const [email, setEmail] = useState(props.email);
const [bannerEmail, setBannerEmail] = useState('');
const [formErrors, setFormErrors] = useState('');
const [validationError, setValidationError] = useState(emailValidationError);
const [key, setKey] = useState('');
@@ -48,33 +46,44 @@ const ForgotPasswordPage = (props) => {
useEffect(() => {
setValidationError(emailValidationError);
}, [emailValidationError]);
useEffect(() => {
if (status === 'complete') {
setEmail('');
} else {
setEmail(props.email);
}
}, [emailValidationError, props.email, status]);
}, [status]);
const getValidationMessage = (value) => {
let error = '';
if (value === '') {
error = intl.formatMessage(messages['forgot.password.empty.email.field.error']);
} else if (!regex.test(value)) {
} else if (!emailRegex.test(value)) {
error = intl.formatMessage(messages['forgot.password.page.invalid.email.message']);
}
return error;
};
const onBlur = (value) => {
const emailError = getValidationMessage(value);
setBannerEmail(value);
props.setForgotPasswordFormData({ email: value, emailValidationError: emailError });
const handleBlur = () => {
props.setForgotPasswordFormData({ email, emailValidationError: getValidationMessage(email) });
};
const onFocus = () => {
props.setForgotPasswordFormData({ emailValidationError: '' });
const handleFocus = () => props.setForgotPasswordFormData({ emailValidationError: '' });
const handleSubmit = (e) => {
e.preventDefault();
setBannerEmail(email);
const error = getValidationMessage(email);
if (error) {
setFormErrors(error);
props.setForgotPasswordFormData({ email, emailValidationError: error });
windowScrollTo({ left: 0, top: 0, behavior: 'smooth' });
} else {
props.forgotPassword(email);
}
};
const tabTitle = (
@@ -86,6 +95,11 @@ const ForgotPasswordPage = (props) => {
return (
<BaseComponent>
<Helmet>
<title>{intl.formatMessage(messages['forgot.password.page.title'],
{ siteName: getConfig().SITE_NAME })}
</title>
</Helmet>
<div>
<Tabs activeKey="" id="controlled-tab" onSelect={(k) => setKey(k)}>
<Tab title={tabTitle} eventKey={LOGIN_PAGE} />
@@ -94,86 +108,55 @@ const ForgotPasswordPage = (props) => {
<Redirect to={updatePathWithQueryParams(key)} />
)}
<div id="main-content" className="main-content">
<Formik
initialValues={{ email }}
enableReinitialize={props.email === ''}
validateOnChange={false}
validate={(values) => {
const validationMessage = getValidationMessage(values.email);
props.setForgotPasswordFormData({ emailValidationError: validationMessage });
if (validationMessage !== '') {
windowScrollTo({ left: 0, top: 0, behavior: 'smooth' });
return { email: validationMessage };
}
return {};
}}
onSubmit={(values) => {
onBlur(values.email);
props.forgotPassword(values.email);
}}
>
{({
errors, handleSubmit, setFieldValue, values,
}) => (
<>
<Helmet>
<title>{intl.formatMessage(messages['forgot.password.page.title'],
{ siteName: getConfig().SITE_NAME })}
</title>
</Helmet>
<Form id="forget-password-form" name="forget-password-form" className="mw-xs">
<ForgotPasswordAlert email={bannerEmail} emailError={errors.email} status={status} />
<h2 className="h4">
{intl.formatMessage(messages['forgot.password.page.heading'])}
</h2>
<p className="mb-4">
{intl.formatMessage(messages['forgot.password.page.instructions'])}
</p>
<FormGroup
floatingLabel={intl.formatMessage(messages['forgot.password.page.email.field.label'])}
name="email"
errorMessage={validationError}
value={values.email}
handleBlur={() => onBlur(values.email)}
handleChange={e => setFieldValue('email', e.target.value)}
handleFocus={() => onFocus()}
helpText={[intl.formatMessage(messages['forgot.password.email.help.text'], { platformName })]}
/>
<StatefulButton
id="submit-forget-password"
name="submit-forget-password"
type="submit"
variant="brand"
className="login-button-width"
state={submitState}
labels={{
default: intl.formatMessage(messages['forgot.password.page.submit.button']),
pending: '',
}}
onClick={handleSubmit}
onMouseDown={(e) => e.preventDefault()}
/>
<Hyperlink
id="forgot-password"
name="forgot-password"
className="ml-4 font-weight-500 text-body"
destination={supportUrl}
target="_blank"
showLaunchIcon={false}
>
{intl.formatMessage(messages['need.help.sign.in.text'])}
</Hyperlink>
<p className="mt-5 small text-gray-700">
{intl.formatMessage(messages['additional.help.text'], { platformName })}
<span>
<Hyperlink isInline destination={`mailto:${getConfig().INFO_EMAIL}`}>{getConfig().INFO_EMAIL}</Hyperlink>
</span>
</p>
</Form>
</>
)}
</Formik>
<Form id="forget-password-form" name="forget-password-form" className="mw-xs">
<ForgotPasswordAlert email={bannerEmail} emailError={formErrors} status={status} />
<h2 className="h4">
{intl.formatMessage(messages['forgot.password.page.heading'])}
</h2>
<p className="mb-4">
{intl.formatMessage(messages['forgot.password.page.instructions'])}
</p>
<FormGroup
floatingLabel={intl.formatMessage(messages['forgot.password.page.email.field.label'])}
name="email"
value={email}
errorMessage={validationError}
handleChange={(e) => setEmail(e.target.value)}
handleBlur={handleBlur}
handleFocus={handleFocus}
helpText={[intl.formatMessage(messages['forgot.password.email.help.text'], { platformName })]}
/>
<StatefulButton
id="submit-forget-password"
name="submit-forget-password"
type="submit"
variant="brand"
className="forgot-password-button-width"
state={submitState}
labels={{
default: intl.formatMessage(messages['forgot.password.page.submit.button']),
pending: '',
}}
onClick={handleSubmit}
onMouseDown={(e) => e.preventDefault()}
/>
<Hyperlink
id="forgot-password"
name="forgot-password"
className="ml-4 font-weight-500 text-body"
destination={getConfig().LOGIN_ISSUE_SUPPORT_LINK}
target="_blank"
showLaunchIcon={false}
>
{intl.formatMessage(messages['need.help.sign.in.text'])}
</Hyperlink>
<p className="mt-5.5 small text-gray-700">
{intl.formatMessage(messages['additional.help.text'], { platformName })}
<span>
<Hyperlink isInline destination={`mailto:${getConfig().INFO_EMAIL}`}>{getConfig().INFO_EMAIL}</Hyperlink>
</span>
</p>
</Form>
</div>
</div>
</BaseComponent>
@@ -181,10 +164,10 @@ const ForgotPasswordPage = (props) => {
};
ForgotPasswordPage.propTypes = {
intl: intlShape.isRequired,
email: PropTypes.string,
emailValidationError: PropTypes.string,
forgotPassword: PropTypes.func.isRequired,
intl: intlShape.isRequired,
setForgotPasswordFormData: PropTypes.func.isRequired,
status: PropTypes.string,
submitState: PropTypes.string,

View File

@@ -51,11 +51,6 @@ const messages = defineMessages({
defaultMessage: 'Enter your email',
description: 'Error message that appears when user tries to submit empty email field',
},
'forgot.password.invalid.email.heading': {
id: 'forgot.password.invalid.email',
defaultMessage: 'An error occurred.',
description: 'heading for invalid email',
},
'forgot.password.invalid.email.message': {
id: 'forgot.password.invalid.email.message',
defaultMessage: "The email address you've provided isn't formatted correctly.",

View File

@@ -19,6 +19,11 @@ $apple-focus-black: $apple-black;
$elevation-level-2-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.15);
// Forgot Password Page
.forgot-password-button-width {
min-width: 6rem;
}
.centered-align-spinner {
left: 0;
right: 0;