Skip to content

Commit

Permalink
fix: hyperlink related issues (#87)
Browse files Browse the repository at this point in the history
  • Loading branch information
zainab-amir authored Jun 27, 2024
1 parent d5aa0cc commit 9e0cc78
Show file tree
Hide file tree
Showing 10 changed files with 69 additions and 73 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ src/i18n/transifex_input.json
*~
module.config.js
.idea/
.env.private
7 changes: 4 additions & 3 deletions example/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,13 @@ initialize({
mergeConfig({
AUTHN_ALGOLIA_APP_ID: process.env.AUTHN_ALGOLIA_APP_ID || '',
AUTHN_ALGOLIA_SEARCH_API_KEY: process.env.AUTHN_ALGOLIA_SEARCH_API_KEY || '',
TOS_AND_HONOR_CODE: process.env.TOS_AND_HONOR_CODE || '',
INFO_EMAIL: process.env.INFO_EMAIL || '',
LOGIN_ISSUE_SUPPORT_LINK: process.env.LOGIN_ISSUE_SUPPORT_LINK || '',
ONBOARDING_COMPONENT_ENV: process.env.ONBOARDING_COMPONENT_ENV || '',
PASSWORD_RESET_SUPPORT_LINK: process.env.PASSWORD_RESET_SUPPORT_LINK || '',
PRIVACY_POLICY: process.env.PRIVACY_POLICY || '',
INFO_EMAIL: process.env.INFO_EMAIL || '',
TOS_AND_HONOR_CODE: process.env.TOS_AND_HONOR_CODE || '',
USER_RETENTION_COOKIE_NAME: process.env.USER_RETENTION_COOKIE_NAME || '',
ONBOARDING_COMPONENT_ENV: process.env.ONBOARDING_COMPONENT_ENV || '',
});
},
},
Expand Down
5 changes: 5 additions & 0 deletions src/common-ui/InlineLink/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ import PropTypes from 'prop-types';
* @param {string} linkHelpText - The help text displayed alongside the link.
* @param {string} linkText - The text displayed for the link.
* @param {Function} onClick - The function to call when the link is clicked. If provided, `destination` is ignored.
* @param {boolean} targetBlank - Tells whether to open the link in a new tab or not
*/
const InlineLink = ({
className = '',
destination = '',
linkHelpText = '',
linkText,
onClick = null,
targetBlank = false,
}) => {
const handleClick = (e) => {
if (onClick) {
Expand All @@ -39,10 +41,12 @@ const InlineLink = ({
</span>
)}
<Hyperlink
target={targetBlank ? '_blank' : '_self'}
className="pl-1 popup-container_inline-link_hyperlink"
destination={destination}
onClick={handleClick}
isInline
showLaunchIcon={false}
>
{linkText}
</Hyperlink>
Expand All @@ -56,6 +60,7 @@ InlineLink.propTypes = {
onClick: PropTypes.func,
linkHelpText: PropTypes.string,
linkText: PropTypes.string.isRequired,
targetBlank: PropTypes.bool,
};

export default InlineLink;
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const AccountActivationMessage = ({ messageType = null }) => {
}
case ACCOUNT_ACTIVATION_MESSAGE.ERROR: {
const supportLink = (
<Alert.Link href={getConfig().ACTIVATION_EMAIL_SUPPORT_LINK}>
<Alert.Link href={`mailto:${getConfig().ACTIVATION_EMAIL_SUPPORT_LINK}`}>
{formatMessage(messages.accountConfirmationSupportLink)}
</Alert.Link>
);
Expand Down
16 changes: 14 additions & 2 deletions src/forms/login-popup/components/LoginFailureAlert.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import { useIntl } from '@edx/frontend-platform/i18n';
import { Alert, Hyperlink } from '@openedx/paragon';
import PropTypes from 'prop-types';

import { setCurrentOpenedForm } from '../../../authn-component/data/reducers';
import {
FORBIDDEN_REQUEST,
FORBIDDEN_REQUEST, FORGOT_PASSWORD_FORM,
INTERNAL_SERVER_ERROR,
INVALID_FORM,
TPA_AUTHENTICATION_FAILURE,
} from '../../../data/constants';
import { useDispatch } from '../../../data/storeHooks';
import {
ACCOUNT_LOCKED_OUT,
ALLOWED_DOMAIN_LOGIN_ERROR,
Expand All @@ -29,9 +31,15 @@ import messages from '../messages';
*/

const LoginFailureAlert = (props) => {
const dispatch = useDispatch();
const { formatMessage } = useIntl();
const { context = {}, errorCode } = props;

const handleResetPasswordLinkClick = (event) => {
event.preventDefault();
dispatch(setCurrentOpenedForm(FORGOT_PASSWORD_FORM));
};

if (!errorCode || errorCode === TPA_AUTHENTICATION_FAILURE) {
return null;
}
Expand Down Expand Up @@ -92,7 +100,11 @@ const LoginFailureAlert = (props) => {
break;
case FAILED_LOGIN_ATTEMPT: {
resetLink = (
<Hyperlink destination="reset" isInline>
<Hyperlink
className="popup_login_form__inline_link-cursor"
onClick={handleResetPasswordLinkClick}
isInline
>
{formatMessage(messages.loginIncorrectCredentialsErrorBeforeAccountBlockedText)}
</Hyperlink>
);
Expand Down
90 changes: 30 additions & 60 deletions src/forms/login-popup/components/LoginFailureAlert.test.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import React from 'react';
import { Provider } from 'react-redux';

import { mergeConfig } from '@edx/frontend-platform';
import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n';
import {
render, screen,
} from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import configureStore from 'redux-mock-store';

import LoginFailureAlert from './LoginFailureAlert';
import {
Expand All @@ -13,6 +16,7 @@ import {
INVALID_FORM,
TPA_AUTHENTICATION_FAILURE,
} from '../../../data/constants';
import { AuthnContext } from '../../../data/storeHooks';
import SSOFailureAlert from '../../common-components/SSOFailureAlert';
import {
ACCOUNT_LOCKED_OUT,
Expand All @@ -25,20 +29,30 @@ import {

const IntlLoginFailureAlert = injectIntl(LoginFailureAlert);
const IntlSSOFailureAlert = injectIntl(SSOFailureAlert);
const mockStore = configureStore();

describe('LoginFailureAlert', () => {
let props = {};
let store = {};

const reduxWrapper = children => (
<IntlProvider locale="en">
<MemoryRouter>
<Provider context={AuthnContext} store={store}>{children}</Provider>
</MemoryRouter>
</IntlProvider>
);

beforeEach(() => {
store = mockStore({});
});

it('should not render error message if errorCode is not available', () => {
props = {
errorCode: '',
};

const { container } = render(
<IntlProvider locale="en">
<IntlLoginFailureAlert {...props} />
</IntlProvider>,
);
const { container } = render(reduxWrapper(<IntlLoginFailureAlert {...props} />));

expect(container.querySelector('#login-failure-alert')).toBeFalsy();
});
Expand All @@ -48,11 +62,7 @@ describe('LoginFailureAlert', () => {
errorCode: NON_COMPLIANT_PASSWORD_EXCEPTION,
};

const { container } = render(
<IntlProvider locale="en">
<IntlLoginFailureAlert {...props} />
</IntlProvider>,
);
const { container } = render(reduxWrapper(<IntlLoginFailureAlert {...props} />));

const expectedMessage = 'We couldn\'t sign you in. We recently changed our password requirements'
+ 'Your current password does not meet the new security requirements. We just sent a '
Expand All @@ -72,11 +82,7 @@ describe('LoginFailureAlert', () => {
errorCode: INACTIVE_USER,
};

const { container } = render(
<IntlProvider locale="en">
<IntlLoginFailureAlert {...props} />
</IntlProvider>,
);
const { container } = render(reduxWrapper(<IntlLoginFailureAlert {...props} />));

const expectedMessage = 'We couldn\'t sign you in. In order to sign in, you need to activate your account.'
+ 'We just sent an activation link to [email protected]. If you do not receive an email, '
Expand All @@ -97,11 +103,7 @@ describe('LoginFailureAlert', () => {
errorCode: FAILED_LOGIN_ATTEMPT,
};

const { container } = render(
<IntlProvider locale="en">
<IntlLoginFailureAlert {...props} />
</IntlProvider>,
);
const { container } = render(reduxWrapper(<IntlLoginFailureAlert {...props} />));

const expectedMessage = 'We couldn\'t sign you in. The username, email or password you entered is incorrect. '
+ 'You have 3 more sign in attempts before your account is temporarily locked.If you\'ve forgotten your password, click here to reset it.';
Expand All @@ -119,11 +121,7 @@ describe('LoginFailureAlert', () => {
errorCode: INCORRECT_EMAIL_PASSWORD,
};

const { container } = render(
<IntlProvider locale="en">
<IntlLoginFailureAlert {...props} />
</IntlProvider>,
);
const { container } = render(reduxWrapper(<IntlLoginFailureAlert {...props} />));

const expectedMessage = 'We couldn\'t sign you in. The username, email, or password you entered is incorrect. Please try again.';

Expand All @@ -135,11 +133,7 @@ describe('LoginFailureAlert', () => {
errorCode: ACCOUNT_LOCKED_OUT,
};

const { container } = render(
<IntlProvider locale="en">
<IntlLoginFailureAlert {...props} />
</IntlProvider>,
);
const { container } = render(reduxWrapper(<IntlLoginFailureAlert {...props} />));

const expectedMessage = 'We couldn\'t sign you in. To protect your account, it\'s been temporarily locked. Try again in 30 minutes.To be on the safe side, you can reset your password before trying again.';
expect(container.querySelector('#login-failure-alert').textContent).toBe(expectedMessage);
Expand All @@ -155,11 +149,7 @@ describe('LoginFailureAlert', () => {
errorCode: INCORRECT_EMAIL_PASSWORD,
};

const { container } = render(
<IntlProvider locale="en">
<IntlLoginFailureAlert {...props} />
</IntlProvider>,
);
const { container } = render(reduxWrapper(<IntlLoginFailureAlert {...props} />));

const expectedMessage = 'We couldn\'t sign you in. The username, email, or password you entered is incorrect. Please try again or reset your password.';

Expand All @@ -171,11 +161,7 @@ describe('LoginFailureAlert', () => {
errorCode: FORBIDDEN_REQUEST,
};

const { container } = render(
<IntlProvider locale="en">
<IntlLoginFailureAlert {...props} />
</IntlProvider>,
);
const { container } = render(reduxWrapper(<IntlLoginFailureAlert {...props} />));

const expectedMessage = 'We couldn\'t sign you in. Too many failed login attempts. Try again later.';

Expand All @@ -187,11 +173,7 @@ describe('LoginFailureAlert', () => {
errorCode: INTERNAL_SERVER_ERROR,
};

const { container } = render(
<IntlProvider locale="en">
<IntlLoginFailureAlert {...props} />
</IntlProvider>,
);
const { container } = render(reduxWrapper(<IntlLoginFailureAlert {...props} />));

const expectedMessage = 'We couldn\'t sign you in. An error has occurred. Try refreshing the page, or check your internet connection.';

Expand All @@ -203,11 +185,7 @@ describe('LoginFailureAlert', () => {
errorCode: INVALID_FORM,
};

const { container } = render(
<IntlProvider locale="en">
<IntlLoginFailureAlert {...props} />
</IntlProvider>,
);
const { container } = render(reduxWrapper(<IntlLoginFailureAlert {...props} />));

const expectedMessage = 'We couldn\'t sign you in. Please fill in the fields below.';
expect(container.querySelector('#login-failure-alert').textContent).toBe(expectedMessage);
Expand All @@ -228,11 +206,7 @@ describe('LoginFailureAlert', () => {
errorCode: ALLOWED_DOMAIN_LOGIN_ERROR,
};

const { container } = render(
<IntlProvider locale="en">
<IntlLoginFailureAlert {...props} />
</IntlProvider>,
);
const { container } = render(reduxWrapper(<IntlLoginFailureAlert {...props} />));

const errorMessage = "We couldn't sign you in. As test.com user, You must login with your test.com Google account.";
const url = 'http://localhost:18000/dashboard/?tpa_hint=google-auth2';
Expand All @@ -257,11 +231,7 @@ describe('LoginFailureAlert', () => {
errorCode: TPA_AUTHENTICATION_FAILURE,
};

const { container } = render(
<IntlProvider locale="en">
<IntlSSOFailureAlert {...props} />
</IntlProvider>,
);
const { container } = render(reduxWrapper(<IntlSSOFailureAlert {...props} />));

expect(container.querySelector('#SSO-failure-alert').textContent).toContain(errorMsg);
});
Expand Down
4 changes: 4 additions & 0 deletions src/forms/login-popup/index.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.login__btn-width {
min-width: 5.75rem !important;
}

.popup_login_form__inline_link-cursor {
cursor: pointer;
}
12 changes: 7 additions & 5 deletions src/forms/registration-popup/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -352,11 +352,13 @@ const RegistrationForm = () => {
</>
)}
</Container>
<div className="bg-dark-500">
<p className="mb-0 text-white m-auto authn-popup__registration-footer">
<HonorCodeAndPrivacyPolicyMessage />
</p>
</div>
{!(autoSubmitRegForm && !errorCode.type) && (
<div className="bg-dark-500">
<p className="mb-0 text-white m-auto authn-popup__registration-footer">
<HonorCodeAndPrivacyPolicyMessage />
</p>
</div>
)}
</div>
);
};
Expand Down
3 changes: 2 additions & 1 deletion src/forms/reset-password-popup/forgot-password/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,11 @@ const ForgotPasswordForm = () => {
destination={getConfig().LOGIN_ISSUE_SUPPORT_LINK}
linkHelpText={formatMessage(messages.resetPasswordFormNeedHelpText)}
linkText={formatMessage(messages.resetPasswordFormHelpCenterLink)}
targetBlank
/>
<InlineLink
className="font-weight-normal small"
destination={getConfig().INFO_EMAIL}
destination={`mailto:${getConfig().INFO_EMAIL}`}
linkHelpText={formatMessage(messages.resetPasswordFormAdditionalHelpText)}
linkText={getConfig().INFO_EMAIL}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ const ResetPasswordFailure = (props) => {

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

0 comments on commit 9e0cc78

Please sign in to comment.