feat: [VAN-953] update progressive profiling according to MFE API (#581)
This commit is contained in:
@@ -32,7 +32,7 @@ const MainApp = () => (
|
||||
<Route
|
||||
exact
|
||||
path={WELCOME_PAGE}
|
||||
component={(getConfig().SHOW_DYNAMIC_PROFILING_PAGE) ? ProgressiveProfiling : WelcomePage}
|
||||
component={(getConfig().ENABLE_DYNAMIC_REGISTRATION_FIELDS) ? ProgressiveProfiling : WelcomePage}
|
||||
/>
|
||||
<Route path={PAGE_NOT_FOUND} component={NotFoundPage} />
|
||||
<Route path="*">
|
||||
|
||||
@@ -10,7 +10,7 @@ import { setCookie } from '../data/utils';
|
||||
|
||||
function RedirectLogistration(props) {
|
||||
const {
|
||||
finishAuthUrl, redirectUrl, redirectToWelcomePage, success,
|
||||
finishAuthUrl, redirectUrl, redirectToWelcomePage, success, optionalFields,
|
||||
} = props;
|
||||
let finalRedirectUrl = '';
|
||||
|
||||
@@ -30,7 +30,16 @@ function RedirectLogistration(props) {
|
||||
// use this component to redirect WelcomePage after successful registration
|
||||
// return <Redirect to={WELCOME_PAGE} />;
|
||||
const registrationResult = { redirectUrl: finalRedirectUrl, success };
|
||||
return <Redirect to={{ pathname: WELCOME_PAGE, state: { registrationResult } }} />;
|
||||
return (
|
||||
<Redirect to={{
|
||||
pathname: WELCOME_PAGE,
|
||||
state: {
|
||||
registrationResult,
|
||||
optionalFields,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
window.location.href = finalRedirectUrl;
|
||||
@@ -43,6 +52,7 @@ RedirectLogistration.defaultProps = {
|
||||
success: false,
|
||||
redirectUrl: '',
|
||||
redirectToWelcomePage: false,
|
||||
optionalFields: {},
|
||||
};
|
||||
|
||||
RedirectLogistration.propTypes = {
|
||||
@@ -50,6 +60,7 @@ RedirectLogistration.propTypes = {
|
||||
success: PropTypes.bool,
|
||||
redirectUrl: PropTypes.string,
|
||||
redirectToWelcomePage: PropTypes.bool,
|
||||
optionalFields: PropTypes.shape({}),
|
||||
};
|
||||
|
||||
export default RedirectLogistration;
|
||||
|
||||
@@ -12,9 +12,9 @@ export const getThirdPartyAuthContextBegin = () => ({
|
||||
type: THIRD_PARTY_AUTH_CONTEXT.BEGIN,
|
||||
});
|
||||
|
||||
export const getThirdPartyAuthContextSuccess = (fieldDescriptions, thirdPartyAuthContext) => ({
|
||||
export const getThirdPartyAuthContextSuccess = (fieldDescriptions, optionalFields, thirdPartyAuthContext) => ({
|
||||
type: THIRD_PARTY_AUTH_CONTEXT.SUCCESS,
|
||||
payload: { fieldDescriptions, thirdPartyAuthContext },
|
||||
payload: { fieldDescriptions, optionalFields, thirdPartyAuthContext },
|
||||
});
|
||||
|
||||
export const getThirdPartyAuthContextFailure = () => ({
|
||||
|
||||
@@ -5,6 +5,7 @@ import { PENDING_STATE, COMPLETE_STATE } from '../../data/constants';
|
||||
export const defaultState = {
|
||||
extendedProfile: [],
|
||||
fieldDescriptions: {},
|
||||
optionalFields: {},
|
||||
thirdPartyAuthApiStatus: null,
|
||||
};
|
||||
|
||||
@@ -20,6 +21,7 @@ const reducer = (state = defaultState, action) => {
|
||||
...state,
|
||||
extendedProfile: action.payload.fieldDescriptions.extendedProfile,
|
||||
fieldDescriptions: action.payload.fieldDescriptions.fields,
|
||||
optionalFields: action.payload.optionalFields,
|
||||
thirdPartyAuthContext: action.payload.thirdPartyAuthContext,
|
||||
thirdPartyAuthApiStatus: COMPLETE_STATE,
|
||||
};
|
||||
|
||||
@@ -18,10 +18,12 @@ import {
|
||||
export function* fetchThirdPartyAuthContext(action) {
|
||||
try {
|
||||
yield put(getThirdPartyAuthContextBegin());
|
||||
const { fieldDescriptions, thirdPartyAuthContext } = yield call(getThirdPartyAuthContext, action.payload.urlParams);
|
||||
const { fieldDescriptions, optionalFields, thirdPartyAuthContext } = yield call(
|
||||
getThirdPartyAuthContext, action.payload.urlParams,
|
||||
);
|
||||
|
||||
yield put(getThirdPartyAuthContextSuccess(
|
||||
fieldDescriptions, thirdPartyAuthContext,
|
||||
fieldDescriptions, optionalFields, thirdPartyAuthContext,
|
||||
));
|
||||
} catch (e) {
|
||||
yield put(getThirdPartyAuthContextFailure());
|
||||
|
||||
@@ -18,3 +18,8 @@ export const extendedProfileSelector = createSelector(
|
||||
commonComponentsSelector,
|
||||
commonComponents => commonComponents.extendedProfile,
|
||||
);
|
||||
|
||||
export const optionalFieldsSelector = createSelector(
|
||||
commonComponentsSelector,
|
||||
commonComponents => commonComponents.optionalFields,
|
||||
);
|
||||
|
||||
@@ -19,6 +19,7 @@ export async function getThirdPartyAuthContext(urlParams) {
|
||||
});
|
||||
return {
|
||||
fieldDescriptions: data.registration_fields || {},
|
||||
optionalFields: data.optional_fields || {},
|
||||
// For backward compatibility with the API, once https://github.com/openedx/edx-platform/pull/30198 is merged
|
||||
// and deployed update it to use data.context_data
|
||||
thirdPartyAuthContext: camelCaseObject(
|
||||
|
||||
@@ -26,7 +26,11 @@ describe('fetchThirdPartyAuthContext', () => {
|
||||
|
||||
it('should call service and dispatch success action', async () => {
|
||||
const getThirdPartyAuthContext = jest.spyOn(api, 'getThirdPartyAuthContext')
|
||||
.mockImplementation(() => Promise.resolve({ thirdPartyAuthContext: data, fieldDescriptions: {} }));
|
||||
.mockImplementation(() => Promise.resolve({
|
||||
thirdPartyAuthContext: data,
|
||||
fieldDescriptions: {},
|
||||
optionalFields: {},
|
||||
}));
|
||||
|
||||
const dispatched = [];
|
||||
await runSaga(
|
||||
@@ -38,7 +42,7 @@ describe('fetchThirdPartyAuthContext', () => {
|
||||
expect(getThirdPartyAuthContext).toHaveBeenCalledTimes(1);
|
||||
expect(dispatched).toEqual([
|
||||
actions.getThirdPartyAuthContextBegin(),
|
||||
actions.getThirdPartyAuthContextSuccess({}, data),
|
||||
actions.getThirdPartyAuthContextSuccess({}, {}, data),
|
||||
]);
|
||||
getThirdPartyAuthContext.mockClear();
|
||||
});
|
||||
|
||||
@@ -40,6 +40,7 @@ import { getThirdPartyAuthContext } from '../common-components/data/actions';
|
||||
import {
|
||||
extendedProfileSelector,
|
||||
fieldDescriptionSelector,
|
||||
optionalFieldsSelector,
|
||||
thirdPartyAuthContextSelector,
|
||||
} from '../common-components/data/selectors';
|
||||
import EnterpriseSSO from '../common-components/EnterpriseSSO';
|
||||
@@ -64,6 +65,7 @@ class RegistrationPage extends React.Component {
|
||||
// permanent part of Authn and remove extra code
|
||||
this.showDynamicRegistrationFields = getConfig().ENABLE_DYNAMIC_REGISTRATION_FIELDS;
|
||||
this.tpaHint = getTpaHint();
|
||||
this.isRegistered = true;
|
||||
this.state = {
|
||||
country: '',
|
||||
email: '',
|
||||
@@ -107,6 +109,7 @@ class RegistrationPage extends React.Component {
|
||||
if (this.tpaHint) {
|
||||
payload.tpa_hint = this.tpaHint;
|
||||
}
|
||||
payload.is_registered = this.isRegistered;
|
||||
this.props.resetRegistrationForm();
|
||||
this.props.getThirdPartyAuthContext(payload);
|
||||
this.getExperiments();
|
||||
@@ -655,7 +658,14 @@ class RegistrationPage extends React.Component {
|
||||
success={this.props.registrationResult.success}
|
||||
redirectUrl={this.props.registrationResult.redirectUrl}
|
||||
finishAuthUrl={finishAuthUrl}
|
||||
redirectToWelcomePage={getConfig().ENABLE_PROGRESSIVE_PROFILING}
|
||||
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
|
||||
}
|
||||
/>
|
||||
<div className="mw-xs mt-3">
|
||||
{this.state.errorCode ? (
|
||||
@@ -832,6 +842,7 @@ class RegistrationPage extends React.Component {
|
||||
RegistrationPage.defaultProps = {
|
||||
extendedProfile: [],
|
||||
fieldDescriptions: {},
|
||||
optionalFields: {},
|
||||
registrationResult: null,
|
||||
registerNewUser: null,
|
||||
registrationErrorCode: null,
|
||||
@@ -853,6 +864,7 @@ RegistrationPage.defaultProps = {
|
||||
RegistrationPage.propTypes = {
|
||||
extendedProfile: PropTypes.arrayOf(PropTypes.string),
|
||||
fieldDescriptions: PropTypes.shape({}),
|
||||
optionalFields: PropTypes.shape({}),
|
||||
intl: intlShape.isRequired,
|
||||
getThirdPartyAuthContext: PropTypes.func.isRequired,
|
||||
registerNewUser: PropTypes.func,
|
||||
@@ -907,6 +919,7 @@ const mapStateToProps = state => {
|
||||
statusCode: state.register.statusCode,
|
||||
usernameSuggestions: usernameSuggestionsSelector(state),
|
||||
fieldDescriptions: fieldDescriptionSelector(state),
|
||||
optionalFields: optionalFieldsSelector(state),
|
||||
extendedProfile: extendedProfileSelector(state),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ import CookiePolicyBanner from '@edx/frontend-component-cookie-policy-banner';
|
||||
import { getConfig, mergeConfig } from '@edx/frontend-platform';
|
||||
import * as analytics from '@edx/frontend-platform/analytics';
|
||||
import { IntlProvider, injectIntl, configure } from '@edx/frontend-platform/i18n';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
import {
|
||||
clearUsernameSuggestions,
|
||||
@@ -22,7 +23,7 @@ import {
|
||||
import RegistrationFailureMessage from '../RegistrationFailure';
|
||||
import RegistrationPage from '../RegistrationPage';
|
||||
|
||||
import { COMPLETE_STATE, PENDING_STATE } from '../../data/constants';
|
||||
import { COMPLETE_STATE, PENDING_STATE, WELCOME_PAGE } from '../../data/constants';
|
||||
|
||||
jest.mock('@edx/frontend-platform/analytics');
|
||||
|
||||
@@ -857,5 +858,63 @@ describe('RegistrationPage', () => {
|
||||
|
||||
expect(registrationPage.find('#profession-error').last().text()).toEqual('Enter profession');
|
||||
});
|
||||
|
||||
it('should redirect to dashboard if features flags are configured but no optional fields are configured', () => {
|
||||
mergeConfig({
|
||||
ENABLE_PROGRESSIVE_PROFILING: true,
|
||||
ENABLE_DYNAMIC_REGISTRATION_FIELDS: true,
|
||||
});
|
||||
const dasboardUrl = 'http://test.com/testing-dashboard/';
|
||||
store = mockStore({
|
||||
...initialState,
|
||||
register: {
|
||||
...initialState.register,
|
||||
registrationResult: {
|
||||
success: true,
|
||||
redirectUrl: dasboardUrl,
|
||||
},
|
||||
commonComponents: {
|
||||
optionalFields: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
delete window.location;
|
||||
window.location = { href: getConfig().BASE_URL };
|
||||
renderer.create(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||
expect(window.location.href).toBe(dasboardUrl);
|
||||
});
|
||||
|
||||
it('should redirect to welcome page when optional fields are configured with feature flags', () => {
|
||||
mergeConfig({
|
||||
ENABLE_PROGRESSIVE_PROFILING: true,
|
||||
ENABLE_DYNAMIC_REGISTRATION_FIELDS: true,
|
||||
});
|
||||
store = mockStore({
|
||||
...initialState,
|
||||
commonComponents: {
|
||||
optionalFields: {
|
||||
country: { name: 'country', error_message: false },
|
||||
},
|
||||
},
|
||||
register: {
|
||||
...initialState.register,
|
||||
registrationResult: {
|
||||
success: true,
|
||||
},
|
||||
|
||||
},
|
||||
});
|
||||
|
||||
delete window.location;
|
||||
window.location = { href: getConfig().BASE_URL + WELCOME_PAGE };
|
||||
renderer.create(reduxWrapper(
|
||||
<IntlProvider locale="en">
|
||||
<MemoryRouter>
|
||||
<Provider store={store}><IntlRegistrationPage {...props} /></Provider>
|
||||
</MemoryRouter>
|
||||
</IntlProvider>,
|
||||
));
|
||||
expect(window.location.href).toBe(getConfig().BASE_URL + WELCOME_PAGE);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,17 +20,16 @@ import {
|
||||
Form,
|
||||
StatefulButton,
|
||||
Hyperlink,
|
||||
Spinner,
|
||||
} from '@edx/paragon';
|
||||
import { Error } from '@edx/paragon/icons';
|
||||
|
||||
import { getFieldData, saveUserProfile } from './data/actions';
|
||||
import { saveUserProfile } from './data/actions';
|
||||
import { welcomePageSelector } from './data/selectors';
|
||||
import messages from './messages';
|
||||
|
||||
import { RedirectLogistration } from '../common-components';
|
||||
import {
|
||||
DEFAULT_REDIRECT_URL, DEFAULT_STATE, FAILURE_STATE, COMPLETE_STATE,
|
||||
DEFAULT_REDIRECT_URL, DEFAULT_STATE, FAILURE_STATE,
|
||||
} from '../data/constants';
|
||||
import FormFieldRenderer from '../field-renderer';
|
||||
import WelcomePageModal from './WelcomePageModal';
|
||||
@@ -38,9 +37,10 @@ import BaseComponent from '../base-component';
|
||||
|
||||
const ProgressiveProfiling = (props) => {
|
||||
const {
|
||||
extendedProfile, fieldDescriptions, formRenderState, intl, submitState, showError,
|
||||
formRenderState, intl, submitState, showError,
|
||||
} = props;
|
||||
|
||||
const optionalFields = props.location.state.optionalFields.fields;
|
||||
const extendedProfile = props.location.state.optionalFields.extended_profile;
|
||||
const [ready, setReady] = useState(false);
|
||||
const [registrationResult, setRegistrationResult] = useState({ redirectUrl: '' });
|
||||
const [values, setValues] = useState({});
|
||||
@@ -52,7 +52,6 @@ const ProgressiveProfiling = (props) => {
|
||||
configureAuth(AxiosJwtAuthService, { loggingService: getLoggingService(), config: getConfig() });
|
||||
ensureAuthenticatedUser(DASHBOARD_URL).then(() => {
|
||||
hydrateAuthenticatedUser().then(() => {
|
||||
props.getFieldData();
|
||||
setReady(true);
|
||||
});
|
||||
});
|
||||
@@ -108,8 +107,8 @@ const ProgressiveProfiling = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
const formFields = Object.keys(fieldDescriptions).map((fieldName) => {
|
||||
const fieldData = fieldDescriptions[fieldName];
|
||||
const formFields = Object.keys(optionalFields).map((fieldName) => {
|
||||
const fieldData = optionalFields[fieldName];
|
||||
return (
|
||||
<span key={fieldData.name}>
|
||||
<FormFieldRenderer
|
||||
@@ -121,89 +120,86 @@ const ProgressiveProfiling = (props) => {
|
||||
);
|
||||
});
|
||||
|
||||
if (formRenderState === COMPLETE_STATE) {
|
||||
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 pp-page-content">
|
||||
<div className="pp-page-heading">
|
||||
<h2 className="h3 text-primary">{intl.formatMessage(messages['progressive.profiling.page.heading'])}</h2>
|
||||
</div>
|
||||
<hr className="border-light-700 mb-4" />
|
||||
{showError ? (
|
||||
<Alert id="pp-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>
|
||||
{formFields}
|
||||
<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 mb-3">
|
||||
<StatefulButton
|
||||
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
|
||||
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>
|
||||
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 pp-page-content">
|
||||
<div className="pp-page-heading">
|
||||
<h2 className="h3 text-primary">{intl.formatMessage(messages['progressive.profiling.page.heading'])}</h2>
|
||||
</div>
|
||||
</BaseComponent>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return <Spinner id="loader" animation="border" variant="primary" className="centered-align-spinner" />;
|
||||
<hr className="border-light-700 mb-4" />
|
||||
{showError ? (
|
||||
<Alert id="pp-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>
|
||||
{formFields}
|
||||
<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 mb-3">
|
||||
<StatefulButton
|
||||
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
|
||||
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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
ProgressiveProfiling.propTypes = {
|
||||
// eslint-disable-next-line react/no-unused-prop-types
|
||||
extendedProfile: PropTypes.arrayOf(PropTypes.string),
|
||||
fieldDescriptions: PropTypes.shape({}),
|
||||
optionalFields: PropTypes.shape({}),
|
||||
formRenderState: PropTypes.string.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
location: PropTypes.shape({
|
||||
state: PropTypes.object,
|
||||
}),
|
||||
getFieldData: PropTypes.func.isRequired,
|
||||
saveUserProfile: PropTypes.func.isRequired,
|
||||
showError: PropTypes.bool,
|
||||
shouldRedirect: PropTypes.bool,
|
||||
@@ -212,7 +208,7 @@ ProgressiveProfiling.propTypes = {
|
||||
|
||||
ProgressiveProfiling.defaultProps = {
|
||||
extendedProfile: [],
|
||||
fieldDescriptions: {},
|
||||
optionalFields: {},
|
||||
location: { state: {} },
|
||||
shouldRedirect: false,
|
||||
showError: false,
|
||||
@@ -220,8 +216,6 @@ ProgressiveProfiling.defaultProps = {
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
extendedProfile: welcomePageSelector(state).extendedProfile,
|
||||
fieldDescriptions: welcomePageSelector(state).fieldDescriptions,
|
||||
formRenderState: welcomePageSelector(state).formRenderState,
|
||||
shouldRedirect: welcomePageSelector(state).success,
|
||||
submitState: welcomePageSelector(state).submitState,
|
||||
@@ -232,6 +226,5 @@ export default connect(
|
||||
mapStateToProps,
|
||||
{
|
||||
saveUserProfile,
|
||||
getFieldData,
|
||||
},
|
||||
)(injectIntl(ProgressiveProfiling));
|
||||
|
||||
@@ -20,21 +20,3 @@ export const saveUserProfileSuccess = () => ({
|
||||
export const saveUserProfileFailure = () => ({
|
||||
type: SAVE_USER_PROFILE.FAILURE,
|
||||
});
|
||||
|
||||
// get field data from platform
|
||||
export const getFieldData = () => ({
|
||||
type: GET_FIELDS_DATA.BASE,
|
||||
});
|
||||
|
||||
export const getFieldDataBegin = () => ({
|
||||
type: GET_FIELDS_DATA.BEGIN,
|
||||
});
|
||||
|
||||
export const getFieldDataSuccess = (data, extendedProfile) => ({
|
||||
type: GET_FIELDS_DATA.SUCCESS,
|
||||
payload: { data, extendedProfile },
|
||||
});
|
||||
|
||||
export const getFieldDataFailure = () => ({
|
||||
type: GET_FIELDS_DATA.FAILURE,
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { GET_FIELDS_DATA, SAVE_USER_PROFILE } from './actions';
|
||||
import { SAVE_USER_PROFILE } from './actions';
|
||||
import {
|
||||
DEFAULT_STATE, PENDING_STATE, COMPLETE_STATE, FAILURE_STATE,
|
||||
DEFAULT_STATE, PENDING_STATE,
|
||||
} from '../../data/constants';
|
||||
|
||||
export const defaultState = {
|
||||
@@ -14,23 +14,6 @@ export const defaultState = {
|
||||
|
||||
const reducer = (state = defaultState, action) => {
|
||||
switch (action.type) {
|
||||
case GET_FIELDS_DATA.BEGIN:
|
||||
return {
|
||||
...state,
|
||||
formRenderState: PENDING_STATE,
|
||||
};
|
||||
case GET_FIELDS_DATA.SUCCESS:
|
||||
return {
|
||||
...state,
|
||||
extendedProfile: action.payload.extendedProfile,
|
||||
fieldDescriptions: action.payload.data,
|
||||
formRenderState: COMPLETE_STATE,
|
||||
};
|
||||
case GET_FIELDS_DATA.FAILURE:
|
||||
return {
|
||||
...state,
|
||||
formRenderState: FAILURE_STATE,
|
||||
};
|
||||
case SAVE_USER_PROFILE.BEGIN:
|
||||
return {
|
||||
...state,
|
||||
|
||||
@@ -1,17 +1,13 @@
|
||||
import { call, put, takeEvery } from 'redux-saga/effects';
|
||||
|
||||
import {
|
||||
GET_FIELDS_DATA,
|
||||
getFieldDataBegin,
|
||||
getFieldDataFailure,
|
||||
getFieldDataSuccess,
|
||||
SAVE_USER_PROFILE,
|
||||
saveUserProfileBegin,
|
||||
saveUserProfileFailure,
|
||||
saveUserProfileSuccess,
|
||||
} from './actions';
|
||||
|
||||
import { patchAccount, getOptionalFieldData } from './service';
|
||||
import { patchAccount } from './service';
|
||||
|
||||
export function* saveUserProfileInformation(action) {
|
||||
try {
|
||||
@@ -24,17 +20,6 @@ export function* saveUserProfileInformation(action) {
|
||||
}
|
||||
}
|
||||
|
||||
export function* getFieldData() {
|
||||
try {
|
||||
yield put(getFieldDataBegin());
|
||||
const data = yield call(getOptionalFieldData);
|
||||
yield put(getFieldDataSuccess(data.fields, data.extended_profile));
|
||||
} catch (e) {
|
||||
yield put(getFieldDataFailure());
|
||||
}
|
||||
}
|
||||
|
||||
export default function* saga() {
|
||||
yield takeEvery(SAVE_USER_PROFILE.BASE, saveUserProfileInformation);
|
||||
yield takeEvery(GET_FIELDS_DATA.BASE, getFieldData);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export async function patchAccount(username, commitValues) {
|
||||
const requestConfig = {
|
||||
headers: { 'Content-Type': 'application/merge-patch+json' },
|
||||
@@ -16,20 +17,3 @@ export async function patchAccount(username, commitValues) {
|
||||
throw (error);
|
||||
});
|
||||
}
|
||||
|
||||
export async function getOptionalFieldData() {
|
||||
const requestConfig = {
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
};
|
||||
|
||||
const { data } = await getAuthenticatedHttpClient()
|
||||
.get(
|
||||
`${getConfig().LMS_BASE_URL}/api/optional_fields`,
|
||||
requestConfig,
|
||||
)
|
||||
.catch((e) => {
|
||||
throw (e);
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -11,10 +11,10 @@ import * as auth from '@edx/frontend-platform/auth';
|
||||
import * as logging from '@edx/frontend-platform/logging';
|
||||
import { injectIntl, IntlProvider, configure } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { getFieldData, saveUserProfile } from '../data/actions';
|
||||
import { saveUserProfile } from '../data/actions';
|
||||
import ProgressiveProfiling from '../ProgressiveProfiling';
|
||||
import {
|
||||
COMPLETE_STATE, DEFAULT_REDIRECT_URL, FAILURE_STATE, PENDING_STATE,
|
||||
COMPLETE_STATE, DEFAULT_REDIRECT_URL, FAILURE_STATE,
|
||||
} from '../../data/constants';
|
||||
|
||||
const IntlProgressiveProfilingPage = injectIntl(ProgressiveProfiling);
|
||||
@@ -36,8 +36,18 @@ describe('ProgressiveProfilingTests', () => {
|
||||
mergeConfig({
|
||||
WELCOME_PAGE_SUPPORT_LINK: 'http://localhost:1999/welcome',
|
||||
});
|
||||
|
||||
const registrationResult = { redirectUrl: 'http://localhost:18000/dashboard', success: true };
|
||||
const registrationResult = { redirectUrl: getConfig().LMS_BASE_URL + DEFAULT_REDIRECT_URL, success: true };
|
||||
const fields = {
|
||||
company: { name: 'company', type: 'text', label: 'Company' },
|
||||
gender: {
|
||||
name: 'gender',
|
||||
type: 'select',
|
||||
label: 'Gender',
|
||||
options: [['m', 'Male'], ['f', 'Female'], ['o', 'Other/Prefer Not to Say']],
|
||||
},
|
||||
};
|
||||
const extendedProfile = ['company'];
|
||||
const optionalFields = { fields, extended_profile: extendedProfile };
|
||||
let props = {};
|
||||
let store = {};
|
||||
const DASHBOARD_URL = getConfig().LMS_BASE_URL.concat(DEFAULT_REDIRECT_URL);
|
||||
@@ -79,43 +89,13 @@ describe('ProgressiveProfilingTests', () => {
|
||||
location: {
|
||||
state: {
|
||||
registrationResult,
|
||||
optionalFields,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
it('should fire action to get form fields', async () => {
|
||||
store.dispatch = jest.fn(store.dispatch);
|
||||
const progressiveProfilingPage = await getProgressiveProfilingPage();
|
||||
|
||||
progressiveProfilingPage.find('button.btn-link').simulate('click');
|
||||
expect(store.dispatch).toHaveBeenCalledWith(getFieldData());
|
||||
});
|
||||
|
||||
it('should show spinner until fields are fetched', async () => {
|
||||
store = mockStore({
|
||||
welcomePage: {
|
||||
formRenderState: PENDING_STATE,
|
||||
},
|
||||
});
|
||||
const progressiveProfilingPage = await getProgressiveProfilingPage();
|
||||
expect(progressiveProfilingPage.find('#loader').exists()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render fields returned by backend api', async () => {
|
||||
store = mockStore({
|
||||
welcomePage: {
|
||||
...initialState.welcomePage,
|
||||
fieldDescriptions: {
|
||||
gender: {
|
||||
name: 'gender',
|
||||
type: 'select',
|
||||
label: 'Gender',
|
||||
options: [['m', 'Male'], ['f', 'Female'], ['o', 'Other/Prefer Not to Say']],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const progressiveProfilingPage = await getProgressiveProfilingPage();
|
||||
expect(progressiveProfilingPage.find('#gender').exists()).toBeTruthy();
|
||||
});
|
||||
@@ -126,27 +106,7 @@ describe('ProgressiveProfilingTests', () => {
|
||||
gender: 'm',
|
||||
extended_profile: [{ field_name: 'company', field_value: 'edx' }],
|
||||
};
|
||||
store = mockStore({
|
||||
welcomePage: {
|
||||
...initialState.welcomePage,
|
||||
extendedProfile: ['company'],
|
||||
fieldDescriptions: {
|
||||
gender: {
|
||||
name: 'gender',
|
||||
type: 'select',
|
||||
label: 'Gender',
|
||||
options: [['m', 'Male'], ['f', 'Female'], ['o', 'Other/Prefer Not to Say']],
|
||||
},
|
||||
company: {
|
||||
name: 'company',
|
||||
type: 'text',
|
||||
label: 'Company',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
store.dispatch = jest.fn(store.dispatch);
|
||||
|
||||
const progressiveProfilingPage = await getProgressiveProfilingPage();
|
||||
progressiveProfilingPage.find('select#gender').simulate('change', { target: { value: 'm', name: 'gender' } });
|
||||
progressiveProfilingPage.find('input#company').simulate('change', { target: { value: 'edx', name: 'company' } });
|
||||
|
||||
Reference in New Issue
Block a user