fix: Use ProgressiveProfiling instead of WelcomePage (#644)
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { AppProvider } from '@edx/frontend-platform/react';
|
||||
import { Redirect, Route, Switch } from 'react-router-dom';
|
||||
|
||||
@@ -14,7 +13,7 @@ import {
|
||||
import { updatePathWithQueryParams } from './data/utils';
|
||||
import ForgotPasswordPage from './forgot-password';
|
||||
import ResetPasswordPage from './reset-password';
|
||||
import WelcomePage, { ProgressiveProfiling } from './welcome';
|
||||
import { ProgressiveProfiling } from './welcome';
|
||||
import './index.scss';
|
||||
|
||||
registerIcons();
|
||||
@@ -29,11 +28,7 @@ const MainApp = () => (
|
||||
<UnAuthOnlyRoute exact path={REGISTER_PAGE} component={Logistration} />
|
||||
<UnAuthOnlyRoute exact path={RESET_PAGE} component={ForgotPasswordPage} />
|
||||
<Route exact path={PASSWORD_RESET_CONFIRM} component={ResetPasswordPage} />
|
||||
<Route
|
||||
exact
|
||||
path={WELCOME_PAGE}
|
||||
component={(getConfig().ENABLE_DYNAMIC_REGISTRATION_FIELDS) ? ProgressiveProfiling : WelcomePage}
|
||||
/>
|
||||
<Route exact path={WELCOME_PAGE} component={ProgressiveProfiling} />
|
||||
<Route path={PAGE_NOT_FOUND} component={NotFoundPage} />
|
||||
<Route path="*">
|
||||
<Redirect to={PAGE_NOT_FOUND} />
|
||||
|
||||
@@ -743,13 +743,8 @@ class RegistrationPage extends React.Component {
|
||||
redirectUrl={this.props.registrationResult.redirectUrl}
|
||||
finishAuthUrl={finishAuthUrl}
|
||||
optionalFields={this.props.optionalFields}
|
||||
redirectToWelcomePage={
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
getConfig().ENABLE_PROGRESSIVE_PROFILING
|
||||
? (getConfig().ENABLE_DYNAMIC_REGISTRATION_FIELDS
|
||||
? Object.keys(this.props.optionalFields).length !== 0 : true
|
||||
) : false
|
||||
}
|
||||
redirectToWelcomePage={getConfig().ENABLE_PROGRESSIVE_PROFILING
|
||||
&& Object.keys(this.props.optionalFields).length !== 0}
|
||||
/>
|
||||
<div className="mw-xs mt-3">
|
||||
{this.state.errorCode ? (
|
||||
|
||||
@@ -1,280 +0,0 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { getConfig, snakeCaseObject } from '@edx/frontend-platform';
|
||||
import { sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||
import {
|
||||
AxiosJwtAuthService,
|
||||
configure as configureAuth,
|
||||
ensureAuthenticatedUser,
|
||||
getAuthenticatedUser,
|
||||
hydrateAuthenticatedUser,
|
||||
} from '@edx/frontend-platform/auth';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { getLoggingService } from '@edx/frontend-platform/logging';
|
||||
import {
|
||||
Alert,
|
||||
Form,
|
||||
Hyperlink,
|
||||
Icon,
|
||||
StatefulButton,
|
||||
} from '@edx/paragon';
|
||||
import { Error, ExpandMore } from '@edx/paragon/icons';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Helmet } from 'react-helmet';
|
||||
|
||||
import BaseComponent from '../base-component';
|
||||
import { RedirectLogistration } from '../common-components';
|
||||
import { DEFAULT_REDIRECT_URL, DEFAULT_STATE } from '../data/constants';
|
||||
import { EDUCATION_LEVELS, GENDER_OPTIONS, YEAR_OF_BIRTH_OPTIONS } from '../register/data/constants';
|
||||
import { saveUserProfile } from './data/actions';
|
||||
import { welcomePageSelector } from './data/selectors';
|
||||
import messages from './messages';
|
||||
import WelcomePageModal from './WelcomePageModal';
|
||||
|
||||
const WelcomePage = (props) => {
|
||||
const { intl, submitState, showError } = props;
|
||||
|
||||
const [ready, setReady] = useState(false);
|
||||
const [registrationResult, setRegistrationResult] = useState({ redirectUrl: '' });
|
||||
const [values, setValues] = useState({ levelOfEducation: '', yearOfBirth: '', gender: '' });
|
||||
const [openDialog, setOpenDialog] = useState(false);
|
||||
|
||||
const DASHBOARD_URL = getConfig().LMS_BASE_URL.concat(DEFAULT_REDIRECT_URL);
|
||||
|
||||
useEffect(() => {
|
||||
window.optimizely = window.optimizely || [];
|
||||
configureAuth(AxiosJwtAuthService, { loggingService: getLoggingService, config: getConfig() });
|
||||
ensureAuthenticatedUser(DASHBOARD_URL).then(() => {
|
||||
hydrateAuthenticatedUser().then(() => {
|
||||
setReady(true);
|
||||
});
|
||||
});
|
||||
|
||||
if (props.location.state && props.location.state.registrationResult) {
|
||||
setRegistrationResult(props.location.state.registrationResult);
|
||||
sendPageEvent('login_and_registration', 'welcome');
|
||||
}
|
||||
}, [DASHBOARD_URL, props.location.state]);
|
||||
|
||||
if (!props.location.state || !props.location.state.registrationResult) {
|
||||
global.location.assign(DASHBOARD_URL);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!ready) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (getConfig().ENABLE_COPPA_COMPLIANCE && EDUCATION_LEVELS) {
|
||||
const index = EDUCATION_LEVELS.indexOf('el');
|
||||
EDUCATION_LEVELS.splice(index, 1);
|
||||
}
|
||||
|
||||
const getOptions = (fieldName) => {
|
||||
const options = {
|
||||
yearOfBirth: YEAR_OF_BIRTH_OPTIONS.map(({ value, label }) => (
|
||||
<option className="data-hj-suppress" key={value} value={value}>{label}</option>
|
||||
)),
|
||||
levelOfEducation: EDUCATION_LEVELS.map(key => (
|
||||
<option className="data-hj-suppress" key={key} value={key}>
|
||||
{intl.formatMessage(messages[`education.levels.${key || 'label'}`])}
|
||||
</option>
|
||||
)),
|
||||
gender: GENDER_OPTIONS.map(key => (
|
||||
<option className="data-hj-suppress" key={key} value={key}>
|
||||
{intl.formatMessage(messages[`gender.options.${key || 'label'}`])}
|
||||
</option>
|
||||
)),
|
||||
};
|
||||
|
||||
return options[fieldName];
|
||||
};
|
||||
|
||||
const handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
const payload = {};
|
||||
const authenticatedUser = getAuthenticatedUser();
|
||||
|
||||
['yearOfBirth', 'gender', 'levelOfEducation'].forEach(fieldName => {
|
||||
if (values[fieldName]) {
|
||||
payload[fieldName] = values[fieldName];
|
||||
}
|
||||
});
|
||||
|
||||
props.saveUserProfile(authenticatedUser.username, snakeCaseObject(payload));
|
||||
|
||||
sendTrackEvent(
|
||||
'edx.bi.welcome.page.submit.clicked',
|
||||
{
|
||||
isGenderSelected: !!payload.gender,
|
||||
isYearOfBirthSelected: !!payload.yearOfBirth,
|
||||
isLevelOfEducationSelected: !!payload.levelOfEducation,
|
||||
},
|
||||
);
|
||||
|
||||
window.optimizely.push({
|
||||
type: 'event',
|
||||
eventName: 'authn_welcome_page_submit_btn_clicked',
|
||||
});
|
||||
};
|
||||
|
||||
const handleSkip = (e) => {
|
||||
e.preventDefault();
|
||||
setOpenDialog(true);
|
||||
sendTrackEvent('edx.bi.welcome.page.skip.link.clicked');
|
||||
|
||||
window.optimizely.push({
|
||||
type: 'event',
|
||||
eventName: 'authn_welcome_page_skip_btn_clicked',
|
||||
});
|
||||
};
|
||||
|
||||
const onChangeHandler = (e) => {
|
||||
setValues({ ...values, [e.target.name]: e.target.value });
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<BaseComponent showWelcomeBanner>
|
||||
<Helmet>
|
||||
<title>{intl.formatMessage(messages['progressive.profiling.page.title'],
|
||||
{ siteName: getConfig().SITE_NAME })}
|
||||
</title>
|
||||
</Helmet>
|
||||
<WelcomePageModal isOpen={openDialog} redirectUrl={registrationResult.redirectUrl} />
|
||||
{props.shouldRedirect ? (
|
||||
<RedirectLogistration
|
||||
success
|
||||
redirectUrl={registrationResult.redirectUrl}
|
||||
/>
|
||||
) : null}
|
||||
<div className="mw-xs welcome-page-content">
|
||||
<div className="welcome-page-heading">
|
||||
<h2 className="pp-page-heading text-primary">{intl.formatMessage(messages['progressive.profiling.page.heading'])}</h2>
|
||||
</div>
|
||||
<hr className="border-light-700 mb-4" />
|
||||
{showError ? (
|
||||
<Alert id="welcome-page-errors" className="mb-3" variant="danger" icon={Error}>
|
||||
<Alert.Heading>{intl.formatMessage(messages['welcome.page.error.heading'])}</Alert.Heading>
|
||||
<p>{intl.formatMessage(messages['welcome.page.error.message'])}</p>
|
||||
</Alert>
|
||||
) : null}
|
||||
<Form id="welcome-page-profile-form" name="welcome-page-profile-form">
|
||||
<Form.Group controlId="levelOfEducation">
|
||||
<Form.Control
|
||||
as="select"
|
||||
name="levelOfEducation"
|
||||
value={values.levelOfEducation}
|
||||
onChange={(e) => onChangeHandler(e)}
|
||||
trailingElement={<Icon src={ExpandMore} />}
|
||||
floatingLabel={intl.formatMessage(messages['education.levels.label'])}
|
||||
>
|
||||
{getOptions('levelOfEducation')}
|
||||
</Form.Control>
|
||||
</Form.Group>
|
||||
{(!getConfig().ENABLE_COPPA_COMPLIANCE)
|
||||
&& (
|
||||
<Form.Group controlId="yearOfBirth">
|
||||
<Form.Control
|
||||
as="select"
|
||||
name="yearOfBirth"
|
||||
value={values.yearOfBirth}
|
||||
onChange={(e) => onChangeHandler(e)}
|
||||
trailingElement={<Icon src={ExpandMore} />}
|
||||
floatingLabel={intl.formatMessage(messages['year.of.birth.label'])}
|
||||
>
|
||||
<option value="">{intl.formatMessage(messages['year.of.birth.label'])}</option>
|
||||
{getOptions('yearOfBirth')}
|
||||
</Form.Control>
|
||||
</Form.Group>
|
||||
)}
|
||||
<Form.Group controlId="gender" className="mb-3">
|
||||
<Form.Control
|
||||
as="select"
|
||||
name="gender"
|
||||
value={values.gender}
|
||||
onChange={(e) => onChangeHandler(e)}
|
||||
trailingElement={<Icon src={ExpandMore} />}
|
||||
floatingLabel={intl.formatMessage(messages['gender.options.label'])}
|
||||
>
|
||||
{getOptions('gender')}
|
||||
</Form.Control>
|
||||
</Form.Group>
|
||||
<span className="progressive-profiling-support">
|
||||
<Hyperlink
|
||||
isInline
|
||||
variant="muted"
|
||||
destination={getConfig().WELCOME_PAGE_SUPPORT_LINK}
|
||||
target="_blank"
|
||||
showLaunchIcon={false}
|
||||
onClick={() => (sendTrackEvent('edx.bi.welcome.page.support.link.clicked'))}
|
||||
>
|
||||
{intl.formatMessage(messages['optional.fields.information.link'])}
|
||||
</Hyperlink>
|
||||
</span>
|
||||
<div className="d-flex mt-4">
|
||||
<StatefulButton
|
||||
name="submit-profile"
|
||||
id="submit-profile"
|
||||
type="submit"
|
||||
variant="brand"
|
||||
className="login-button-width"
|
||||
state={submitState}
|
||||
labels={{
|
||||
default: intl.formatMessage(messages['optional.fields.submit.button']),
|
||||
pending: '',
|
||||
}}
|
||||
onClick={handleSubmit}
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
/>
|
||||
<StatefulButton
|
||||
id="skip-profile"
|
||||
name="skip-profile"
|
||||
className="text-gray-700 font-weight-500"
|
||||
type="submit"
|
||||
variant="link"
|
||||
labels={{
|
||||
default: intl.formatMessage(messages['optional.fields.skip.button']),
|
||||
}}
|
||||
onClick={handleSkip}
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
/>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</BaseComponent>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
WelcomePage.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
location: PropTypes.shape({
|
||||
state: PropTypes.object,
|
||||
}),
|
||||
saveUserProfile: PropTypes.func.isRequired,
|
||||
showError: PropTypes.bool,
|
||||
shouldRedirect: PropTypes.bool,
|
||||
submitState: PropTypes.string,
|
||||
};
|
||||
|
||||
WelcomePage.defaultProps = {
|
||||
location: { state: {} },
|
||||
shouldRedirect: false,
|
||||
showError: false,
|
||||
submitState: DEFAULT_STATE,
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
shouldRedirect: welcomePageSelector(state).success,
|
||||
submitState: welcomePageSelector(state).submitState,
|
||||
showError: welcomePageSelector(state).showError,
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
{
|
||||
saveUserProfile,
|
||||
},
|
||||
)(injectIntl(WelcomePage));
|
||||
@@ -1,4 +1,3 @@
|
||||
export { default } from './WelcomePage';
|
||||
export { default as ProgressiveProfiling } from './ProgressiveProfiling';
|
||||
export { default as reducer } from './data/reducers';
|
||||
export { default as saga } from './data/sagas';
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import { mergeConfig } from '@edx/frontend-platform';
|
||||
import * as analytics from '@edx/frontend-platform/analytics';
|
||||
import * as auth from '@edx/frontend-platform/auth';
|
||||
import { configure, injectIntl, IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import { mount } from 'enzyme';
|
||||
import { act } from 'react-dom/test-utils';
|
||||
import configureStore from 'redux-mock-store';
|
||||
|
||||
import { saveUserProfile } from '../data/actions';
|
||||
import WelcomePage from '../WelcomePage';
|
||||
|
||||
const IntlWelcomePage = injectIntl(WelcomePage);
|
||||
const mockStore = configureStore();
|
||||
|
||||
jest.mock('@edx/frontend-platform/auth');
|
||||
jest.mock('@edx/frontend-platform/analytics');
|
||||
|
||||
analytics.sendTrackEvent = jest.fn();
|
||||
analytics.sendPageEvent = jest.fn();
|
||||
auth.configure = jest.fn();
|
||||
|
||||
auth.ensureAuthenticatedUser = jest.fn().mockImplementation(() => Promise.resolve(true));
|
||||
auth.hydrateAuthenticatedUser = jest.fn().mockImplementation(() => Promise.resolve(true));
|
||||
|
||||
describe('WelcomePageTests', () => {
|
||||
mergeConfig({
|
||||
WELCOME_PAGE_SUPPORT_LINK: 'http://localhost:1999/welcome',
|
||||
});
|
||||
|
||||
const registrationResult = { redirectUrl: 'http://localhost:18000/dashboard', success: true };
|
||||
const props = {
|
||||
location: {
|
||||
state: {
|
||||
registrationResult,
|
||||
},
|
||||
},
|
||||
};
|
||||
let store = {};
|
||||
|
||||
const reduxWrapper = children => (
|
||||
<IntlProvider locale="en">
|
||||
<Provider store={store}>{children}</Provider>
|
||||
</IntlProvider>
|
||||
);
|
||||
|
||||
const getWelcomePage = async () => {
|
||||
const welcomePage = mount(reduxWrapper(<IntlWelcomePage {...props} />));
|
||||
await act(async () => {
|
||||
await Promise.resolve(welcomePage);
|
||||
await new Promise(resolve => setImmediate(resolve));
|
||||
welcomePage.update();
|
||||
});
|
||||
|
||||
return welcomePage;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
store = mockStore({});
|
||||
auth.getAuthenticatedUser = jest.fn(() => ({ userId: 3, username: 'edX' }));
|
||||
configure({
|
||||
loggingService: { logError: jest.fn() },
|
||||
config: {
|
||||
ENVIRONMENT: 'production',
|
||||
LANGUAGE_PREFERENCE_COOKIE_NAME: 'yum',
|
||||
},
|
||||
messages: { 'es-419': {}, de: {}, 'en-us': {} },
|
||||
});
|
||||
});
|
||||
|
||||
it('should submit user profile details on form submission', async () => {
|
||||
const formPayload = {
|
||||
year_of_birth: 1997,
|
||||
level_of_education: 'other',
|
||||
gender: 'm',
|
||||
};
|
||||
store.dispatch = jest.fn(store.dispatch);
|
||||
const welcomePage = await getWelcomePage();
|
||||
|
||||
welcomePage.find('select#gender').simulate('change', { target: { value: 'm', name: 'gender' } });
|
||||
welcomePage.find('select#yearOfBirth').simulate('change', { target: { value: 1997, name: 'yearOfBirth' } });
|
||||
welcomePage.find('select#levelOfEducation').simulate('change', { target: { value: 'other', name: 'levelOfEducation' } });
|
||||
|
||||
welcomePage.find('button.btn-brand').simulate('click');
|
||||
expect(store.dispatch).toHaveBeenCalledWith(saveUserProfile('edX', formPayload));
|
||||
|
||||
const customProps = {
|
||||
isGenderSelected: true,
|
||||
isYearOfBirthSelected: true,
|
||||
isLevelOfEducationSelected: true,
|
||||
};
|
||||
expect(analytics.sendTrackEvent).toHaveBeenCalledWith('edx.bi.welcome.page.submit.clicked', customProps);
|
||||
});
|
||||
|
||||
it('should open modal on pressing skip for now button', async () => {
|
||||
const welcomePage = await getWelcomePage();
|
||||
|
||||
welcomePage.find('button.btn-link').simulate('click');
|
||||
expect(welcomePage.find('.pgn__modal-content-container').exists()).toBeTruthy();
|
||||
expect(analytics.sendTrackEvent).toHaveBeenCalledWith('edx.bi.welcome.page.skip.link.clicked');
|
||||
});
|
||||
|
||||
it('should send analytic event for support link click', async () => {
|
||||
const welcomePage = await getWelcomePage();
|
||||
|
||||
welcomePage.find('.progressive-profiling-support a[target="_blank"]').simulate('click');
|
||||
expect(analytics.sendTrackEvent).toHaveBeenCalledWith('edx.bi.welcome.page.support.link.clicked');
|
||||
});
|
||||
|
||||
it('should show error message when patch request fails', async () => {
|
||||
store = mockStore({
|
||||
welcomePage: {
|
||||
showError: true,
|
||||
},
|
||||
});
|
||||
const welcomePage = await getWelcomePage();
|
||||
expect(welcomePage.find('#welcome-page-errors').exists()).toBeTruthy();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user