diff --git a/package-lock.json b/package-lock.json index a05b8b10..1c332ba1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4691,31 +4691,6 @@ } } }, - "@loadable/component": { - "version": "5.14.1", - "resolved": "https://registry.npmjs.org/@loadable/component/-/component-5.14.1.tgz", - "integrity": "sha512-UQBZfZrp1FLTf8RNhljXNHFNY4QhAA1L2+GOEeABBFre9TD0aFyQh3Sai5QxcOfy+FTbjIfti5iHaNRR7yUzEQ==", - "requires": { - "@babel/runtime": "^7.7.7", - "hoist-non-react-statics": "^3.3.1", - "react-is": "^16.12.0" - }, - "dependencies": { - "@babel/runtime": { - "version": "7.12.18", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.18.tgz", - "integrity": "sha512-BogPQ7ciE6SYAUPtlm9tWbgI9+2AgqSam6QivMgXgAT+fKbgppaj4ZX15MHeLC1PVF5sNk70huBu20XxWOs8Cg==", - "requires": { - "regenerator-runtime": "^0.13.4" - } - }, - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" - } - } - }, "@mrmlnc/readdir-enhanced": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz", diff --git a/package.json b/package.json index 8bdc7ec6..c6b456aa 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,6 @@ "@fortawesome/free-regular-svg-icons": "5.15.1", "@fortawesome/free-solid-svg-icons": "5.15.1", "@fortawesome/react-fontawesome": "0.1.13", - "@loadable/component": "^5.14.1", "babel-polyfill": "6.26.0", "classnames": "^2.2.6", "extract-react-intl-messages": "^4.1.1", diff --git a/src/common-components/UnAuthOnlyRoute.jsx b/src/common-components/UnAuthOnlyRoute.jsx new file mode 100644 index 00000000..f136f4f3 --- /dev/null +++ b/src/common-components/UnAuthOnlyRoute.jsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { Route } from 'react-router-dom'; +import { AppContext } from '@edx/frontend-platform/react'; + +import { DEFAULT_REDIRECT_URL } from '../data/constants'; + +/** + * This wrapper redirects the requester to our default redirect url if they are + * already authenticated. + */ +const UnAuthOnlyRoute = (props) => { + const { authenticatedUser, config } = React.useContext(AppContext); + + if (authenticatedUser) { + global.location.href = config.LMS_BASE_URL.concat(DEFAULT_REDIRECT_URL); + return null; + } + + return ; +}; + +export default UnAuthOnlyRoute; diff --git a/src/common-components/UnAuthenticatedRoute.jsx b/src/common-components/UnAuthenticatedRoute.jsx deleted file mode 100644 index 2c9371f7..00000000 --- a/src/common-components/UnAuthenticatedRoute.jsx +++ /dev/null @@ -1,36 +0,0 @@ -/* eslint-disable react/prop-types */ -import React, { useEffect } from 'react'; -import { Route, useRouteMatch } from 'react-router-dom'; -import { AppContext } from '@edx/frontend-platform/react'; -import { sendPageEvent } from '@edx/frontend-platform/analytics'; - -import { DEFAULT_REDIRECT_URL } from '../data/constants'; - -/** - * This wrapper redirects the requester to our default redirect url if they are - * already authenticated. - */ -const UnAuthenticatedRoute = (props) => { - const { authenticatedUser, config } = React.useContext(AppContext); - const match = useRouteMatch({ - path: props.path, - exact: props.exact, - strict: props.strict, - sensitive: props.sensitive, - }); - - if (authenticatedUser) { - global.location.href = config.LMS_BASE_URL.concat(DEFAULT_REDIRECT_URL); - return null; - } - - useEffect(() => { - if (match) { - sendPageEvent('login_and_registration', props.path.replace('/', '')); - } - }, [match]); - - return ; -}; - -export default UnAuthenticatedRoute; diff --git a/src/common-components/index.jsx b/src/common-components/index.jsx index 65fc8d4b..20894220 100644 --- a/src/common-components/index.jsx +++ b/src/common-components/index.jsx @@ -1,7 +1,7 @@ export { default as HeaderLayout } from './HeaderLayout'; export { default as RedirectLogistration } from './RedirectLogistration'; export { default as registerIcons } from './RegisterFaIcons'; -export { default as UnAuthenticatedRoute } from './UnAuthenticatedRoute'; +export { default as UnAuthOnlyRoute } from './UnAuthOnlyRoute'; export { default as NotFoundPage } from './NotFoundPage'; export { default as SocialAuthProviders } from './SocialAuthProviders'; export { default as ThirdPartyAuthAlert } from './ThirdPartyAuthAlert'; @@ -12,4 +12,3 @@ 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'; -export { default as Spinner } from './Spinner'; diff --git a/src/common-components/tests/UnAuthenticatedRoute.test.jsx b/src/common-components/tests/UnAuthOnlyRoute.test.jsx similarity index 58% rename from src/common-components/tests/UnAuthenticatedRoute.test.jsx rename to src/common-components/tests/UnAuthOnlyRoute.test.jsx index bfe34986..3b0ad0cb 100644 --- a/src/common-components/tests/UnAuthenticatedRoute.test.jsx +++ b/src/common-components/tests/UnAuthOnlyRoute.test.jsx @@ -1,36 +1,31 @@ -/* eslint-disable react/prop-types */ import React from 'react'; import { mount } from 'enzyme'; import { BrowserRouter as Router, MemoryRouter, Switch } from 'react-router-dom'; import { getConfig } from '@edx/frontend-platform'; -import * as analytics from '@edx/frontend-platform/analytics'; -import { UnAuthenticatedRoute } from '..'; -import { DEFAULT_REDIRECT_URL, LOGIN_PAGE, REGISTER_PAGE } from '../../data/constants'; +import { UnAuthOnlyRoute } from '..'; +import { DEFAULT_REDIRECT_URL, LOGIN_PAGE } from '../../data/constants'; const RRD = require('react-router-dom'); // Just render plain div with its children +// eslint-disable-next-line react/prop-types RRD.BrowserRouter = ({ children }) =>
{ children }
; module.exports = RRD; -jest.mock('@edx/frontend-platform/analytics'); -analytics.sendPageEvent = jest.fn(); - const TestApp = () => (
- (Login Page)} /> - (Register Page)} /> + (Login Page)} />
); -describe('UnAuthenticatedRoute', () => { - const routerWrapper = (initialEntry) => ( - +describe('UnAuthOnlyRoute', () => { + const routerWrapper = () => ( + ); @@ -65,14 +60,4 @@ describe('UnAuthenticatedRoute', () => { expect(wrapper.find('span').text()).toBe('Login Page'); }); - - it('send page event when login page is rendered', () => { - mount(routerWrapper()); - expect(analytics.sendPageEvent).toHaveBeenCalledWith('login_and_registration', 'login'); - }); - - it('send page event when register page is rendered', () => { - mount(routerWrapper(REGISTER_PAGE)); - expect(analytics.sendPageEvent).toHaveBeenCalledWith('login_and_registration', 'register'); - }); }); diff --git a/src/forgot-password/ForgotPasswordPage.jsx b/src/forgot-password/ForgotPasswordPage.jsx index 6142b76c..3e5446dd 100644 --- a/src/forgot-password/ForgotPasswordPage.jsx +++ b/src/forgot-password/ForgotPasswordPage.jsx @@ -6,6 +6,7 @@ import { connect } from 'react-redux'; import { Redirect } from 'react-router-dom'; import { getConfig } from '@edx/frontend-platform'; +import { sendPageEvent } from '@edx/frontend-platform/analytics'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { Alert, @@ -63,6 +64,8 @@ const ForgotPasswordPage = (props) => { return error; }; + sendPageEvent('login_and_registration', 'reset'); + return ( import('./ForgotPasswordPage.jsx'), { - fallback: , -}); - -const LoadableForgotPasswordPage = () => ; - -export default LoadableForgotPasswordPage; diff --git a/src/forgot-password/index.js b/src/forgot-password/index.js index 5e3f59c4..2d30ff06 100644 --- a/src/forgot-password/index.js +++ b/src/forgot-password/index.js @@ -1,4 +1,4 @@ -export { default } from './LoadableForgotPasswordPage'; +export { default } from './ForgotPasswordPage'; export { default as reducer } from './data/reducers'; export { FORGOT_PASSWORD } from './data/actions'; export { default as saga } from './data/sagas'; diff --git a/src/index.jsx b/src/index.jsx index e88fb667..c3b345f5 100755 --- a/src/index.jsx +++ b/src/index.jsx @@ -7,19 +7,20 @@ import { AppProvider, ErrorPage } from '@edx/frontend-platform/react'; import React from 'react'; import ReactDOM from 'react-dom'; import { Redirect, Route, Switch } from 'react-router-dom'; + import { messages as headerMessages } from '@edx/frontend-component-header'; import configureStore from './data/configureStore'; -import LoadableForgotPasswordPage from './forgot-password'; -import LoadableRegistrationPage from './register'; -import LoadableLoginPage from './login'; -import LoadableResetPasswordPage from './reset-password'; +import { RegistrationPage } from './register'; +import { LoginPage } from './login'; import { LOGIN_PAGE, PAGE_NOT_FOUND, REGISTER_PAGE, RESET_PAGE, PASSWORD_RESET_CONFIRM, } from './data/constants'; +import ForgotPasswordPage from './forgot-password'; import { - HeaderLayout, UnAuthenticatedRoute, registerIcons, NotFoundPage, + HeaderLayout, UnAuthOnlyRoute, registerIcons, NotFoundPage, } from './common-components'; +import ResetPasswordPage from './reset-password'; import appMessages from './i18n'; import './index.scss'; @@ -34,10 +35,10 @@ subscribe(APP_READY, () => { - - - - + + + + diff --git a/src/login/LoadableLoginPage.jsx b/src/login/LoadableLoginPage.jsx deleted file mode 100644 index 93782c61..00000000 --- a/src/login/LoadableLoginPage.jsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import loadable from '@loadable/component'; -import Spinner from '../common-components/Spinner'; - -const LoadableComponent = loadable(() => import('./LoginPage.jsx'), { - fallback: , -}); - -const LoadableLoginPage = () => ; - -export default LoadableLoginPage; diff --git a/src/login/LoginHelpLinks.jsx b/src/login/LoginHelpLinks.jsx index 46cbfff0..31f179bc 100644 --- a/src/login/LoginHelpLinks.jsx +++ b/src/login/LoginHelpLinks.jsx @@ -1,5 +1,4 @@ import React, { useState } from 'react'; -import { Link } from 'react-router-dom'; import PropTypes from 'prop-types'; import { sendTrackEvent } from '@edx/frontend-platform/analytics'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; @@ -30,17 +29,19 @@ const LoginHelpLinks = (props) => { }; const forgotPasswordLink = () => ( - {intl.formatMessage(messages['forgot.password.link'])} - + ); const signUpLink = () => ( - {intl.formatMessage(messages['register.link'])} - + + {intl.formatMessage(messages['register.link'])} + ); const loginIssueSupportURL = (config) => (config.LOGIN_ISSUE_SUPPORT_LINK diff --git a/src/login/LoginPage.jsx b/src/login/LoginPage.jsx index eb0ea683..fddff73a 100644 --- a/src/login/LoginPage.jsx +++ b/src/login/LoginPage.jsx @@ -1,6 +1,5 @@ import React from 'react'; import Skeleton from 'react-loading-skeleton'; -import { Link } from 'react-router-dom'; import { getConfig } from '@edx/frontend-platform'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; @@ -38,6 +37,7 @@ class LoginPage extends React.Component { constructor(props, context) { super(props, context); + sendPageEvent('login_and_registration', 'login'); this.state = { password: '', email: '', @@ -197,13 +197,9 @@ class LoginPage extends React.Component { ) : null}

{intl.formatMessage(messages['first.time.here'])} - - {intl.formatMessage(messages['create.an.account'])} - + + {intl.formatMessage(messages['create.an.account'])}. +


diff --git a/src/login/index.js b/src/login/index.js index 8591800b..769161d2 100644 --- a/src/login/index.js +++ b/src/login/index.js @@ -1,4 +1,4 @@ -export { default } from './LoadableLoginPage'; +export { default as LoginPage } from './LoginPage'; export { default as reducer } from './data/reducers'; export { default as saga } from './data/sagas'; export { storeName } from './data/selectors'; diff --git a/src/login/messages.jsx b/src/login/messages.jsx index 9878fde8..d64a2dc7 100644 --- a/src/login/messages.jsx +++ b/src/login/messages.jsx @@ -48,7 +48,7 @@ const messages = defineMessages({ }, 'create.an.account': { id: 'create.an.account', - defaultMessage: 'Create an account.', + defaultMessage: 'Create an account', description: 'Message on button to return to register page', }, 'or.sign.in.with': { diff --git a/src/login/tests/LoginHelpLinks.test.jsx b/src/login/tests/LoginHelpLinks.test.jsx index 6adb7dab..e975aa1c 100644 --- a/src/login/tests/LoginHelpLinks.test.jsx +++ b/src/login/tests/LoginHelpLinks.test.jsx @@ -1,5 +1,4 @@ import React from 'react'; -import { BrowserRouter as Router } from 'react-router-dom'; import { IntlProvider } from '@edx/frontend-platform/i18n'; import * as analytics from '@edx/frontend-platform/analytics'; import { mount } from 'enzyme'; @@ -21,9 +20,7 @@ describe('LoginHelpLinks', () => { const reduxWrapper = children => ( - - {children} - + {children} ); diff --git a/src/login/tests/LoginPage.test.jsx b/src/login/tests/LoginPage.test.jsx index 525b2646..6a892a77 100644 --- a/src/login/tests/LoginPage.test.jsx +++ b/src/login/tests/LoginPage.test.jsx @@ -1,6 +1,5 @@ import React from 'react'; import { Provider } from 'react-redux'; -import { BrowserRouter as Router } from 'react-router-dom'; import renderer from 'react-test-renderer'; import { mount } from 'enzyme'; import configureStore from 'redux-mock-store'; @@ -19,6 +18,7 @@ import { COMPLETE_STATE, PENDING_STATE } from '../../data/constants'; jest.mock('@edx/frontend-platform/analytics'); analytics.sendTrackEvent = jest.fn(); +analytics.sendPageEvent = jest.fn(); const IntlLoginFailureMessage = injectIntl(LoginFailureMessage); const IntlLoginPage = injectIntl(LoginPage); @@ -61,11 +61,7 @@ describe('LoginPage', () => { const reduxWrapper = children => ( - - - {children} - - + {children} ); @@ -381,6 +377,11 @@ describe('LoginPage', () => { expect(analytics.sendTrackEvent).toHaveBeenCalledWith('edx.bi.register_form.toggled', { category: 'user-engagement' }); }); + it('send page event when login page is rendered', () => { + mount(reduxWrapper()); + expect(analytics.sendPageEvent).toHaveBeenCalledWith('login_and_registration', 'login'); + }); + it('send tracking and page events when institutional button is clicked', () => { store = mockStore({ ...initialState, diff --git a/src/login/tests/__snapshots__/LoginPage.test.jsx.snap b/src/login/tests/__snapshots__/LoginPage.test.jsx.snap index 7abff277..44fd63e5 100644 --- a/src/login/tests/__snapshots__/LoginPage.test.jsx.snap +++ b/src/login/tests/__snapshots__/LoginPage.test.jsx.snap @@ -13,11 +13,13 @@ exports[`LoginPage should match TPA provider snapshot 1`] = `

First time here? - Create an account. + Create an account + .


First time here? - Create an account. + Create an account + .


First time here? - Create an account. + Create an account + .


First time here? - Create an account. + Create an account + .


First time here? - Create an account. + Create an account + .


import('./RegistrationPage.jsx'), { - fallback: , -}); - -const LoadableRegistrationPage = () => ; - -export default LoadableRegistrationPage; diff --git a/src/register/RegistrationPage.jsx b/src/register/RegistrationPage.jsx index 0794ad7e..3fc92875 100644 --- a/src/register/RegistrationPage.jsx +++ b/src/register/RegistrationPage.jsx @@ -1,5 +1,4 @@ import React from 'react'; -import { Link } from 'react-router-dom'; import camelCase from 'lodash.camelcase'; import { connect } from 'react-redux'; @@ -7,7 +6,7 @@ import Skeleton from 'react-loading-skeleton'; import PropTypes from 'prop-types'; import { getConfig } from '@edx/frontend-platform'; -import { sendTrackEvent } from '@edx/frontend-platform/analytics'; +import { sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics'; import { injectIntl, intlShape, getCountryList, getLocale, FormattedMessage, } from '@edx/frontend-platform/i18n'; @@ -37,7 +36,9 @@ class RegistrationPage extends React.Component { constructor(props, context) { super(props, context); + sendPageEvent('login_and_registration', 'register'); this.intl = props.intl; + this.state = { email: '', name: '', @@ -435,13 +436,9 @@ class RegistrationPage extends React.Component { )}

{intl.formatMessage(messages['already.have.an.edx.account'])} - + {intl.formatMessage(messages['sign.in.hyperlink'])} - +


{intl.formatMessage(messages['create.a.new.account'])}

diff --git a/src/register/index.js b/src/register/index.js index 0703e15e..496c0f7b 100644 --- a/src/register/index.js +++ b/src/register/index.js @@ -1,4 +1,4 @@ -export { default } from './LoadableRegistrationPage'; +export { default as RegistrationPage } from './RegistrationPage'; export { default as reducer } from './data/reducers'; export { default as saga } from './data/sagas'; export { storeName } from './data/selectors'; diff --git a/src/register/tests/RegistrationPage.test.jsx b/src/register/tests/RegistrationPage.test.jsx index 0c2995f1..ae4fcc98 100644 --- a/src/register/tests/RegistrationPage.test.jsx +++ b/src/register/tests/RegistrationPage.test.jsx @@ -1,6 +1,5 @@ import React from 'react'; import { Provider } from 'react-redux'; -import { BrowserRouter as Router } from 'react-router-dom'; import renderer from 'react-test-renderer'; import { mount } from 'enzyme'; import configureStore from 'redux-mock-store'; @@ -19,6 +18,7 @@ import { fetchRealtimeValidations, registerNewUser } from '../data/actions'; jest.mock('@edx/frontend-platform/analytics'); analytics.sendTrackEvent = jest.fn(); +analytics.sendPageEvent = jest.fn(); const IntlRegistrationPage = injectIntl(RegistrationPage); const IntlRegistrationFailure = injectIntl(RegistrationFailureMessage); @@ -76,11 +76,7 @@ describe('RegistrationPageTests', () => { const reduxWrapper = children => ( - - - {children} - - + {children} ); @@ -133,6 +129,11 @@ describe('RegistrationPageTests', () => { expect(analytics.sendTrackEvent).toHaveBeenCalledWith('edx.bi.login_form.toggled', { category: 'user-engagement' }); }); + it('send page event when register page is rendered', () => { + mount(reduxWrapper()); + expect(analytics.sendPageEvent).toHaveBeenCalledWith('login_and_registration', 'register'); + }); + it('should show optional fields section on optional check enabled', () => { const registrationPage = mount(reduxWrapper()); registrationPage.find('input#optional').simulate('change', { target: { checked: true } }); diff --git a/src/register/tests/__snapshots__/RegistrationPage.test.jsx.snap b/src/register/tests/__snapshots__/RegistrationPage.test.jsx.snap index a24177be..fbf4fe93 100644 --- a/src/register/tests/__snapshots__/RegistrationPage.test.jsx.snap +++ b/src/register/tests/__snapshots__/RegistrationPage.test.jsx.snap @@ -13,9 +13,10 @@ exports[`RegistrationPageTests should match TPA provider snapshot 1`] = `

Already have an edX account? Sign in. @@ -1561,9 +1562,10 @@ exports[`RegistrationPageTests should match default section snapshot 1`] = `

Already have an edX account? Sign in. @@ -3070,9 +3072,10 @@ exports[`RegistrationPageTests should match pending button state snapshot 1`] =

Already have an edX account? Sign in. diff --git a/src/reset-password/LoadableResetPasswordPage.jsx b/src/reset-password/LoadableResetPasswordPage.jsx deleted file mode 100644 index 4f6b7831..00000000 --- a/src/reset-password/LoadableResetPasswordPage.jsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; -import loadable from '@loadable/component'; -import Spinner from '../common-components/Spinner'; - -const LoadableComponent = loadable(() => import('./ResetPasswordPage.jsx'), { - fallback: , -}); - -const LoadableResetPasswordPage = () => ; - -export default LoadableResetPasswordPage; diff --git a/src/reset-password/ResetPasswordPage.jsx b/src/reset-password/ResetPasswordPage.jsx index 5687cf4c..1b46aeaa 100644 --- a/src/reset-password/ResetPasswordPage.jsx +++ b/src/reset-password/ResetPasswordPage.jsx @@ -18,8 +18,8 @@ import ResetSuccessMessage from './ResetSuccess'; import { AuthnValidationFormGroup, APIFailureMessage, - Spinner, } from '../common-components'; +import Spinner from './Spinner'; import { API_RATELIMIT_ERROR, INTERNAL_SERVER_ERROR } from '../data/constants'; const ResetPasswordPage = (props) => { diff --git a/src/common-components/Spinner.jsx b/src/reset-password/Spinner.jsx similarity index 100% rename from src/common-components/Spinner.jsx rename to src/reset-password/Spinner.jsx diff --git a/src/reset-password/index.js b/src/reset-password/index.js index 335d1d21..2116170a 100644 --- a/src/reset-password/index.js +++ b/src/reset-password/index.js @@ -1,4 +1,4 @@ -export { default } from './LoadableResetPasswordPage'; +export { default } from './ResetPasswordPage'; export { default as reducer } from './data/reducers'; export { RESET_PASSWORD } from './data/actions'; export { default as saga } from './data/sagas';