From b894e3ce0c65e23a6a8d9230e47ad48ae5981d47 Mon Sep 17 00:00:00 2001 From: Zainab Amir Date: Fri, 22 Jan 2021 13:17:03 +0500 Subject: [PATCH] Add account activation messages (#82) Show messages based on the query paran recieved during the redirect after account activation link is clicked VAN-304 --- src/index.jsx | 1 + src/logistration/AccountActivationMessage.jsx | 63 +++++++++++++++++++ src/logistration/LoginFailure.jsx | 4 +- src/logistration/LoginPage.jsx | 9 ++- src/logistration/data/constants.js | 7 +++ src/logistration/messages.jsx | 26 ++++++++ .../tests/AccountActivationMessage.test.jsx | 57 +++++++++++++++++ src/logistration/tests/LoginPage.test.jsx | 16 ++++- .../__snapshots__/LoginFailure.test.jsx.snap | 3 +- 9 files changed, 178 insertions(+), 8 deletions(-) create mode 100644 src/logistration/AccountActivationMessage.jsx create mode 100644 src/logistration/tests/AccountActivationMessage.test.jsx diff --git a/src/index.jsx b/src/index.jsx index 5887abb0..576521f5 100755 --- a/src/index.jsx +++ b/src/index.jsx @@ -58,6 +58,7 @@ initialize({ config: () => { mergeConfig({ LOGIN_ISSUE_SUPPORT_LINK: process.env.LOGIN_ISSUE_SUPPORT_LINK || null, + ACTIVATION_EMAIL_SUPPORT_LINK: process.env.ACTIVATION_EMAIL_SUPPORT_LINK || null, }); }, }, diff --git a/src/logistration/AccountActivationMessage.jsx b/src/logistration/AccountActivationMessage.jsx new file mode 100644 index 00000000..91a53e1f --- /dev/null +++ b/src/logistration/AccountActivationMessage.jsx @@ -0,0 +1,63 @@ +import React from 'react'; + +import { getConfig } from '@edx/frontend-platform'; +import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n'; +import { Alert } from '@edx/paragon'; +import PropTypes from 'prop-types'; + +import { ACCOUNT_ACTIVATION_MESSAGE } from './data/constants'; +import messages from './messages'; + +const AccountActivationMessage = (props) => { + const { intl, messageType } = props; + const variant = messageType === ACCOUNT_ACTIVATION_MESSAGE.ERROR ? 'danger' : messageType; + + let activationMessage; + let heading; + + switch (messageType) { + case ACCOUNT_ACTIVATION_MESSAGE.SUCCESS: { + heading = intl.formatMessage(messages['authn.account.activation.success.message.title']); + activationMessage = intl.formatMessage(messages['authn.account.activation.success.message']); + break; + } + case ACCOUNT_ACTIVATION_MESSAGE.INFO: { + activationMessage = intl.formatMessage(messages['authn.account.already.activated.message']); + break; + } + case ACCOUNT_ACTIVATION_MESSAGE.ERROR: { + const supportLink = ( + + {intl.formatMessage(messages['authn.account.activation.support.link'])} + + ); + + heading = intl.formatMessage(messages['authn.account.activation.error.message.title']); + activationMessage = ( + + ); + break; + } + default: + break; + } + + return activationMessage ? ( + + {heading && {heading}} + {activationMessage} + + ) : null; +}; + +AccountActivationMessage.propTypes = { + messageType: PropTypes.string.isRequired, + intl: intlShape.isRequired, +}; + +export default injectIntl(AccountActivationMessage); diff --git a/src/logistration/LoginFailure.jsx b/src/logistration/LoginFailure.jsx index c30d2530..ddb241b7 100644 --- a/src/logistration/LoginFailure.jsx +++ b/src/logistration/LoginFailure.jsx @@ -1,7 +1,7 @@ import React from 'react'; import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n'; -import { Alert, Hyperlink } from '@edx/paragon'; +import { Alert } from '@edx/paragon'; import PropTypes from 'prop-types'; import { processLink } from '../data/utils/dataUtils'; @@ -82,7 +82,7 @@ const LoginFailureMessage = (props) => { return (
  • {beforeLink} - {linkText} + {linkText} {afterLink}
  • ); diff --git a/src/logistration/LoginPage.jsx b/src/logistration/LoginPage.jsx index 3295f140..2bd323b3 100644 --- a/src/logistration/LoginPage.jsx +++ b/src/logistration/LoginPage.jsx @@ -1,6 +1,7 @@ import React from 'react'; import Skeleton from 'react-loading-skeleton'; +import { getConfig } from '@edx/frontend-platform'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { Form, Hyperlink, Input, StatefulButton, ValidationFormGroup, @@ -8,7 +9,7 @@ import { import PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import { getConfig } from '@edx/frontend-platform'; +import AccountActivationMessage from './AccountActivationMessage'; import ConfirmationAlert from './ConfirmationAlert'; import { getThirdPartyAuthContext, loginRequest } from './data/actions'; import { loginErrorSelector, loginRequestSelector, thirdPartyAuthContextSelector } from './data/selectors'; @@ -16,10 +17,10 @@ import InstitutionLogistration, { RenderInstitutionButton } from './InstitutionL import LoginHelpLinks from './LoginHelpLinks'; import LoginFailureMessage from './LoginFailure'; import messages from './messages'; -import { RedirectLogistration } from '../common-components'; import SocialAuthProviders from './SocialAuthProviders'; import ThirdPartyAuthAlert from './ThirdPartyAuthAlert'; +import { RedirectLogistration } from '../common-components'; import { DEFAULT_REDIRECT_URL, DEFAULT_STATE, LOGIN_PAGE, REGISTER_PAGE, ENTERPRISE_LOGIN_URL, PENDING_STATE, } from '../data/constants'; @@ -147,6 +148,9 @@ class LoginPage extends React.Component { } = this.props; const { currentProvider, providers, secondaryProviders } = this.props.thirdPartyAuthContext; + const params = (new URL(window.location.href)).searchParams; + const activationMsgType = params.get('account_activation_status'); + if (this.state.institutionLogin) { return ( )} {this.props.loginError ? : null} + {activationMsgType && } {this.props.forgotPassword.status === 'complete' ? : null}

    diff --git a/src/logistration/data/constants.js b/src/logistration/data/constants.js index 342730dd..1f0c77f7 100644 --- a/src/logistration/data/constants.js +++ b/src/logistration/data/constants.js @@ -1,3 +1,10 @@ export const INACTIVE_USER = 'inactive-user'; export const INTERNAL_SERVER_ERROR = 'internal-server-error'; export const NON_COMPLIANT_PASSWORD_EXCEPTION = 'NonCompliantPasswordException'; + +// Account Activation Message +export const ACCOUNT_ACTIVATION_MESSAGE = { + INFO: 'info', + SUCCESS: 'success', + ERROR: 'error', +}; diff --git a/src/logistration/messages.jsx b/src/logistration/messages.jsx index d1ca6dec..b6ddbe2e 100644 --- a/src/logistration/messages.jsx +++ b/src/logistration/messages.jsx @@ -216,6 +216,32 @@ const messages = defineMessages({ defaultMessage: 'Sign in with your company or school', description: 'Company or school login link text.', }, + // Account Activation Strings + 'authn.account.activation.success.message.title': { + id: 'authn.account.activation.success.message.title', + defaultMessage: 'Success! You have activated your account.', + description: 'Account Activation success message title', + }, + 'authn.account.activation.success.message': { + id: 'authn.account.activation.success.message', + defaultMessage: 'You will now receive email updates and alerts from us related to the courses you are enrolled in. Sign In to continue.', + description: 'Message show to learners when their account has been activated successfully', + }, + 'authn.account.already.activated.message': { + id: 'authn.account.already.activated.message', + defaultMessage: 'This account has already been activated.', + description: 'Message shown when learner account has already been activated', + }, + 'authn.account.activation.error.message.title': { + id: 'authn.account.activation.error.message.title', + defaultMessage: 'Your account could not be activated', + description: 'Account Activation error message title', + }, + 'authn.account.activation.support.link': { + id: 'authn.account.activation.support.link', + defaultMessage: 'contact support', + description: 'Link text used in account activation error message to go to learner help center', + }, }); export default messages; diff --git a/src/logistration/tests/AccountActivationMessage.test.jsx b/src/logistration/tests/AccountActivationMessage.test.jsx new file mode 100644 index 00000000..be0b5bbb --- /dev/null +++ b/src/logistration/tests/AccountActivationMessage.test.jsx @@ -0,0 +1,57 @@ +import React from 'react'; +import { mount } from 'enzyme'; + +import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n'; + +import AccountActivationMessage from '../AccountActivationMessage'; +import { ACCOUNT_ACTIVATION_MESSAGE } from '../data/constants'; + +const IntlAccountActivationMessage = injectIntl(AccountActivationMessage); + +describe('AccountActivationMessage', () => { + it('should match account already activated message', () => { + const accountActivationMessage = mount( + + + , + ); + + const expectedMessage = 'This account has already been activated.'; + expect(accountActivationMessage.find('#account-activation-message').find('div').text()).toEqual(expectedMessage); + }); + + it('should match account activated success message', () => { + const accountActivationMessage = mount( + + + , + ); + + const expectedMessage = 'Success! You have activated your account.' + + 'You will now receive email updates and alerts from us related to ' + + 'the courses you are enrolled in. Sign In to continue.'; + expect(accountActivationMessage.find('#account-activation-message').first().text()).toEqual(expectedMessage); + }); + + it('should match account activation error message', () => { + const accountActivationMessage = mount( + + + , + ); + + const expectedMessage = 'Your account could not be activated' + + 'Something went wrong, please contact support to resolve this issue.'; + expect(accountActivationMessage.find('#account-activation-message').first().text()).toEqual(expectedMessage); + }); + + it('should not display anything for invalid message type', () => { + const accountActivationMessage = mount( + + + , + ); + + expect(accountActivationMessage).toEqual({}); + }); +}); diff --git a/src/logistration/tests/LoginPage.test.jsx b/src/logistration/tests/LoginPage.test.jsx index 02e6cbad..657a1653 100644 --- a/src/logistration/tests/LoginPage.test.jsx +++ b/src/logistration/tests/LoginPage.test.jsx @@ -117,6 +117,16 @@ describe('LoginPage', () => { expect(tree).toMatchSnapshot(); }); + it('should show account activation message', () => { + delete window.location; + window.location = { href: getConfig().BASE_URL.concat('/login?account_activation_status=info') }; + + const expectedMessage = 'This account has already been activated.'; + + const loginPage = mount(reduxWrapper()); + expect(loginPage.find('#account-activation-message').find('div').text()).toEqual(expectedMessage); + }); + it('should display login help button', () => { const root = mount(reduxWrapper()); expect(root.find('button.field-link').first().text()).toEqual('Need help signing in?'); @@ -154,7 +164,7 @@ describe('LoginPage', () => { }, }); delete window.location; - window.location = { href: '' }; + window.location = { href: getConfig().BASE_URL }; renderer.create(reduxWrapper()); expect(window.location.href).toBe(dasboardUrl); }); @@ -177,7 +187,7 @@ describe('LoginPage', () => { }); delete window.location; - window.location = { href: '' }; + window.location = { href: getConfig().BASE_URL }; renderer.create(reduxWrapper()); expect(window.location.href).toBe(getConfig().LMS_BASE_URL + authCompleteUrl); }); @@ -199,7 +209,7 @@ describe('LoginPage', () => { }); delete window.location; - window.location = { href: '' }; + window.location = { href: getConfig().BASE_URL }; const loginPage = mount(reduxWrapper()); diff --git a/src/logistration/tests/__snapshots__/LoginFailure.test.jsx.snap b/src/logistration/tests/__snapshots__/LoginFailure.test.jsx.snap index 527de7cb..d12bdfa1 100644 --- a/src/logistration/tests/__snapshots__/LoginFailure.test.jsx.snap +++ b/src/logistration/tests/__snapshots__/LoginFailure.test.jsx.snap @@ -36,9 +36,10 @@ exports[`LoginFailureMessage should match error message containing link snapshot

  • To be on the safe side, you can reset your password here