fix: Use ProgressiveProfiling instead of WelcomePage (#644)

This commit is contained in:
Attiya Ishaque
2022-09-19 14:21:41 +05:00
committed by GitHub
parent f2989e836a
commit 766438dcf3
5 changed files with 4 additions and 416 deletions

View File

@@ -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} />

View File

@@ -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 ? (

View File

@@ -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));

View File

@@ -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';

View File

@@ -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();
});
});