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 May 9, 2024
1 parent 4b83b3e commit 7f62a63
Show file tree
Hide file tree
Showing 18 changed files with 456 additions and 27 deletions.
2 changes: 2 additions & 0 deletions .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ REFRESH_ACCESS_TOKEN_ENDPOINT=http://localhost:18000/login_refresh
SITE_NAME=edX
AUTHN_ALGOLIA_APP_ID=''
AUTHN_ALGOLIA_SEARCH_API_KEY=''
INFO_EMAIL=[email protected]
LOGIN_ISSUE_SUPPORT_LINK='http://localhost:18000/login-issue-support-url'
8 changes: 3 additions & 5 deletions src/forms/fields/email-field/index.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import React from 'react';

import { useIntl } from '@edx/frontend-platform/i18n';
import { Form } from '@openedx/paragon';
import PropTypes from 'prop-types';

import messages from './messages';

/**
* Email field component. It accepts following handler(s)
* - handleChange for setting value change and
Expand All @@ -14,17 +11,17 @@ import messages from './messages';
* - setting value on change
*/
const EmailField = (props) => {
const { formatMessage } = useIntl();
const { name, value, handleChange } = props;

return (
<Form.Group controlId="email" className="w-100 mb-4">
<Form.Control
className="mr-0"
type="email"
name={name}
value={value}
onChange={handleChange}
floatingLabel={formatMessage(messages.registrationFormEmailFieldLabel)}
floatingLabel={props.floatingLabel}
/>
</Form.Group>
);
Expand All @@ -34,6 +31,7 @@ EmailField.propTypes = {
name: PropTypes.string.isRequired,
value: PropTypes.string.isRequired,
handleChange: PropTypes.func.isRequired,
floatingLabel: PropTypes.func.isRequired,
};

export default EmailField;
11 changes: 0 additions & 11 deletions src/forms/fields/email-field/messages.jsx

This file was deleted.

15 changes: 11 additions & 4 deletions src/forms/fields/password-field/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const PasswordField = (props) => {
value,
handleChange,
handleFocus,
floatingLabel,
} = props;

const [isPasswordHidden, setHiddenTrue, setHiddenFalse] = useToggle(true);
Expand Down Expand Up @@ -76,23 +77,24 @@ const PasswordField = (props) => {
{props.value.length >= 8
? <Icon className="text-success mr-1" src={Check} />
: <Icon className="mr-1 text-light-700" src={Remove} />}
{formatMessage(messages.eightCharcters)}
{formatMessage(messages.eightCharacters)}
</span>
</Tooltip>
);

return (
<Form.Group controlId="password" className="w-100 mb-4">
<OverlayTrigger key="tooltip" placement={placement} overlay={tooltip} show>
<OverlayTrigger key="tooltip" placement={placement} overlay={tooltip} hide>
<Form.Control
as="input"
className="mr-0"
type={isPasswordHidden ? 'password' : 'text'}
name={name}
value={value}
onChange={handleChange}
onFocus={handleFocus}
trailingElement={isPasswordHidden ? ShowButton : HideButton}
floatingLabel={formatMessage(messages.registrationFormPasswordFieldLabel)}
floatingLabel={floatingLabel}
/>
</OverlayTrigger>
{errorMessage !== '' && (
Expand All @@ -111,11 +113,16 @@ const PasswordField = (props) => {
};

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

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

export default PasswordField;
7 changes: 1 addition & 6 deletions src/forms/fields/password-field/messages.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
import { defineMessages } from '@edx/frontend-platform/i18n';

const messages = defineMessages({
registrationFormPasswordFieldLabel: {
id: 'registration.form.password.label',
defaultMessage: 'Password',
description: 'Label for password input field',
},
showPasswordAlt: {
id: 'show.password',
defaultMessage: 'Show password',
Expand All @@ -26,7 +21,7 @@ const messages = defineMessages({
defaultMessage: '1 number',
description: 'password requirement to have 1 number',
},
eightCharcters: {
eightCharacters: {
id: 'eight.characters',
defaultMessage: '8 characters',
description: 'password requirement to have a minimum of 8 characters',
Expand Down
1 change: 1 addition & 0 deletions src/forms/fields/text-field/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ const TextField = (props) => {
<Form.Control
as="input"
type="text"
className="mr-0"
name={name}
value={value}
onChange={handleChange}
Expand Down
1 change: 1 addition & 0 deletions src/forms/login-popup/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ const LoginForm = () => {
errorMessage={formErrors.password}
handleChange={handleOnChange}
handleFocus={handleOnFocus}
floatingLabel={formatMessage(messages.loginFormPasswordFieldLabel)}
/>
{/* TODO: this destination will be replaced with actual links */}
<Hyperlink className="hyper-link" destination="#" isInline>
Expand Down
2 changes: 2 additions & 0 deletions src/forms/registration-popup/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ const RegistrationForm = () => {
name="email"
value={formFields.email}
handleChange={handleOnChange}
floatingLabel={formatMessage(messages.registrationFormEmailFieldLabel)}
/>
<MarketingEmailOptInCheckbox
name="marketingEmailOptIn"
Expand All @@ -135,6 +136,7 @@ const RegistrationForm = () => {
errorMessage=""
handleChange={handleOnChange}
handleFocus={() => { }}
floatingLabel={formatMessage(messages.registrationFormPasswordFieldLabel)}
/>
</Stepper.Step>
<div className="d-flex flex-column my-4">
Expand Down
10 changes: 10 additions & 0 deletions src/forms/registration-popup/messages.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,16 @@ const messages = defineMessages({
defaultMessage: 'Fill out the fields below to create your account',
description: 'Heading that appear on the second step',
},
registrationFormPasswordFieldLabel: {
id: 'registration.form.password.label',
defaultMessage: 'Password',
description: 'Label for password input field',
},
registrationFormEmailFieldLabel: {
id: 'registration.form.email.label',
defaultMessage: 'Enter your email',
description: 'Label for email input field',
},
});

export default messages;
25 changes: 25 additions & 0 deletions src/forms/reset-password-popup/ResetPasswordHeader.jsx
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 messages from './messages';

/**
* Header component for the reset password form.
* Renders the heading for the reset password form along with a separator.
* @returns {JSX.Element} The rendered header component.
*/
const ResetPasswordHeader = () => {
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 ResetPasswordHeader;
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react';

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

import messages from '../messages';

/**
* Component that renders the display confirmation message after sending the password reset email.
* @returns {JSX.Element} The rendered confirmation message component.
*/
const ForgotPasswordEmailSentConfirmation = () => {
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={{
/* TODO: this email will be replaced with actual email */
email: <span className="data-hj-suppress">[email protected]</span>,
supportLink: (
<Alert.Link href={getConfig().PASSWORD_RESET_SUPPORT_LINK} target="_blank">
{formatMessage(messages.resetPasswordFormAdditionalHelpLink)}
</Alert.Link>
),
}}
/>
</span>
</div>

);
};

export default ForgotPasswordEmailSentConfirmation;
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import React, { useState } from 'react';

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

import ForgotPasswordEmailSentConfirmation from './ForgotPasswordEmailSentConfirmation';
import { InlineLink } from '../../../common-ui';
import EmailField from '../../fields/email-field';
import messages from '../messages';
import ResetPasswordHeader from '../ResetPasswordHeader';
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);
};

return (
<Container size="lg" className="authn__popup-container p-5 overflow-auto">
<ResetPasswordHeader />
{!isSuccess && (
<Form id="forgot-password-form" name="reset-password-form" className="d-flex flex-column my-4.5">
<EmailField
name="email"
value={formFields.email}
handleChange={handleOnChange}
floatingLabel={formatMessage(messages.forgotPasswordFormEmailFieldLabel)}
/>
<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>
<InlineLink
className="mb-2"
destination={getConfig().LOGIN_ISSUE_SUPPORT_LINK}
linkHelpText={formatMessage(messages.resetPasswordFormNeedHelpText)}
linkText={formatMessage(messages.resetPasswordFormHelpCenterLink)}
/>
<InlineLink
className="mb-2 font-weight-normal small"
destination={getConfig().INFO_EMAIL}
linkHelpText={formatMessage(messages.resetPasswordFormAdditionalHelpText)}
linkText={formatMessage(messages.resetPasswordFormAdditionalHelpLink)}
/>
</div>
</Form>
)}
{isSuccess && (
<ForgotPasswordEmailSentConfirmation />
)}
<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;
16 changes: 16 additions & 0 deletions src/forms/reset-password-popup/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@import "~@edx/brand/paragon/variables";

.separator {
border: 0;
border-top: 0.075rem solid $light-500
}
.back-to-login__button {
border-color: $primary-500;
border: 2px solid;
}
#set-reset-password-form .pgn__form-control-floating-label-text:after {
content: none;
}
.help-text {
line-height: 0.5;
}
Loading

0 comments on commit 7f62a63

Please sign in to comment.