Skip to content

Commit

Permalink
feat: design for forgot password and reset password
Browse files Browse the repository at this point in the history
  • Loading branch information
attiyaIshaque committed Apr 24, 2024
1 parent 3fa33c1 commit 3ded298
Show file tree
Hide file tree
Showing 11 changed files with 448 additions and 1 deletion.
1 change: 1 addition & 0 deletions src/forms/fields/email-field/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const EmailField = (props) => {
return (
<Form.Group controlId="email" className="w-100 mb-4">
<Form.Control
className="mr-0"
type="email"
name={name}
value={value}
Expand Down
19 changes: 18 additions & 1 deletion src/forms/fields/password-field/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,38 @@ const PasswordField = (props) => {
return (
<Form.Group controlId="password" className="w-100 mb-4">
<Form.Control
className="mr-0"
type={isPasswordHidden ? 'password' : 'text'}
name={name}
value={value}
onChange={handleChange}
onBlur={props.handleBlur}
aria-invalid={props.errorMessage !== ''}
trailingElement={isPasswordHidden ? ShowButton : HideButton}
floatingLabel={formatMessage(messages.registrationFormPasswordFieldLabel)}
floatingLabel={props.floatingLabel}

/>
{props.errorMessage !== '' && (
<Form.Control.Feedback key="error" className="form-text-size" hasIcon={false} feedback-for={props.name} type="invalid">
{props.errorMessage}
</Form.Control.Feedback>
)}
</Form.Group>
);
};

PasswordField.defaultProps = {
errorMessage: '',
handleBlur: null,
};

PasswordField.propTypes = {
name: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
handleChange: PropTypes.func.isRequired,
errorMessage: PropTypes.string,
floatingLabel: PropTypes.string.isRequired,
handleBlur: PropTypes.func,
};

export default PasswordField;
1 change: 1 addition & 0 deletions src/forms/registration-popup/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ const RegistrationForm = () => {
name="password"
value={formFields.password}
handleChange={handleOnChange}
floatingLabel={formatMessage(messages.registrationFormPasswordFieldLabel)}
/>
<MarketingEmailOptOutCheckbox
name="marketingEmailOptOut"
Expand Down
5 changes: 5 additions & 0 deletions src/forms/registration-popup/messages.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ const messages = defineMessages({
defaultMessage: 'Sign in with your credentials',
description: 'Text for signing in with credentials',
},
registrationFormPasswordFieldLabel: {
id: 'registration.form.password.label',
defaultMessage: 'Password',
description: 'Label for password input field',
},
});

export default messages;
20 changes: 20 additions & 0 deletions src/forms/reset-password-popup/ResetPasswordPopupHeader.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';

import { useIntl } from '@edx/frontend-platform/i18n';

import messages from './messages';

const ResetPasswordPopupHeader = () => {
const { formatMessage } = useIntl();

return (
<>
<h2 className="font-italic text-center display-1 mb-4 text-dark-500">
{formatMessage(messages.resetPasswordFormHeading)}
</h2>
<hr className="separator mb-3 mt-3" />
</>
);
};

export default ResetPasswordPopupHeader;
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from 'react';

import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
import { Alert } from '@openedx/paragon';

import messages from '../messages';

const ForgotPasswordEmailSent = () => {
const { formatMessage } = useIntl();

return (
<div className="text-gray-800 mb-5">
<span className="font-weight-bold mr-2 h3 text-center d-block">
{formatMessage(messages.emailSentMessage)}
</span>
<span>
<FormattedMessage
id="forgot.password.confirmation.message"
defaultMessage="We sent an email to {email} with instructions to reset your password.
If you do not receive a password reset message after 1 minute, verify that you entered
the correct email address, or check your spam folder. If you need further assistance, visit Help
Center contact edX support at {supportLink}."
description="Forgot password confirmation message"
values={{
email: <span className="data-hj-suppress">[email protected]</span>,
supportLink: (
<Alert.Link href="#" target="_blank">
[email protected]
</Alert.Link>
),
}}
/>
</span>
</div>

);
};

export default ForgotPasswordEmailSent;
104 changes: 104 additions & 0 deletions src/forms/reset-password-popup/forgot-password/ForgotPasswordPage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React, { useState } from 'react';

import { useIntl } from '@edx/frontend-platform/i18n';
import {
Button, Container, Form, Hyperlink,
} from '@openedx/paragon';

import ForgotPasswordEmailSent from './ForgotPasswordEmailSent';
import EmailField from '../../fields/email-field';
import messages from '../messages';
import ResetPasswordPopupHeader from '../ResetPasswordPopupHeader';
import '../index.scss';

/**
* ForgotPasswordForm component for handling user password reset.
* This component provides a form for users to reset their password by entering their email.
*/
const ForgotPasswordPage = () => {
const { formatMessage } = useIntl();

const [formFields, setFormFields] = useState({ email: '' });
const [isSuccess, setIsSuccess] = useState(false);

const handleOnChange = (event) => {
const { name } = event.target;
const value = event.target.type === 'checkbox' ? event.target.checked : event.target.value;
setFormFields(prevState => ({ ...prevState, [name]: value }));
};

const handleSubmit = (e) => {
e.preventDefault();
setIsSuccess(true);
};

// JSX for sign-in link section
const signInLink = (
<span className="mt-4 text-gray-800 mt-1 d-block help-text">
{formatMessage(messages.resetPasswordFormNeedHelpText)}
<Hyperlink isInline className="p-2" destination="#">
{formatMessage(messages.resetPasswordFormHelpCenterLink)}
</Hyperlink>
</span>
);

// JSX for sign-in with additional help link section
const signInWithAdditionalHelpLink = (
<span className="font-weight-normal small">
{formatMessage(messages.resetPasswordFormAdditionalHelpText)}
<Hyperlink isInline className="p-2" destination="#">
{formatMessage(messages.resetPasswordFormAdditionalHelpLink)}
</Hyperlink>
</span>
);

return (
<Container size="lg" className="reset-password-form overflow-auto">
<ResetPasswordPopupHeader />
{ !isSuccess && (
<Form id="reset-password-form" name="reset-password-form" className="d-flex flex-column my-4.5">
<EmailField
name="email"
value={formFields.email}
handleChange={handleOnChange}
/>

<Button
id="reset-password-user"
name="reset-password-user"
variant="primary"
type="submit"
className="align-self-end"
onClick={handleSubmit}
onMouseDown={(e) => e.preventDefault()}
>
{formatMessage(messages.resetPasswordFormSubmitButton)}
</Button>

<div>
{signInLink}
{signInWithAdditionalHelpLink}
</div>
</Form>
)}
{ isSuccess && (
<ForgotPasswordEmailSent />
)}
<form className="text-center">
<Button
id="reset-password-back-to-login"
name="reset-password-back-to-login"
variant="tertiary"
type="submit"
className="align-self-center back-to-login-button"
onClick={handleSubmit}
onMouseDown={(e) => e.preventDefault()}
>
{formatMessage(messages.resetPasswordBackToLoginButton)}
</Button>
</form>
</Container>
);
};

export default ForgotPasswordPage;
22 changes: 22 additions & 0 deletions src/forms/reset-password-popup/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@import "~@edx/brand/paragon/variables";

.reset-password-form {
padding: 3.5rem 2.5rem;
}

.separator {
border: 0;
border-top: 0.075rem solid $light-500
}

.back-to-login-button {
border-color: $primary-500;
border: 2px solid;
}

.pgn__form-control-floating-label-text:after {
content: none;
}
.help-text {
line-height: 0.5;
}
101 changes: 101 additions & 0 deletions src/forms/reset-password-popup/messages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { defineMessages } from '@edx/frontend-platform/i18n';

const messages = defineMessages({
resetPasswordFormHeading: {
id: 'reset.password.form.heading',
defaultMessage: 'Reset Password',
description: 'Reset password form main heading',
},
resetPasswordFormSubmitButton: {
id: 'reset.password.form.submit.button',
defaultMessage: 'Submit',
description: 'Text for submit button on reset password form',
},
resetPasswordFormNeedHelpText: {
id: 'reset.password.form.need.help.text',
defaultMessage: 'Need help signing in?',
description: 'reset Password help text',
},
resetPasswordFormHelpCenterLink: {
id: 'reset.password.form.help.center.link',
defaultMessage: 'Help center',
description: 'Text for help center link',
},
resetPasswordFormAdditionalHelpText: {
id: 'reset.password.form.additional.help.text',
defaultMessage: 'For additional help, contact edX support at',
description: 'Label for link that leads learners to the email page',
},
resetPasswordFormAdditionalHelpLink: {
id: 'reset.password.form.additional.help.link',
defaultMessage: '[email protected]',
description: 'Link for email',
},
resetPasswordBackToLoginButton: {
id: 'reset.password.back.to.login.button',
defaultMessage: 'Back to login',
description: 'Text for back to login button on reset password form',
},
newPasswordLabel: {
id: 'new.password.label',
defaultMessage: 'New password',
description: 'New password field label for the reset password page.',
},
confirmPasswordLabel: {
id: 'confirm.password.label',
defaultMessage: 'Confirm password',
description: 'Confirm password field label for the reset password page.',
},
resetPasswordButton: {
id: 'reset.password.button',
defaultMessage: 'Reset password',
description: 'Button text for reset password popup.',
},
enterConfirmPasswordMessage: {
id: 'enter.confirm.password.message',
defaultMessage: 'Enter and confirm the new password',
description: 'Message for entering and confirming the new password',
},
// email sent messages
emailSentMessage: {
id: 'email.sent.message',
defaultMessage: 'Email has been sent',
description: 'Notification message indicating that an email has been sent',
},

resetPasswordInstructionsMessage: {
id: 'reset.password.instructions.message',
defaultMessage: 'We sent an email to [email protected] with instructions to reset your password. If you do not receive a password reset message after 1 minute, verify that you entered the correct email address, or check your spam folder. If you need further assistance, visit Help Center or contact edX support at [email protected]',
description: 'Instructions message after sending reset password email',
},
// validation errors
passwordRequiredMessage: {
id: 'password.required.message',
defaultMessage: 'Password is a required field',
description: 'Error message for empty password',
},
passwordValidationMessage: {
id: 'password.validation.message',
defaultMessage: 'Password criteria has not been met',
description: 'Error message for invalid password',
},
passwordDoNotMatch: {
id: 'passwords.do.not.match',
defaultMessage: 'Passwords do not match',
description: 'Password format error.',
},
confirmYourPassword: {
id: 'confirm.your.password',
defaultMessage: 'Confirm your password',
description: 'Field validation message when confirm password is empty',
},
// alert banner strings
resetPasswordFailureHeading: {
id: 'reset.password.failure.heading',
defaultMessage: 'We couldn\'t reset your password.',
description: 'Heading for reset password request failure',
},

});

export default messages;
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';

import { useIntl } from '@edx/frontend-platform/i18n';
import { Alert } from '@openedx/paragon';

import messages from '../messages';

const ResetPasswordFailure = () => {
const { formatMessage } = useIntl();

const errorMessage = ' ';
const heading = formatMessage(messages.resetPasswordFailureHeading);

if (errorMessage) {
return (
<Alert id="validation-errors" className="mb-4" variant="danger">
{heading}
</Alert>
);
}

return null;
};

export default ResetPasswordFailure;
Loading

0 comments on commit 3ded298

Please sign in to comment.