diff --git a/src/logistration/LoginFailure.jsx b/src/logistration/LoginFailure.jsx index f1d4d393..03be8952 100644 --- a/src/logistration/LoginFailure.jsx +++ b/src/logistration/LoginFailure.jsx @@ -1,8 +1,11 @@ import React from 'react'; -import PropTypes from 'prop-types'; -import { FormattedMessage } from '@edx/frontend-platform/i18n'; +import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { Alert, Hyperlink } from '@edx/paragon'; +import PropTypes from 'prop-types'; + +import { NON_COMPLIANT_PASSWORD_EXCEPTION } from './data/constants'; +import messages from './messages'; const processLink = (link) => { let matches; @@ -13,6 +16,9 @@ const processLink = (link) => { }; const LoginFailureMessage = (props) => { + const errorMessage = props.errors; + const { errorCode, intl } = props; + const Link = (args) => ( <> {args.beforeLink} @@ -23,26 +29,47 @@ const LoginFailureMessage = (props) => { > ); - const errorMessage = props.errors; - let errorList = errorMessage.trim().split('\n'); - errorList = errorList.map((error) => { - let matches; - if (error.includes('a href')) { - matches = processLink(error); - const [beforeLink, link, linkText, afterLink] = matches; - return ( -
@@ -250,7 +252,10 @@ LoginPage.propTypes = {
}),
getThirdPartyAuthContext: PropTypes.func.isRequired,
intl: intlShape.isRequired,
- loginError: PropTypes.string,
+ loginError: PropTypes.shape({
+ value: PropTypes.string,
+ errorCode: PropTypes.string,
+ }),
loginRequest: PropTypes.func.isRequired,
loginResult: PropTypes.shape({
redirectUrl: PropTypes.string,
@@ -270,10 +275,11 @@ const mapStateToProps = state => {
const forgotPassword = forgotPasswordResultSelector(state);
const loginResult = loginRequestSelector(state);
const thirdPartyAuthContext = thirdPartyAuthContextSelector(state);
+ const loginError = loginErrorSelector(state);
return {
- loginError: state.logistration.loginError,
submitState: state.logistration.submitState,
forgotPassword,
+ loginError,
loginResult,
thirdPartyAuthContext,
};
diff --git a/src/logistration/_style.scss b/src/logistration/_style.scss
index e0ce7463..dee24c65 100644
--- a/src/logistration/_style.scss
+++ b/src/logistration/_style.scss
@@ -2,6 +2,7 @@
// #COLORS
// ----------------------------
$link-blue: #23419f;
+$mfe-font-color: #23419f;
$font-blue: #126f9a;
$white: #FFFFFF;
@@ -16,7 +17,7 @@ $apple-black: #000000;
$apple-focus-black: $apple-black;
.font-color {
- color: $font-blue;
+ color: $mfe-font-color;
}
.login-container {
diff --git a/src/logistration/data/constants.js b/src/logistration/data/constants.js
new file mode 100644
index 00000000..2d49df51
--- /dev/null
+++ b/src/logistration/data/constants.js
@@ -0,0 +1,2 @@
+// eslint-disable-next-line import/prefer-default-export
+export const NON_COMPLIANT_PASSWORD_EXCEPTION = 'NonCompliantPasswordException';
diff --git a/src/logistration/data/sagas.js b/src/logistration/data/sagas.js
index fedd9b7f..f57713f7 100644
--- a/src/logistration/data/sagas.js
+++ b/src/logistration/data/sagas.js
@@ -1,5 +1,7 @@
import { call, put, takeEvery } from 'redux-saga/effects';
+import { camelCaseObject } from '@edx/frontend-platform';
+
// Actions
import {
REGISTER_NEW_USER,
@@ -51,7 +53,7 @@ export function* handleLoginRequest(action) {
} catch (e) {
const statusCodes = [400];
if (e.response && statusCodes.includes(e.response.status)) {
- yield put(loginRequestFailure(e.response.data.value));
+ yield put(loginRequestFailure(camelCaseObject(e.response.data)));
}
}
}
diff --git a/src/logistration/data/selectors.js b/src/logistration/data/selectors.js
index 4dc7d718..13763bc1 100644
--- a/src/logistration/data/selectors.js
+++ b/src/logistration/data/selectors.js
@@ -9,6 +9,11 @@ export const loginRequestSelector = createSelector(
logistration => logistration.loginResult,
);
+export const loginErrorSelector = createSelector(
+ logistrationSelector,
+ logistration => logistration.loginError,
+);
+
export const registrationRequestSelector = createSelector(
logistrationSelector,
logistration => logistration.registrationResult,
diff --git a/src/logistration/messages.jsx b/src/logistration/messages.jsx
index 3d442448..8b18c99a 100644
--- a/src/logistration/messages.jsx
+++ b/src/logistration/messages.jsx
@@ -100,7 +100,12 @@ const messages = defineMessages({
'logistration.login.institution.login.sign.in.with': {
id: 'logistration.login.institution.login.sign.in.with',
defaultMessage: 'or sign in with',
- description: 'gives hint about other sign options ',
+ description: 'gives hint about other sign options',
+ },
+ 'logistration.non.compliant.password.title': {
+ id: 'logistration.non.compliant.password.title',
+ defaultMessage: 'We recently changed our password requirements',
+ description: 'A title that appears in bold before error message for non-compliant password',
},
});
diff --git a/src/logistration/tests/LoginFailure.test.jsx b/src/logistration/tests/LoginFailure.test.jsx
new file mode 100644
index 00000000..2135318a
--- /dev/null
+++ b/src/logistration/tests/LoginFailure.test.jsx
@@ -0,0 +1,41 @@
+import React from 'react';
+import renderer from 'react-test-renderer';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+
+import LoginFailureMessage from '../LoginFailure';
+import { NON_COMPLIANT_PASSWORD_EXCEPTION } from '../data/constants';
+
+describe('LoginFailureMessage', () => {
+ let props = {};
+
+ beforeEach(() => {
+ props = {
+ errors: 'Some error occurred logging you in.',
+ };
+ });
+
+ it('should match non compliant password error message snapshot', () => {
+ props = {
+ ...props,
+ errorCode: NON_COMPLIANT_PASSWORD_EXCEPTION,
+ };
+
+ const tree = renderer.create(
+