refactor: remove legacy code and move redesign code to src
VAN-670
This commit is contained in:
@@ -23,11 +23,10 @@ AUTHN_MINIMAL_HEADER=true
|
||||
LOGIN_ISSUE_SUPPORT_LINK='/login-issue-support-url'
|
||||
TOS_AND_HONOR_CODE='http://localhost:18000/honor'
|
||||
PRIVACY_POLICY='http://localhost:18000/privacy'
|
||||
REGISTRATION_OPTIONAL_FIELDS='gender,goals,level_of_education,year_of_birth'
|
||||
REGISTRATION_OPTIONAL_FIELDS=''
|
||||
USER_SURVEY_COOKIE_NAME='openedx-user-survey-type'
|
||||
COOKIE_DOMAIN='localhost'
|
||||
WELCOME_PAGE_SUPPORT_LINK='http://localhost:1999/welcome'
|
||||
INFO_EMAIL='info@edx.org'
|
||||
DISABLE_ENTERPRISE_LOGIN=''
|
||||
DEFAULT_DESIGN='redesign'
|
||||
REGISTER_CONVERSION_COOKIE_NAME='openedx-user-register-conversion'
|
||||
|
||||
2
Makefile
2
Makefile
@@ -2,7 +2,7 @@ transifex_resource = frontend-app-authn
|
||||
transifex_langs = "ar,fr,es_419,zh_CN"
|
||||
|
||||
transifex_utils = ./node_modules/.bin/transifex-utils.js
|
||||
i18n = ./src/legacy/i18n
|
||||
i18n = ./src/i18n
|
||||
transifex_input = $(i18n)/transifex_input.json
|
||||
tx_url1 = https://www.transifex.com/api/2/project/edx-platform/resource/$(transifex_resource)/translation/en/strings/
|
||||
tx_url2 = https://www.transifex.com/api/2/project/edx-platform/resource/$(transifex_resource)/source/
|
||||
|
||||
@@ -6,10 +6,7 @@ module.exports = createConfig('jest', {
|
||||
],
|
||||
coveragePathIgnorePatterns: [
|
||||
'src/setupTest.js',
|
||||
'src/legacy/i18n',
|
||||
'src/redesign/i18n',
|
||||
'src/i18n',
|
||||
'src/index.jsx',
|
||||
'src/legacy/index.jsx',
|
||||
'src/redesign/index.jsx',
|
||||
],
|
||||
});
|
||||
|
||||
37
package-lock.json
generated
37
package-lock.json
generated
@@ -3869,29 +3869,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@edx/frontend-component-header": {
|
||||
"version": "2.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-component-header/-/frontend-component-header-2.2.5.tgz",
|
||||
"integrity": "sha512-8ZfdQBgp5beLd3r+xIxpDQT4/eYggUrzkHe4iJk322lYqmjAEHio9jrLnHv7lW/B8ooH3qZNl+sJlT2UcWRxmg==",
|
||||
"requires": {
|
||||
"babel-polyfill": "6.26.0",
|
||||
"react-responsive": "8.0.3",
|
||||
"react-transition-group": "4.3.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"react-responsive": {
|
||||
"version": "8.0.3",
|
||||
"resolved": "https://registry.npmjs.org/react-responsive/-/react-responsive-8.0.3.tgz",
|
||||
"integrity": "sha512-F9VXyLao7O8XHXbLjQbIr4+mC6Zr0RDTwNjd7ixTmYEAyKyNanBkLkFchNaMZgszoSK6PgSs/3m/QDWw33/gpg==",
|
||||
"requires": {
|
||||
"hyphenate-style-name": "^1.0.0",
|
||||
"matchmediaquery": "^0.3.0",
|
||||
"prop-types": "^15.6.1",
|
||||
"shallow-equal": "^1.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@edx/frontend-platform": {
|
||||
"version": "1.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-1.8.4.tgz",
|
||||
@@ -7273,6 +7250,7 @@
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-polyfill/-/babel-polyfill-6.26.0.tgz",
|
||||
"integrity": "sha1-N5k3q8Z9eJWXCtxiHyhM2WbPIVM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-runtime": "^6.26.0",
|
||||
"core-js": "^2.5.0",
|
||||
@@ -7282,12 +7260,14 @@
|
||||
"core-js": {
|
||||
"version": "2.6.12",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
|
||||
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="
|
||||
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
|
||||
"dev": true
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.10.5",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz",
|
||||
"integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg="
|
||||
"integrity": "sha1-M2w+/BIgrc7dosn6tntaeVWjNlg=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -7355,6 +7335,7 @@
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
|
||||
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"core-js": "^2.4.0",
|
||||
"regenerator-runtime": "^0.11.0"
|
||||
@@ -7363,12 +7344,14 @@
|
||||
"core-js": {
|
||||
"version": "2.6.12",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz",
|
||||
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="
|
||||
"integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
|
||||
"dev": true
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.11.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
|
||||
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
|
||||
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
],
|
||||
"scripts": {
|
||||
"build": "fedx-scripts webpack",
|
||||
"i18n_extract": "BABEL_ENV=i18n fedx-scripts babel src/legacy --quiet > /dev/null",
|
||||
"i18n_extract": "BABEL_ENV=i18n fedx-scripts babel src --quiet > /dev/null",
|
||||
"is-es5": "es-check es5 ./dist/*.js",
|
||||
"lint": "fedx-scripts eslint --ext .js --ext .jsx .",
|
||||
"snapshot": "fedx-scripts jest --updateSnapshot",
|
||||
@@ -36,7 +36,6 @@
|
||||
"dependencies": {
|
||||
"@edx/brand": "npm:@edx/brand-openedx@1.1.0",
|
||||
"@edx/frontend-component-cookie-policy-banner": "2.1.12",
|
||||
"@edx/frontend-component-header": "2.2.5",
|
||||
"@edx/frontend-platform": "1.8.4",
|
||||
"@edx/paragon": "16.6.1",
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.32",
|
||||
|
||||
@@ -19,7 +19,7 @@ import './index.scss';
|
||||
|
||||
registerIcons();
|
||||
|
||||
const RedesignApp = () => (
|
||||
const MainApp = () => (
|
||||
<AppProvider store={configureStore()}>
|
||||
<BaseComponent>
|
||||
<Switch>
|
||||
@@ -40,4 +40,4 @@ const RedesignApp = () => (
|
||||
</AppProvider>
|
||||
);
|
||||
|
||||
export default RedesignApp;
|
||||
export default MainApp;
|
||||
@@ -1,9 +1,7 @@
|
||||
import React, { useEffect } from 'react';
|
||||
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { history } from '@edx/frontend-platform';
|
||||
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
|
||||
import {
|
||||
ExtraSmall, Small, Medium, Large, ExtraLarge, ExtraExtraLarge,
|
||||
@@ -18,18 +16,6 @@ import AuthMediumLayout from './AuthMediumLayout';
|
||||
import AuthSmallLayout from './AuthSmallLayout';
|
||||
|
||||
const BaseComponent = ({ children }) => {
|
||||
let { search } = history.location;
|
||||
|
||||
if (search && search.indexOf('theme') < 0) {
|
||||
search = `${search}&theme=redesign`;
|
||||
} else {
|
||||
search = '?theme=redesign';
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
history.replace({ search });
|
||||
}, []);
|
||||
|
||||
const authenticatedUser = getAuthenticatedUser();
|
||||
|
||||
return (
|
||||
@@ -3,7 +3,7 @@ import { runSaga } from 'redux-saga';
|
||||
import * as actions from '../actions';
|
||||
import { fetchThirdPartyAuthContext } from '../sagas';
|
||||
import * as api from '../service';
|
||||
import initializeMockLogging from '../../../../setupTest';
|
||||
import initializeMockLogging from '../../../setupTest';
|
||||
|
||||
const { loggingService } = initializeMockLogging();
|
||||
|
||||
@@ -3,7 +3,7 @@ import { runSaga } from 'redux-saga';
|
||||
import * as actions from '../actions';
|
||||
import { handleForgotPassword } from '../sagas';
|
||||
import * as api from '../service';
|
||||
import initializeMockLogging from '../../../../setupTest';
|
||||
import initializeMockLogging from '../../../setupTest';
|
||||
|
||||
const { loggingService } = initializeMockLogging();
|
||||
|
||||
@@ -8,30 +8,12 @@ import { ErrorPage } from '@edx/frontend-platform/react';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import { messages as headerMessages } from '@edx/frontend-component-header';
|
||||
|
||||
const RedesignApp = React.lazy(() => import('./redesign/index.jsx'));
|
||||
const LegacyApp = React.lazy(() => import('./legacy/index.jsx'));
|
||||
|
||||
const redesignAppMessages = React.lazy(() => import('./redesign/i18n'));
|
||||
const legacyAppMessages = React.lazy(() => import('./legacy/i18n'));
|
||||
|
||||
const OLD_DESIGN = 'legacy';
|
||||
const NEW_DESIGN = 'redesign';
|
||||
const DEFAULT_DESIGN = process.env.DEFAULT_DESIGN || OLD_DESIGN;
|
||||
const CHOSEN_DESIGN = localStorage.getItem('DESIGN_NAME') || DEFAULT_DESIGN;
|
||||
const REGISTRATION_OPTIONAL_FIELDS = CHOSEN_DESIGN === DEFAULT_DESIGN ? process.env.REGISTRATION_OPTIONAL_FIELDS : '';
|
||||
|
||||
const AppSelector = () => (
|
||||
<React.Suspense fallback={<></>}>
|
||||
{(CHOSEN_DESIGN === OLD_DESIGN) && <LegacyApp />}
|
||||
{(CHOSEN_DESIGN === NEW_DESIGN) && <RedesignApp />}
|
||||
</React.Suspense>
|
||||
);
|
||||
import MainApp from './MainApp';
|
||||
import messages from './i18n';
|
||||
|
||||
subscribe(APP_READY, () => {
|
||||
ReactDOM.render(
|
||||
<AppSelector />,
|
||||
<MainApp />,
|
||||
document.getElementById('root'),
|
||||
);
|
||||
});
|
||||
@@ -49,20 +31,18 @@ initialize({
|
||||
PASSWORD_RESET_SUPPORT_LINK: process.env.PASSWORD_RESET_SUPPORT_LINK || null,
|
||||
TOS_AND_HONOR_CODE: process.env.TOS_AND_HONOR_CODE || null,
|
||||
PRIVACY_POLICY: process.env.PRIVACY_POLICY || null,
|
||||
REGISTRATION_OPTIONAL_FIELDS,
|
||||
REGISTRATION_OPTIONAL_FIELDS: process.env.REGISTRATION_OPTIONAL_FIELDS || '',
|
||||
USER_SURVEY_COOKIE_NAME: process.env.USER_SURVEY_COOKIE_NAME || null,
|
||||
COOKIE_DOMAIN: process.env.COOKIE_DOMAIN,
|
||||
WELCOME_PAGE_SUPPORT_LINK: process.env.WELCOME_PAGE_SUPPORT_LINK || null,
|
||||
DISABLE_ENTERPRISE_LOGIN: process.env.DISABLE_ENTERPRISE_LOGIN || '',
|
||||
INFO_EMAIL: process.env.INFO_EMAIL || '',
|
||||
REGISTER_CONVERSION_COOKIE_NAME: process.env.REGISTER_CONVERSION_COOKIE_NAME || null,
|
||||
DESIGN_NAME: CHOSEN_DESIGN,
|
||||
ENABLE_PROGRESSIVE_PROFILING: process.env.ENABLE_PROGRESSIVE_PROFILING || false,
|
||||
});
|
||||
},
|
||||
},
|
||||
messages: [
|
||||
CHOSEN_DESIGN === DEFAULT_DESIGN ? legacyAppMessages : redesignAppMessages,
|
||||
headerMessages,
|
||||
messages,
|
||||
],
|
||||
});
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
@import "~@edx/paragon/scss/core/core";
|
||||
@import "~@edx/brand/paragon/overrides";
|
||||
|
||||
@import "~@edx/frontend-component-header/dist/index";
|
||||
|
||||
@import '@edx/frontend-component-cookie-policy-banner/build/_cookie-policy-banner';
|
||||
|
||||
@import "./style";
|
||||
@@ -1,340 +0,0 @@
|
||||
// ----------------------------
|
||||
// #COLORS
|
||||
// ----------------------------
|
||||
$font-blue: #126f9a;
|
||||
$white: #FFFFFF;
|
||||
|
||||
// social platforms
|
||||
$facebook-blue: #1877F2;
|
||||
$facebook-focus-blue: #29487d;
|
||||
$google-blue: #4285f4;
|
||||
$google-focus-blue: #287ae6;
|
||||
$microsoft-black: #2f2f2f;
|
||||
$microsoft-focus-black: #000;
|
||||
$apple-black: #000000;
|
||||
$apple-focus-black: $apple-black;
|
||||
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
left: -10000px;
|
||||
top: auto;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.focus-out {
|
||||
position: absolute;
|
||||
padding-left: 17px;
|
||||
opacity: 0.75;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.alert-link {
|
||||
font-weight: normal;
|
||||
text-decoration: underline;
|
||||
color: #0075b4 !important;
|
||||
|
||||
&:hover {
|
||||
color: #065683 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.authn-header {
|
||||
border-bottom: 1px solid #e7e7e7;
|
||||
height: 3.75rem;
|
||||
position: relative;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.authn-header img {
|
||||
height: 1.75rem;
|
||||
margin-left: 2rem;
|
||||
padding: 1rem 0;
|
||||
display: block;
|
||||
position: relative;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
.btn-social {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
|
||||
margin-bottom: 1rem;
|
||||
font-size: 14px;
|
||||
|
||||
background-color: $white;
|
||||
border: 1px solid $font-blue;
|
||||
width: 242px;
|
||||
height: 36px;
|
||||
color: $font-blue;
|
||||
|
||||
.icon-image {
|
||||
background-color: transparent;
|
||||
max-height: 24px;
|
||||
max-width: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-tpa {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
padding-left: 20px;
|
||||
|
||||
.icon-image {
|
||||
background-color: transparent;
|
||||
max-height: 24px;
|
||||
max-width: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.tpa-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.font-container {
|
||||
background-color: $font-blue;
|
||||
color: $white;
|
||||
font-size: 11px;
|
||||
|
||||
margin-left: -6px;
|
||||
padding-top: 10px;
|
||||
min-width: 30px;
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
.btn-oa2-facebook {
|
||||
color: $white;
|
||||
border-color: $facebook-blue;
|
||||
background-color: $facebook-blue;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: $facebook-focus-blue;
|
||||
border: 1px solid $facebook-focus-blue;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-oa2-google-oauth2 {
|
||||
color: $white;
|
||||
border-color: $google-blue;
|
||||
background-color: $google-blue;
|
||||
|
||||
.icon-image {
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: $google-focus-blue;
|
||||
border: 1px solid $google-focus-blue;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-oa2-apple-id {
|
||||
color: $white;
|
||||
border-color: $apple-black;
|
||||
background-color: $apple-black;
|
||||
font-size: 16px;
|
||||
|
||||
.icon-image {
|
||||
max-height: 1.8em;
|
||||
max-width: 2.0em;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: $apple-focus-black;
|
||||
border: 1px solid $apple-focus-black;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-oa2-azuread-oauth2 {
|
||||
color: $white;
|
||||
border-color: $microsoft-black;
|
||||
background-color: $microsoft-black;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: $microsoft-focus-black;
|
||||
border: 1px solid $microsoft-focus-black;
|
||||
color: $white;
|
||||
}
|
||||
}
|
||||
|
||||
.submit {
|
||||
display: inherit;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.section-heading-line {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
|
||||
&:before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
width: 20%;
|
||||
background-color: gray;
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
width: 20%;
|
||||
background-color: gray;
|
||||
height: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.field-link {
|
||||
font-weight: normal;
|
||||
display: block;
|
||||
color: $primary;
|
||||
margin-bottom: 5px;
|
||||
margin-top: 5px;
|
||||
border: none;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
text-transform: initial;
|
||||
letter-spacing: normal;
|
||||
text-decoration: none;
|
||||
text-shadow: none;
|
||||
|
||||
&:focus,
|
||||
&:hover {
|
||||
color: $primary;
|
||||
}
|
||||
}
|
||||
|
||||
.login-help {
|
||||
padding-left: 14px;
|
||||
}
|
||||
|
||||
.opt-inline-field {
|
||||
display: inline-block;
|
||||
width: 50%;
|
||||
|
||||
.form-control {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.opt-year-field {
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
.invalid-feedback {
|
||||
color: $red;
|
||||
}
|
||||
|
||||
.full-vertical-height {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.help-links {
|
||||
margin-left: -5px;
|
||||
}
|
||||
|
||||
#honor-code p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#honor-code a span {
|
||||
@extend .sr-only;
|
||||
}
|
||||
|
||||
.mw-420 {
|
||||
max-width: 420px;
|
||||
}
|
||||
|
||||
.mw-500 {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.mw-32em {
|
||||
max-width: 32em;
|
||||
}
|
||||
|
||||
.h-90 {
|
||||
height: 90%;
|
||||
}
|
||||
|
||||
.mt-10 {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.pt-10 {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
@media (min-width: 576px) {
|
||||
.reset-password-container {
|
||||
width: 420px;
|
||||
max-width: 420px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.mw-500 {
|
||||
width: 500px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.btn-social {
|
||||
width: 47%;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.tpa-container {
|
||||
justify-content: center;
|
||||
}
|
||||
.form-control {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 450px) {
|
||||
.section-heading-line {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
width: 10%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.custom-select-size {
|
||||
background-size: 8px 10px;
|
||||
}
|
||||
|
||||
.x-small-label {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Alert } from '@edx/paragon';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import PropTypes from 'prop-types';
|
||||
import messages from './messages';
|
||||
import { API_RATELIMIT_ERROR, INTERNAL_SERVER_ERROR } from '../data/constants';
|
||||
|
||||
const APIFailureMessage = (props) => {
|
||||
const { intl, header, errorCode } = props;
|
||||
let errorMessage = null;
|
||||
let id = null;
|
||||
|
||||
switch (errorCode) {
|
||||
case INTERNAL_SERVER_ERROR:
|
||||
id = INTERNAL_SERVER_ERROR;
|
||||
errorMessage = intl.formatMessage(messages['internal.server.error.message']);
|
||||
break;
|
||||
case API_RATELIMIT_ERROR:
|
||||
id = API_RATELIMIT_ERROR;
|
||||
errorMessage = intl.formatMessage(messages['server.ratelimit.error.message']);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="d-flex justify-content-center m-4">
|
||||
<div className="d-flex flex-column mw-500">
|
||||
<Alert id={id} variant="danger">
|
||||
<Alert.Heading>
|
||||
{header}
|
||||
</Alert.Heading>
|
||||
<ul>
|
||||
<li key={errorMessage}>
|
||||
{errorMessage}
|
||||
</li>
|
||||
</ul>
|
||||
</Alert>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
APIFailureMessage.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
header: PropTypes.string.isRequired,
|
||||
errorCode: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(APIFailureMessage);
|
||||
@@ -1,166 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Form,
|
||||
Input,
|
||||
ValidationFormGroup,
|
||||
} from '@edx/paragon';
|
||||
|
||||
const AuthnCustomValidationFormGroup = (props) => {
|
||||
const {
|
||||
onBlur, onChange, onClick, onFocus,
|
||||
} = props;
|
||||
const [showHelpText, setShowHelpText] = useState(false);
|
||||
const [showLabelText, setShowLabelText] = useState(false);
|
||||
|
||||
// handler code that need to be invoked via input
|
||||
const onClickHandler = (e, clickCb) => {
|
||||
setShowHelpText(true);
|
||||
setShowLabelText(true);
|
||||
if (clickCb) {
|
||||
clickCb(e);
|
||||
}
|
||||
};
|
||||
const onBlurHandler = (e, blurCb) => {
|
||||
setShowHelpText(false);
|
||||
setShowLabelText(false);
|
||||
if (blurCb) {
|
||||
blurCb(e);
|
||||
}
|
||||
};
|
||||
const onChangeHandler = (e, changeCb) => {
|
||||
if (changeCb) {
|
||||
changeCb(e);
|
||||
}
|
||||
};
|
||||
const onFocusHandler = (e, focusCb) => {
|
||||
if (focusCb) {
|
||||
focusCb(e);
|
||||
}
|
||||
};
|
||||
const onOptionalHandler = (e, clickCb) => { clickCb(e); };
|
||||
|
||||
const showLabel = () => {
|
||||
let className;
|
||||
if (props.optionalFieldCheckbox || (!showLabelText && (props.value !== '' || props.type === 'select'))) {
|
||||
className = 'sr-only';
|
||||
} else if (showLabelText) {
|
||||
className = 'pt-10 x-small-label';
|
||||
} else {
|
||||
className = 'pt-10 focus-out';
|
||||
}
|
||||
|
||||
return (
|
||||
<Form.Label htmlFor={props.for} className={className}>{props.label}</Form.Label>
|
||||
);
|
||||
};
|
||||
const showOptional = () => {
|
||||
const additionalField = props.optionalFieldCheckbox ? (
|
||||
<p role="presentation" id="additionalFields" className="mb-1 small" onClick={(e) => onOptionalHandler(e, onClick)}>
|
||||
{props.checkboxMessage}
|
||||
</p>
|
||||
) : <span />;
|
||||
return additionalField;
|
||||
};
|
||||
|
||||
const inputProps = {
|
||||
name: props.name,
|
||||
id: props.for,
|
||||
type: props.type,
|
||||
value: props.value,
|
||||
className: props.inputFieldStyle,
|
||||
'aria-invalid': props.ariaInvalid,
|
||||
autoComplete: 'on',
|
||||
};
|
||||
inputProps.onChange = (e) => onChangeHandler(e, onChange);
|
||||
inputProps.onClick = (e) => onClickHandler(e, onClick);
|
||||
inputProps.onBlur = (e) => onBlurHandler(e, onBlur);
|
||||
inputProps.onFocus = (e) => onFocusHandler(e, onFocus);
|
||||
|
||||
if (props.type === 'select') {
|
||||
inputProps.options = props.selectOptions;
|
||||
inputProps.className = props.value === '' ? `${props.inputFieldStyle} text-muted` : props.inputFieldStyle;
|
||||
}
|
||||
if (props.type === 'checkbox') {
|
||||
inputProps.checked = props.isChecked;
|
||||
}
|
||||
|
||||
const validationGroupProps = {
|
||||
for: props.for,
|
||||
};
|
||||
if (!props.optionalFieldCheckbox) {
|
||||
validationGroupProps.invalid = props.invalid;
|
||||
validationGroupProps.invalidMessage = props.invalidMessage;
|
||||
validationGroupProps.helpText = showHelpText ? props.helpText : '';
|
||||
} else {
|
||||
validationGroupProps.className = props.optionalFieldCheckbox ? 'custom-control pt-10 mb-0' : '';
|
||||
}
|
||||
if (props.className) {
|
||||
validationGroupProps.className = props.className;
|
||||
}
|
||||
|
||||
return (
|
||||
<ValidationFormGroup
|
||||
{...validationGroupProps}
|
||||
>
|
||||
{showLabel()}
|
||||
<Input
|
||||
{...inputProps}
|
||||
required
|
||||
/>
|
||||
{showOptional()}
|
||||
</ValidationFormGroup>
|
||||
);
|
||||
};
|
||||
|
||||
AuthnCustomValidationFormGroup.defaultProps = {
|
||||
name: '',
|
||||
for: '',
|
||||
label: '',
|
||||
optionalFieldCheckbox: false,
|
||||
type: '',
|
||||
value: '',
|
||||
invalid: false,
|
||||
ariaInvalid: false,
|
||||
invalidMessage: '',
|
||||
inputFieldStyle: '',
|
||||
helpText: '',
|
||||
className: '',
|
||||
onClick: null,
|
||||
onBlur: null,
|
||||
onChange: null,
|
||||
onFocus: null,
|
||||
isChecked: false,
|
||||
checkboxMessage: '',
|
||||
selectOptions: null,
|
||||
};
|
||||
|
||||
AuthnCustomValidationFormGroup.propTypes = {
|
||||
name: PropTypes.string,
|
||||
for: PropTypes.string,
|
||||
label: PropTypes.string,
|
||||
type: PropTypes.string,
|
||||
value: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.bool,
|
||||
]),
|
||||
invalid: PropTypes.bool,
|
||||
ariaInvalid: PropTypes.bool,
|
||||
invalidMessage: PropTypes.string,
|
||||
helpText: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
inputFieldStyle: PropTypes.string,
|
||||
isChecked: PropTypes.bool,
|
||||
optionalFieldCheckbox: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
onBlur: PropTypes.func,
|
||||
onChange: PropTypes.func,
|
||||
onFocus: PropTypes.func,
|
||||
checkboxMessage: PropTypes.string,
|
||||
selectOptions: PropTypes.arrayOf(PropTypes.shape({
|
||||
key: PropTypes.string,
|
||||
value: PropTypes.string,
|
||||
})),
|
||||
};
|
||||
|
||||
export default AuthnCustomValidationFormGroup;
|
||||
@@ -1,49 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Alert } from '@edx/paragon';
|
||||
|
||||
import messages from './messages';
|
||||
|
||||
const ConfirmationAlert = (props) => {
|
||||
const { email, intl } = props;
|
||||
|
||||
return (
|
||||
<Alert id="confirmation-alert" variant="success">
|
||||
<Alert.Heading>{intl.formatMessage(messages['forgot.password.confirmation.title'])}</Alert.Heading>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="forgot.password.confirmation.message"
|
||||
defaultMessage="You entered {strongEmail}. If this email address is associated with your
|
||||
edX account, we will send a message with password recovery instructions to this email address."
|
||||
description="Forgot password confirmation message"
|
||||
values={{ strongEmail: <strong className="data-hj-suppress">{email}</strong> }}
|
||||
/>
|
||||
</p>
|
||||
<p>{intl.formatMessage(messages['forgot.password.confirmation.info'])}</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="forgot.password.technical.support.help.message"
|
||||
defaultMessage="If you need further assistance, {technicalSupportLink}."
|
||||
description="Message to help user contact technical support"
|
||||
values={{
|
||||
technicalSupportLink: (
|
||||
<Alert.Link href={getConfig().PASSWORD_RESET_SUPPORT_LINK}>
|
||||
{intl.formatMessage(messages['forgot.password.confirmation.support.link'])}
|
||||
</Alert.Link>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
</Alert>
|
||||
);
|
||||
};
|
||||
|
||||
ConfirmationAlert.propTypes = {
|
||||
email: PropTypes.string.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(ConfirmationAlert);
|
||||
@@ -1,105 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import PropTypes from 'prop-types';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { faSignInAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import {
|
||||
Form, Button,
|
||||
} from '@edx/paragon';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
|
||||
import { LOGIN_PAGE, SUPPORTED_ICON_CLASSES } from '../data/constants';
|
||||
import messages from './messages';
|
||||
|
||||
const EnterpriseSSO = (props) => {
|
||||
const { intl } = props;
|
||||
const tpaProvider = props.provider;
|
||||
|
||||
const handleSubmit = (e, url) => {
|
||||
e.preventDefault();
|
||||
window.location.href = getConfig().LMS_BASE_URL + url;
|
||||
};
|
||||
|
||||
const handleClick = (e) => {
|
||||
e.preventDefault();
|
||||
window.location.href = LOGIN_PAGE;
|
||||
};
|
||||
|
||||
if (tpaProvider) {
|
||||
return (
|
||||
<div className="d-flex justify-content-center m-4">
|
||||
<div className="d-flex flex-column">
|
||||
<div className="mw-450">
|
||||
<h3>Sign in</h3>
|
||||
<Form className="m-0">
|
||||
<p>{intl.formatMessage(messages['enterprisetpa.title.heading'], { providerName: tpaProvider.name })}</p>
|
||||
<Button
|
||||
id={tpaProvider.id}
|
||||
key={tpaProvider.id}
|
||||
type="submit"
|
||||
variant="primary"
|
||||
className="btn-tpa"
|
||||
onClick={(e) => handleSubmit(e, tpaProvider.loginUrl)}
|
||||
>
|
||||
{tpaProvider.iconImage ? (
|
||||
<div aria-hidden="true">
|
||||
<img className="icon-image" src={tpaProvider.iconImage} alt={`icon ${tpaProvider.name}`} />
|
||||
<span className="pl-2" aria-hidden="true">{intl.formatMessage(messages['enterprisetpa.sso.button.title'], { providerName: tpaProvider.name })}</span>
|
||||
</div>
|
||||
)
|
||||
: (
|
||||
<>
|
||||
<div className="font-container" aria-hidden="true">
|
||||
<FontAwesomeIcon
|
||||
icon={SUPPORTED_ICON_CLASSES.includes(tpaProvider.iconClass) ? ['fab', tpaProvider.iconClass] : faSignInAlt}
|
||||
/>
|
||||
</div>
|
||||
<span className="pl-2" aria-hidden="true">{intl.formatMessage(messages['enterprisetpa.sso.button.title'], { providerName: tpaProvider.name })}</span>
|
||||
</>
|
||||
)}
|
||||
</Button>
|
||||
<div className="mb-4" />
|
||||
<Button
|
||||
type="submit"
|
||||
variant="outline-primary"
|
||||
state="Complete"
|
||||
className="w-100"
|
||||
onClick={(e) => handleClick(e)}
|
||||
>
|
||||
{intl.formatMessage(messages['enterprisetpa.login.button.text'])}
|
||||
</Button>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return <div />;
|
||||
};
|
||||
|
||||
EnterpriseSSO.defaultProps = {
|
||||
provider: {
|
||||
id: '',
|
||||
name: '',
|
||||
iconClass: '',
|
||||
iconImage: '',
|
||||
loginUrl: '',
|
||||
registerUrl: '',
|
||||
},
|
||||
};
|
||||
|
||||
EnterpriseSSO.propTypes = {
|
||||
provider: PropTypes.shape({
|
||||
id: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
iconClass: PropTypes.string,
|
||||
iconImage: PropTypes.string,
|
||||
loginUrl: PropTypes.string,
|
||||
registerUrl: PropTypes.string,
|
||||
}),
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(EnterpriseSSO);
|
||||
@@ -1,22 +0,0 @@
|
||||
import React from 'react';
|
||||
import CookiePolicyBanner from '@edx/frontend-component-cookie-policy-banner';
|
||||
import PropTypes from 'prop-types';
|
||||
import { getLocale } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import Header from '@edx/frontend-component-header';
|
||||
|
||||
const HeaderLayout = ({ children }) => (
|
||||
<div className="d-flex flex-column">
|
||||
<CookiePolicyBanner languageCode={getLocale()} />
|
||||
<Header />
|
||||
<main className="flex-grow-1" id="main">
|
||||
{children}
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
|
||||
HeaderLayout.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
};
|
||||
|
||||
export default HeaderLayout;
|
||||
@@ -1,101 +0,0 @@
|
||||
import React from 'react';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Hyperlink } from '@edx/paragon';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faChevronLeft } from '@fortawesome/free-solid-svg-icons';
|
||||
import messages from './messages';
|
||||
|
||||
export const RenderInstitutionButton = props => {
|
||||
const { onSubmitHandler, secondaryProviders, buttonTitle } = props;
|
||||
if (secondaryProviders !== undefined && secondaryProviders.length > 0) {
|
||||
return (
|
||||
<Button
|
||||
className="w-auto mb-3"
|
||||
block
|
||||
variant="outline-primary"
|
||||
onClick={onSubmitHandler}
|
||||
>
|
||||
{buttonTitle}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
return <></>;
|
||||
};
|
||||
|
||||
const InstitutionLogistration = props => {
|
||||
const lmsBaseUrl = getConfig().LMS_BASE_URL;
|
||||
const {
|
||||
intl,
|
||||
onSubmitHandler,
|
||||
secondaryProviders,
|
||||
headingTitle,
|
||||
buttonTitle,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="d-flex justify-content-center m-4">
|
||||
<div className="flex-column">
|
||||
<div className="mt-3">
|
||||
<FontAwesomeIcon className="mr-2" icon={faChevronLeft} />
|
||||
<Hyperlink
|
||||
destination=""
|
||||
onClick={onSubmitHandler}
|
||||
>
|
||||
{buttonTitle}
|
||||
</Hyperlink>
|
||||
</div>
|
||||
<h1 className="mt-3 mb-4 font-weight-normal h3">
|
||||
{headingTitle}
|
||||
</h1>
|
||||
<p className="mb-2">
|
||||
{intl.formatMessage(messages['institution.login.page.sub.heading'])}
|
||||
</p>
|
||||
<div className="mb-2 ml-2">
|
||||
<ul>
|
||||
{secondaryProviders.map(provider => (
|
||||
<li key={provider}>
|
||||
<Hyperlink destination={lmsBaseUrl + provider.loginUrl}>{provider.name}</Hyperlink>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const LogistrationDefaultProps = {
|
||||
secondaryProviders: [],
|
||||
buttonTitle: '',
|
||||
};
|
||||
const LogistrationProps = {
|
||||
onSubmitHandler: PropTypes.func.isRequired,
|
||||
secondaryProviders: PropTypes.arrayOf(PropTypes.shape({
|
||||
name: PropTypes.string.isRequried,
|
||||
loginUrl: PropTypes.string.isRequired,
|
||||
})),
|
||||
buttonTitle: PropTypes.string,
|
||||
};
|
||||
|
||||
RenderInstitutionButton.propTypes = {
|
||||
...LogistrationProps,
|
||||
};
|
||||
RenderInstitutionButton.defaultProps = {
|
||||
...LogistrationDefaultProps,
|
||||
};
|
||||
|
||||
InstitutionLogistration.propTypes = {
|
||||
...LogistrationProps,
|
||||
intl: intlShape.isRequired,
|
||||
headingTitle: PropTypes.string,
|
||||
};
|
||||
InstitutionLogistration.defaultProps = {
|
||||
...LogistrationDefaultProps,
|
||||
headingTitle: '',
|
||||
};
|
||||
|
||||
export default injectIntl(InstitutionLogistration);
|
||||
@@ -1,75 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faSignInAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
import messages from './messages';
|
||||
import { LOGIN_PAGE, SUPPORTED_ICON_CLASSES } from '../data/constants';
|
||||
|
||||
function SocialAuthProviders(props) {
|
||||
const { intl, referrer, socialAuthProviders } = props;
|
||||
|
||||
function handleSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const url = e.currentTarget.dataset.providerUrl;
|
||||
window.location.href = getConfig().LMS_BASE_URL + url;
|
||||
}
|
||||
|
||||
const socialAuth = socialAuthProviders.map((provider, index) => (
|
||||
<button
|
||||
id={provider.id}
|
||||
key={provider.id}
|
||||
type="button"
|
||||
className={`btn-social btn-${provider.id} ${index % 2 === 0 ? 'mr-3' : ''}`}
|
||||
data-provider-url={referrer === LOGIN_PAGE ? provider.loginUrl : provider.registerUrl}
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
{provider.iconImage ? (
|
||||
<div className="ml-auto" aria-hidden="true">
|
||||
<img className="icon-image" src={provider.iconImage} alt={`icon ${provider.name}`} />
|
||||
</div>
|
||||
)
|
||||
: (
|
||||
<>
|
||||
<div className="font-container" aria-hidden="true">
|
||||
<FontAwesomeIcon
|
||||
icon={SUPPORTED_ICON_CLASSES.includes(provider.iconClass) ? ['fab', provider.iconClass] : faSignInAlt}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
<span id="provider-name" className="mr-auto pl-2" aria-hidden="true">{provider.name}</span>
|
||||
<span className="sr-only">
|
||||
{referrer === LOGIN_PAGE
|
||||
? intl.formatMessage(messages['sso.sign.in.with'], { providerName: provider.name })
|
||||
: intl.formatMessage(messages['sso.create.account.using'], { providerName: provider.name })}
|
||||
</span>
|
||||
</button>
|
||||
));
|
||||
|
||||
return <>{socialAuth}</>;
|
||||
}
|
||||
|
||||
SocialAuthProviders.defaultProps = {
|
||||
referrer: LOGIN_PAGE,
|
||||
socialAuthProviders: [],
|
||||
};
|
||||
|
||||
SocialAuthProviders.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
referrer: PropTypes.string,
|
||||
socialAuthProviders: PropTypes.arrayOf(PropTypes.shape({
|
||||
id: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
iconClass: PropTypes.string,
|
||||
iconImage: PropTypes.string,
|
||||
loginUrl: PropTypes.string,
|
||||
registerUrl: PropTypes.string,
|
||||
})),
|
||||
};
|
||||
|
||||
export default injectIntl(SocialAuthProviders);
|
||||
@@ -1,45 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
import { Alert } from '@edx/paragon';
|
||||
import { LOGIN_PAGE, REGISTER_PAGE } from '../data/constants';
|
||||
|
||||
const ThirdPartyAuthAlert = (props) => {
|
||||
const { currentProvider, referrer, platformName } = props;
|
||||
let message;
|
||||
|
||||
if (referrer === LOGIN_PAGE) {
|
||||
message = (
|
||||
<FormattedMessage
|
||||
id="login.third.party.auth.account.not.linked.message"
|
||||
defaultMessage="You have successfully signed into {currentProvider}, but your {currentProvider} account does not have a linked {platformName} account. To link your accounts, sign in now using your {platformName} password."
|
||||
description="Message that appears on login page if user has successfully authenticated with TPA but no associated platform account exists"
|
||||
values={{ currentProvider, platformName }}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
message = (
|
||||
<FormattedMessage
|
||||
id="register.third.party.auth.account.not.linked.message"
|
||||
defaultMessage="You've successfully signed into {currentProvider}. We just need a little more information before you start learning with {platformName}."
|
||||
description="Message that appears on register page if user has successfully authenticated with TPA but no associated platform account exists"
|
||||
values={{ currentProvider, platformName }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <Alert id="tpa-alert" className={referrer === REGISTER_PAGE ? 'alert-success mt-n2' : 'alert-warning mt-n2'}>{ message }</Alert>;
|
||||
};
|
||||
|
||||
ThirdPartyAuthAlert.defaultProps = {
|
||||
referrer: LOGIN_PAGE,
|
||||
};
|
||||
|
||||
ThirdPartyAuthAlert.propTypes = {
|
||||
currentProvider: PropTypes.string.isRequired,
|
||||
platformName: PropTypes.string.isRequired,
|
||||
referrer: PropTypes.string,
|
||||
};
|
||||
|
||||
export default ThirdPartyAuthAlert;
|
||||
@@ -1,23 +0,0 @@
|
||||
import { camelCaseObject, convertKeyNames, getConfig } from '@edx/frontend-platform';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export async function getThirdPartyAuthContext(urlParams) {
|
||||
const requestConfig = {
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
params: urlParams,
|
||||
isPublic: true,
|
||||
};
|
||||
|
||||
const { data } = await getAuthenticatedHttpClient()
|
||||
.get(
|
||||
`${getConfig().LMS_BASE_URL}/api/third_party_auth_context`,
|
||||
requestConfig,
|
||||
)
|
||||
.catch((e) => {
|
||||
throw (e);
|
||||
});
|
||||
return {
|
||||
thirdPartyAuthContext: camelCaseObject(convertKeyNames(data, { fullname: 'name' })),
|
||||
};
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
export { default as HeaderLayout } from './HeaderLayout';
|
||||
export { default as RedirectLogistration } from './RedirectLogistration';
|
||||
export { default as registerIcons } from './RegisterFaIcons';
|
||||
export { default as UnAuthOnlyRoute } from './UnAuthOnlyRoute';
|
||||
export { default as NotFoundPage } from './NotFoundPage';
|
||||
export { default as SocialAuthProviders } from './SocialAuthProviders';
|
||||
export { default as ThirdPartyAuthAlert } from './ThirdPartyAuthAlert';
|
||||
export { default as InstitutionLogistration } from './InstitutionLogistration';
|
||||
export { RenderInstitutionButton } from './InstitutionLogistration';
|
||||
export { default as AuthnValidationFormGroup } from './AuthnValidationFormGroup';
|
||||
export { default as APIFailureMessage } from './APIFailureMessage';
|
||||
export { default as reducer } from './data/reducers';
|
||||
export { default as saga } from './data/sagas';
|
||||
export { storeName } from './data/selectors';
|
||||
@@ -1,65 +0,0 @@
|
||||
import { defineMessages } from '@edx/frontend-platform/i18n';
|
||||
|
||||
const messages = defineMessages({
|
||||
'institution.login.page.sub.heading': {
|
||||
id: 'institution.login.page.sub.heading',
|
||||
defaultMessage: 'Choose your institution from the list below:',
|
||||
description: 'Heading of the institutions list',
|
||||
},
|
||||
// Confirmation Alert Message
|
||||
'forgot.password.confirmation.title': {
|
||||
id: 'forgot.password.confirmation.title',
|
||||
defaultMessage: 'Check your email',
|
||||
description: 'Forgot password confirmation message title',
|
||||
},
|
||||
'forgot.password.confirmation.support.link': {
|
||||
id: 'forgot.password.confirmation.support.link',
|
||||
defaultMessage: 'contact technical support',
|
||||
description: 'Technical support link text',
|
||||
},
|
||||
'forgot.password.confirmation.info': {
|
||||
id: 'forgot.password.confirmation.info',
|
||||
defaultMessage: '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.',
|
||||
description: 'Part of message that appears after user requests password change',
|
||||
},
|
||||
'internal.server.error.message': {
|
||||
id: 'internal.server.error.message',
|
||||
defaultMessage: 'An error has occurred. Try refreshing the page, or check your internet connection.',
|
||||
description: 'Error message that appears when server responds with 500 error code',
|
||||
},
|
||||
'server.ratelimit.error.message': {
|
||||
id: 'server.ratelimit.error.message',
|
||||
defaultMessage: 'An error has occurred because of too many requests. Please try again after some time.',
|
||||
description: 'Error message that appears when server responds with 429 error code',
|
||||
},
|
||||
// enterprise sso strings
|
||||
'enterprisetpa.title.heading': {
|
||||
id: 'enterprisetpa.title.heading',
|
||||
defaultMessage: 'Would you like to sign in using your {providerName} credentials?',
|
||||
description: 'Header text used in enterprise third party authentication',
|
||||
},
|
||||
'enterprisetpa.sso.button.title': {
|
||||
id: 'enterprisetpa.sso.button.title',
|
||||
defaultMessage: 'Sign in using {providerName}',
|
||||
description: 'Text for third party auth provider buttons',
|
||||
},
|
||||
'enterprisetpa.login.button.text': {
|
||||
id: 'enterprisetpa.login.button.text',
|
||||
defaultMessage: 'Show me other ways to sign in or register',
|
||||
description: 'Button text for login',
|
||||
},
|
||||
// social auth providers
|
||||
'sso.sign.in.with': {
|
||||
id: 'sso.sign.in.with',
|
||||
defaultMessage: 'Sign in with {providerName}',
|
||||
description: 'Screen reader text that appears before social auth provider name',
|
||||
},
|
||||
'sso.create.account.using': {
|
||||
id: 'sso.create.account.using',
|
||||
defaultMessage: 'Create account using {providerName}',
|
||||
description: 'Screen reader text that appears before social auth provider name',
|
||||
},
|
||||
});
|
||||
|
||||
export default messages;
|
||||
@@ -1,46 +0,0 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
import AuthnCustomValidationFormGroup from '../AuthnValidationFormGroup';
|
||||
|
||||
describe('AuthnCustomValidationFormGroup', () => {
|
||||
let props = {
|
||||
label: 'Email Label',
|
||||
for: 'email',
|
||||
name: 'email',
|
||||
type: 'email',
|
||||
value: '',
|
||||
helpText: 'Email field help text',
|
||||
};
|
||||
|
||||
it('should show label in place of placeholder when field is empty', () => {
|
||||
const validationFormGroup = mount(<AuthnCustomValidationFormGroup {...props} />);
|
||||
expect(validationFormGroup.find('label').prop('className')).toEqual('pgn__form-label pt-10 focus-out');
|
||||
});
|
||||
|
||||
it('should show label on top of field when field is focused in', () => {
|
||||
const validationFormGroup = mount(<AuthnCustomValidationFormGroup {...props} />);
|
||||
|
||||
validationFormGroup.find('input').simulate('focus');
|
||||
expect(validationFormGroup.find('label').prop('className')).toEqual('pgn__form-label pt-10 focus-out');
|
||||
});
|
||||
|
||||
it('should keep label hidden for checkbox field', () => {
|
||||
props = {
|
||||
...props,
|
||||
type: 'checkbox',
|
||||
optionalFieldCheckbox: true,
|
||||
};
|
||||
const validationFormGroup = mount(<AuthnCustomValidationFormGroup {...props} />);
|
||||
expect(validationFormGroup.find('label').prop('className')).toEqual('pgn__form-label sr-only');
|
||||
});
|
||||
|
||||
it('should keep label hidden when input field is not empty', () => {
|
||||
props = {
|
||||
...props,
|
||||
value: 'test',
|
||||
};
|
||||
const validationFormGroup = mount(<AuthnCustomValidationFormGroup {...props} />);
|
||||
expect(validationFormGroup.find('label').prop('className')).toEqual('pgn__form-label sr-only');
|
||||
});
|
||||
});
|
||||
@@ -1,34 +0,0 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
import { mergeConfig } from '@edx/frontend-platform';
|
||||
import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import ConfirmationAlert from '../ConfirmationAlert';
|
||||
|
||||
const IntlConfirmationAlertMessage = injectIntl(ConfirmationAlert);
|
||||
|
||||
describe('ConfirmationAlert', () => {
|
||||
const supportLink = 'https://support.test.com/What-if-I-did-not-receive-a-password-reset-message';
|
||||
mergeConfig({
|
||||
PASSWORD_RESET_SUPPORT_LINK: supportLink,
|
||||
});
|
||||
|
||||
it('should match default confirmation message', () => {
|
||||
const confirmationAlertMessage = mount(
|
||||
<IntlProvider locale="en">
|
||||
<IntlConfirmationAlertMessage email="test@example.com" />
|
||||
</IntlProvider>,
|
||||
);
|
||||
|
||||
const expectedMessage = 'Check your email'
|
||||
+ 'You entered test@example.com. If this email address is associated with your edX account, '
|
||||
+ 'we will send a message with password recovery instructions to this email address.'
|
||||
+ '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, contact technical support.';
|
||||
|
||||
expect(confirmationAlertMessage.find('#confirmation-alert').first().text()).toEqual(expectedMessage);
|
||||
expect(confirmationAlertMessage.find('#confirmation-alert').find('a').props().href).toEqual(supportLink);
|
||||
});
|
||||
});
|
||||
@@ -1,39 +0,0 @@
|
||||
import React from 'react';
|
||||
import renderer from 'react-test-renderer';
|
||||
import { IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import ThirdPartyAuthAlert from '../ThirdPartyAuthAlert';
|
||||
|
||||
describe('ThirdPartyAuthAlert', () => {
|
||||
let props = {};
|
||||
|
||||
beforeEach(() => {
|
||||
props = {
|
||||
currentProvider: 'Google',
|
||||
platformName: 'edX',
|
||||
};
|
||||
});
|
||||
|
||||
it('should match login page third party auth alert message snapshot', () => {
|
||||
const tree = renderer.create(
|
||||
<IntlProvider locale="en">
|
||||
<ThirdPartyAuthAlert {...props} />
|
||||
</IntlProvider>,
|
||||
).toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should match register page third party auth alert message snapshot', () => {
|
||||
props = {
|
||||
...props,
|
||||
referrer: 'register',
|
||||
};
|
||||
|
||||
const tree = renderer.create(
|
||||
<IntlProvider locale="en">
|
||||
<ThirdPartyAuthAlert {...props} />
|
||||
</IntlProvider>,
|
||||
).toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -1,156 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`SocialAuthProviders should match social auth provider with default icon snapshot 1`] = `
|
||||
<button
|
||||
className="btn-social btn-oa2-apple-id mr-3"
|
||||
data-provider-url="/auth/login/apple-id/?auth_entry=login&next=/dashboard"
|
||||
id="oa2-apple-id"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="font-container"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="svg-inline--fa fa-sign-in-alt fa-w-16 "
|
||||
data-icon="sign-in-alt"
|
||||
data-prefix="fas"
|
||||
focusable="false"
|
||||
role="img"
|
||||
style={Object {}}
|
||||
viewBox="0 0 512 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M416 448h-84c-6.6 0-12-5.4-12-12v-40c0-6.6 5.4-12 12-12h84c17.7 0 32-14.3 32-32V160c0-17.7-14.3-32-32-32h-84c-6.6 0-12-5.4-12-12V76c0-6.6 5.4-12 12-12h84c53 0 96 43 96 96v192c0 53-43 96-96 96zm-47-201L201 79c-15-15-41-4.5-41 17v96H24c-13.3 0-24 10.7-24 24v96c0 13.3 10.7 24 24 24h136v96c0 21.5 26 32 41 17l168-168c9.3-9.4 9.3-24.6 0-34z"
|
||||
fill="currentColor"
|
||||
style={Object {}}
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="mr-auto pl-2"
|
||||
id="provider-name"
|
||||
>
|
||||
Apple
|
||||
</span>
|
||||
<span
|
||||
className="sr-only"
|
||||
>
|
||||
Sign in with Apple
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`SocialAuthProviders should match social auth provider with iconClass snapshot 1`] = `
|
||||
<button
|
||||
className="btn-social btn-oa2-apple-id mr-3"
|
||||
data-provider-url="/auth/login/apple-id/?auth_entry=login&next=/dashboard"
|
||||
id="oa2-apple-id"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="font-container"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="svg-inline--fa fa-google fa-w-16 "
|
||||
data-icon="google"
|
||||
data-prefix="fab"
|
||||
focusable="false"
|
||||
role="img"
|
||||
style={Object {}}
|
||||
viewBox="0 0 488 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M488 261.8C488 403.3 391.1 504 248 504 110.8 504 0 393.2 0 256S110.8 8 248 8c66.8 0 123 24.5 166.3 64.9l-67.5 64.9C258.5 52.6 94.3 116.6 94.3 256c0 86.5 69.1 156.6 153.7 156.6 98.2 0 135-70.4 140.8-106.9H248v-85.3h236.1c2.3 12.7 3.9 24.9 3.9 41.4z"
|
||||
fill="currentColor"
|
||||
style={Object {}}
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="mr-auto pl-2"
|
||||
id="provider-name"
|
||||
>
|
||||
Apple
|
||||
</span>
|
||||
<span
|
||||
className="sr-only"
|
||||
>
|
||||
Sign in with Apple
|
||||
</span>
|
||||
</button>
|
||||
`;
|
||||
|
||||
exports[`SocialAuthProviders should match social auth provider with iconImage snapshot 1`] = `
|
||||
Array [
|
||||
<button
|
||||
className="btn-social btn-oa2-apple-id mr-3"
|
||||
data-provider-url="/auth/login/apple-id/?auth_entry=login&next=/dashboard"
|
||||
id="oa2-apple-id"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="ml-auto"
|
||||
>
|
||||
<img
|
||||
alt="icon Apple"
|
||||
className="icon-image"
|
||||
src="https://edx.devstack.lms/logo.png"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="mr-auto pl-2"
|
||||
id="provider-name"
|
||||
>
|
||||
Apple
|
||||
</span>
|
||||
<span
|
||||
className="sr-only"
|
||||
>
|
||||
Sign in with Apple
|
||||
</span>
|
||||
</button>,
|
||||
<button
|
||||
className="btn-social btn-oa2-facebook "
|
||||
data-provider-url="/auth/login/facebook/?auth_entry=login&next=/dashboard"
|
||||
id="oa2-facebook"
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="ml-auto"
|
||||
>
|
||||
<img
|
||||
alt="icon Facebook"
|
||||
className="icon-image"
|
||||
src="https://edx.devstack.lms/facebook-logo.png"
|
||||
/>
|
||||
</div>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
className="mr-auto pl-2"
|
||||
id="provider-name"
|
||||
>
|
||||
Facebook
|
||||
</span>
|
||||
<span
|
||||
className="sr-only"
|
||||
>
|
||||
Sign in with Facebook
|
||||
</span>
|
||||
</button>,
|
||||
]
|
||||
`;
|
||||
@@ -1,41 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`ThirdPartyAuthAlert should match login page third party auth alert message snapshot 1`] = `
|
||||
<div
|
||||
className="fade alert-content alert-warning mt-n2 alert show"
|
||||
id="tpa-alert"
|
||||
role="alert"
|
||||
>
|
||||
<div
|
||||
className="pgn__alert-message-wrapper"
|
||||
>
|
||||
<div
|
||||
className="alert-message-content"
|
||||
>
|
||||
<span>
|
||||
You have successfully signed into Google, but your Google account does not have a linked edX account. To link your accounts, sign in now using your edX password.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ThirdPartyAuthAlert should match register page third party auth alert message snapshot 1`] = `
|
||||
<div
|
||||
className="fade alert-content alert-warning mt-n2 alert show"
|
||||
id="tpa-alert"
|
||||
role="alert"
|
||||
>
|
||||
<div
|
||||
className="pgn__alert-message-wrapper"
|
||||
>
|
||||
<div
|
||||
className="alert-message-content"
|
||||
>
|
||||
<span>
|
||||
You've successfully signed into Google. We just need a little more information before you start learning with edX.
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -1,31 +0,0 @@
|
||||
// URL Paths
|
||||
export const LOGIN_PAGE = '/login';
|
||||
export const REGISTER_PAGE = '/register';
|
||||
export const RESET_PAGE = '/reset';
|
||||
export const WELCOME_PAGE = '/welcome';
|
||||
export const DEFAULT_REDIRECT_URL = '/dashboard';
|
||||
export const PASSWORD_RESET_CONFIRM = '/password_reset_confirm/:token/';
|
||||
export const PAGE_NOT_FOUND = '/notfound';
|
||||
export const ENTERPRISE_LOGIN_URL = '/enterprise/login';
|
||||
|
||||
// Constants
|
||||
export const SUPPORTED_ICON_CLASSES = ['apple', 'facebook', 'google', 'microsoft'];
|
||||
|
||||
// Error Codes
|
||||
export const INTERNAL_SERVER_ERROR = 'internal-server-error';
|
||||
export const API_RATELIMIT_ERROR = 'api-ratelimit-error';
|
||||
|
||||
// States
|
||||
export const DEFAULT_STATE = 'default';
|
||||
export const PENDING_STATE = 'pending';
|
||||
export const COMPLETE_STATE = 'complete';
|
||||
|
||||
// Regex
|
||||
export const VALID_EMAIL_REGEX = '(^[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+(\\.[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+)*'
|
||||
+ '|^"([\\001-\\010\\013\\014\\016-\\037!#-\\[\\]-\\177]|\\\\[\\001-\\011\\013\\014\\016-\\177])*"'
|
||||
+ ')@((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\\.)+)(?:[A-Z0-9-]{2,63})'
|
||||
+ '|\\[(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\]$';
|
||||
|
||||
// Query string parameters that can be passed to LMS to manage
|
||||
// things like auto-enrollment upon login and registration.
|
||||
export const AUTH_PARAMS = ['course_id', 'enrollment_action', 'course_mode', 'email_opt_in', 'purchase_workflow', 'next'];
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user