Keep the query params intact within the app. (#201)
Currently, if the user lands on one page with course_id and enroll action params and navigates to another page with the MFE, the query params are not passed to that other page and resulting in not enrolling into the course. Also add these query params into login and register payload. VAN-415
This commit is contained in:
21
package-lock.json
generated
21
package-lock.json
generated
@@ -9704,8 +9704,7 @@
|
||||
"decode-uri-component": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
|
||||
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
|
||||
"dev": true
|
||||
"integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU="
|
||||
},
|
||||
"decompress": {
|
||||
"version": "4.2.1",
|
||||
@@ -17665,6 +17664,16 @@
|
||||
"cast-array": "~1.0.0",
|
||||
"object-filter": "~1.0.2",
|
||||
"query-string": "~2.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"query-string": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-2.4.2.tgz",
|
||||
"integrity": "sha1-fbBmZCCAS6qSrp8miWKFWnYUPfs=",
|
||||
"requires": {
|
||||
"strict-uri-encode": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"make-dir": {
|
||||
@@ -20685,10 +20694,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"query-string": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-2.4.2.tgz",
|
||||
"integrity": "sha1-fbBmZCCAS6qSrp8miWKFWnYUPfs=",
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz",
|
||||
"integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==",
|
||||
"requires": {
|
||||
"decode-uri-component": "^0.2.0",
|
||||
"object-assign": "^4.1.0",
|
||||
"strict-uri-encode": "^1.0.0"
|
||||
}
|
||||
},
|
||||
|
||||
11
package.json
11
package.json
@@ -35,25 +35,27 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@edx/brand": "npm:@edx/brand-openedx@1.1.0",
|
||||
"@edx/frontend-component-cookie-policy-banner": "2.1.8",
|
||||
"@edx/frontend-component-header": "2.2.4",
|
||||
"@edx/frontend-platform": "1.8.4",
|
||||
"@edx/frontend-component-cookie-policy-banner": "2.1.8",
|
||||
"@edx/paragon": "13.16.1",
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.32",
|
||||
"@fortawesome/free-brands-svg-icons": "5.15.1",
|
||||
"@fortawesome/free-regular-svg-icons": "5.15.1",
|
||||
"@fortawesome/free-solid-svg-icons": "5.15.1",
|
||||
"@fortawesome/react-fontawesome": "0.1.13",
|
||||
"core-js": "3.9.1",
|
||||
"classnames": "2.2.6",
|
||||
"core-js": "3.9.1",
|
||||
"extract-react-intl-messages": "4.1.1",
|
||||
"form-urlencoded": "4.2.1",
|
||||
"formik": "2.2.6",
|
||||
"lodash.camelcase": "4.3.0",
|
||||
"lodash.snakecase": "4.1.1",
|
||||
"prop-types": "15.7.2",
|
||||
"query-string": "5.1.1",
|
||||
"react": "16.14.0",
|
||||
"react-dom": "16.14.0",
|
||||
"react-helmet": "6.1.0",
|
||||
"react-loading-skeleton": "2.1.1",
|
||||
"react-redux": "7.2.2",
|
||||
"react-router": "5.2.0",
|
||||
@@ -64,9 +66,8 @@
|
||||
"redux-mock-store": "1.5.4",
|
||||
"redux-saga": "1.1.3",
|
||||
"redux-thunk": "2.3.0",
|
||||
"reselect": "4.0.0",
|
||||
"react-helmet": "6.1.0",
|
||||
"regenerator-runtime": "0.13.7"
|
||||
"regenerator-runtime": "0.13.7",
|
||||
"reselect": "4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@edx/frontend-build": "5.6.8",
|
||||
|
||||
@@ -24,3 +24,7 @@ export const VALID_EMAIL_REGEX = '(^[-!#$%&\'*+/=?^_`{}|~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'];
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
// Utility functions
|
||||
|
||||
import * as QueryString from 'query-string';
|
||||
import { AUTH_PARAMS } from '../constants';
|
||||
|
||||
export default function processLink(link) {
|
||||
let matches;
|
||||
link.replace(/(.*?)<a href=["']([^"']*).*?>([^<]+)<\/a>(.*)/g, function () { // eslint-disable-line func-names
|
||||
@@ -40,3 +43,25 @@ export const processTpaHintURL = (params) => {
|
||||
}
|
||||
return tpaHint;
|
||||
};
|
||||
|
||||
export const updatePathWithQueryParams = (path) => {
|
||||
const queryParams = window.location.search;
|
||||
|
||||
if (!queryParams) {
|
||||
return path;
|
||||
}
|
||||
|
||||
return `${path}${queryParams}`;
|
||||
};
|
||||
|
||||
export const getAllPossibleQueryParam = () => {
|
||||
const urlParams = QueryString.parse(document.location.search);
|
||||
const params = {};
|
||||
Object.entries(urlParams).forEach(([key, value]) => {
|
||||
if (AUTH_PARAMS.indexOf(key) > -1) {
|
||||
params[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
return params;
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import processLink from './dataUtils';
|
||||
import { LOGIN_PAGE } from '../constants';
|
||||
import processLink, { updatePathWithQueryParams } from './dataUtils';
|
||||
|
||||
describe('processLink', () => {
|
||||
it('should use the provided processLink function to', () => {
|
||||
@@ -12,3 +13,20 @@ describe('processLink', () => {
|
||||
expect(matches[2]).toEqual(expectedText);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updatePathWithQueryParams', () => {
|
||||
it('should append query params into the path', () => {
|
||||
const params = '?course_id=testCourseId';
|
||||
const expectedPath = `${LOGIN_PAGE}${params}`;
|
||||
|
||||
Object.defineProperty(window, 'location', {
|
||||
value: {
|
||||
href: 'http://localhost/',
|
||||
search: params,
|
||||
},
|
||||
});
|
||||
const updatedPath = updatePathWithQueryParams(LOGIN_PAGE);
|
||||
|
||||
expect(updatedPath).toEqual(expectedPath);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
export { default, getTpaProvider, processTpaHintURL } from './dataUtils';
|
||||
export {
|
||||
default,
|
||||
getTpaProvider,
|
||||
processTpaHintURL,
|
||||
updatePathWithQueryParams,
|
||||
getAllPossibleQueryParam,
|
||||
} from './dataUtils';
|
||||
export { default as AsyncActionType } from './reduxUtils';
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
import APIFailureMessage from '../common-components/APIFailureMessage';
|
||||
import { INTERNAL_SERVER_ERROR, LOGIN_PAGE, VALID_EMAIL_REGEX } from '../data/constants';
|
||||
import LoginHelpLinks from '../login/LoginHelpLinks';
|
||||
import { updatePathWithQueryParams } from '../data/utils';
|
||||
|
||||
const ForgotPasswordPage = (props) => {
|
||||
const { intl, status } = props;
|
||||
@@ -92,7 +93,7 @@ const ForgotPasswordPage = (props) => {
|
||||
{ siteName: getConfig().SITE_NAME })}
|
||||
</title>
|
||||
</Helmet>
|
||||
{status === 'complete' ? <Redirect to={LOGIN_PAGE} /> : null}
|
||||
{status === 'complete' ? <Redirect to={updatePathWithQueryParams(LOGIN_PAGE)} /> : null}
|
||||
<div className="d-flex justify-content-center m-4">
|
||||
<div className="d-flex flex-column">
|
||||
<Form className="mw-500">
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
RESET_PAGE,
|
||||
} from '../data/constants';
|
||||
import messages from './messages';
|
||||
import { updatePathWithQueryParams } from '../data/utils';
|
||||
|
||||
const LoginHelpLinks = (props) => {
|
||||
const { intl, page } = props;
|
||||
@@ -31,7 +32,7 @@ const LoginHelpLinks = (props) => {
|
||||
const forgotPasswordLink = () => (
|
||||
<Hyperlink
|
||||
className="field-link"
|
||||
destination={RESET_PAGE}
|
||||
destination={updatePathWithQueryParams(RESET_PAGE)}
|
||||
onClick={handleForgotPasswordLinkClickEvent}
|
||||
>
|
||||
{intl.formatMessage(messages['forgot.password.link'])}
|
||||
@@ -39,7 +40,7 @@ const LoginHelpLinks = (props) => {
|
||||
);
|
||||
|
||||
const signUpLink = () => (
|
||||
<Hyperlink className="field-link" destination={REGISTER_PAGE}>
|
||||
<Hyperlink className="field-link" destination={updatePathWithQueryParams(REGISTER_PAGE)}>
|
||||
{intl.formatMessage(messages['register.link'])}
|
||||
</Hyperlink>
|
||||
);
|
||||
|
||||
@@ -32,7 +32,9 @@ import {
|
||||
DEFAULT_REDIRECT_URL, DEFAULT_STATE, LOGIN_PAGE, REGISTER_PAGE, ENTERPRISE_LOGIN_URL, PENDING_STATE,
|
||||
} from '../data/constants';
|
||||
import { forgotPasswordResultSelector } from '../forgot-password';
|
||||
import { getTpaProvider, processTpaHintURL } from '../data/utils';
|
||||
import {
|
||||
getTpaProvider, processTpaHintURL, updatePathWithQueryParams, getAllPossibleQueryParam,
|
||||
} from '../data/utils';
|
||||
|
||||
class LoginPage extends React.Component {
|
||||
constructor(props, context) {
|
||||
@@ -90,16 +92,10 @@ class LoginPage extends React.Component {
|
||||
return;
|
||||
}
|
||||
|
||||
const params = (new URL(document.location)).searchParams;
|
||||
const payload = { email, password };
|
||||
const next = params.get('next');
|
||||
const courseId = params.get('course_id');
|
||||
if (next) {
|
||||
payload.next = next;
|
||||
}
|
||||
if (courseId) {
|
||||
payload.course_id = courseId;
|
||||
}
|
||||
let payload = { email, password };
|
||||
const postParams = getAllPossibleQueryParam();
|
||||
|
||||
payload = { ...payload, ...postParams };
|
||||
this.props.loginRequest(payload);
|
||||
}
|
||||
|
||||
@@ -203,7 +199,11 @@ class LoginPage extends React.Component {
|
||||
) : null}
|
||||
<p>
|
||||
{intl.formatMessage(messages['first.time.here'])}
|
||||
<Hyperlink className="ml-1" destination={REGISTER_PAGE} onClick={this.handleCreateAccountLinkClickEvent}>
|
||||
<Hyperlink
|
||||
className="ml-1"
|
||||
destination={updatePathWithQueryParams(REGISTER_PAGE)}
|
||||
onClick={this.handleCreateAccountLinkClickEvent}
|
||||
>
|
||||
{intl.formatMessage(messages['create.an.account'])}.
|
||||
</Hyperlink>
|
||||
</p>
|
||||
|
||||
@@ -31,7 +31,9 @@ import EnterpriseSSO from '../common-components/EnterpriseSSO';
|
||||
import {
|
||||
DEFAULT_REDIRECT_URL, DEFAULT_STATE, LOGIN_PAGE, PENDING_STATE, REGISTER_PAGE,
|
||||
} from '../data/constants';
|
||||
import { getTpaProvider, processTpaHintURL } from '../data/utils';
|
||||
import {
|
||||
getTpaProvider, processTpaHintURL, updatePathWithQueryParams, getAllPossibleQueryParam,
|
||||
} from '../data/utils';
|
||||
|
||||
class RegistrationPage extends React.Component {
|
||||
constructor(props, context) {
|
||||
@@ -153,8 +155,7 @@ class RegistrationPage extends React.Component {
|
||||
|
||||
handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
const params = (new URL(document.location)).searchParams;
|
||||
const payload = {
|
||||
let payload = {
|
||||
name: this.state.name,
|
||||
username: this.state.username,
|
||||
email: this.state.email,
|
||||
@@ -168,14 +169,8 @@ class RegistrationPage extends React.Component {
|
||||
payload.password = this.state.password;
|
||||
}
|
||||
|
||||
const next = params.get('next');
|
||||
const courseId = params.get('course_id');
|
||||
if (next) {
|
||||
payload.next = next;
|
||||
}
|
||||
if (courseId) {
|
||||
payload.course_id = courseId;
|
||||
}
|
||||
const postParams = getAllPossibleQueryParam();
|
||||
payload = { ...payload, ...postParams };
|
||||
|
||||
let finalValidation = this.state.formValid;
|
||||
if (!this.state.formValid) {
|
||||
@@ -461,7 +456,11 @@ class RegistrationPage extends React.Component {
|
||||
)}
|
||||
<p>
|
||||
{intl.formatMessage(messages['already.have.an.edx.account'])}
|
||||
<Hyperlink className="ml-1" destination={LOGIN_PAGE} onClick={this.handleLoginLinkClickEvent}>
|
||||
<Hyperlink
|
||||
className="ml-1"
|
||||
destination={updatePathWithQueryParams(LOGIN_PAGE)}
|
||||
onClick={this.handleLoginLinkClickEvent}
|
||||
>
|
||||
{intl.formatMessage(messages['sign.in.hyperlink'])}
|
||||
</Hyperlink>
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user