feat: remove optional fields from register form (#528)
* feat: remove optional fields from register form This is part of the larger ticket where we will move optional fields to progressive profiling page. VAN-837
This commit is contained in:
1
.env
1
.env
@@ -16,7 +16,6 @@ SITE_NAME=null
|
||||
USER_INFO_COOKIE_NAME=null
|
||||
AUTHN_MINIMAL_HEADER=true
|
||||
LOGIN_ISSUE_SUPPORT_LINK=''
|
||||
REGISTRATION_OPTIONAL_FIELDS=''
|
||||
USER_SURVEY_COOKIE_NAME=null
|
||||
COOKIE_DOMAIN=null
|
||||
WELCOME_PAGE_SUPPORT_LINK=null
|
||||
|
||||
@@ -23,7 +23,6 @@ AUTHN_MINIMAL_HEADER=true
|
||||
LOGIN_ISSUE_SUPPORT_LINK='/login-issue-support-url'
|
||||
TOS_AND_HONOR_CODE='http://localhost:18000/honor'
|
||||
PRIVACY_POLICY='http://localhost:18000/privacy'
|
||||
REGISTRATION_OPTIONAL_FIELDS=''
|
||||
USER_SURVEY_COOKIE_NAME='openedx-user-survey-type'
|
||||
COOKIE_DOMAIN='localhost'
|
||||
WELCOME_PAGE_SUPPORT_LINK='http://localhost:1999/welcome'
|
||||
|
||||
@@ -31,7 +31,6 @@ initialize({
|
||||
PASSWORD_RESET_SUPPORT_LINK: process.env.PASSWORD_RESET_SUPPORT_LINK || null,
|
||||
TOS_AND_HONOR_CODE: process.env.TOS_AND_HONOR_CODE || null,
|
||||
PRIVACY_POLICY: process.env.PRIVACY_POLICY || null,
|
||||
REGISTRATION_OPTIONAL_FIELDS: process.env.REGISTRATION_OPTIONAL_FIELDS || '',
|
||||
USER_SURVEY_COOKIE_NAME: process.env.USER_SURVEY_COOKIE_NAME || null,
|
||||
COOKIE_DOMAIN: process.env.COOKIE_DOMAIN,
|
||||
WELCOME_PAGE_SUPPORT_LINK: process.env.WELCOME_PAGE_SUPPORT_LINK || null,
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { Form, Icon } from '@edx/paragon';
|
||||
import { ExpandMore } from '@edx/paragon/icons';
|
||||
|
||||
import { EDUCATION_LEVELS, GENDER_OPTIONS, YEAR_OF_BIRTH_OPTIONS } from './data/constants';
|
||||
import messages from './messages';
|
||||
|
||||
const OptionalFields = (props) => {
|
||||
const {
|
||||
intl, optionalFields, onChangeHandler, values,
|
||||
} = props;
|
||||
|
||||
if (getConfig().ENABLE_COPPA_COMPLIANCE && EDUCATION_LEVELS) {
|
||||
const index = EDUCATION_LEVELS.indexOf('el');
|
||||
EDUCATION_LEVELS.splice(index, 1);
|
||||
}
|
||||
const getOptions = () => ({
|
||||
|
||||
yearOfBirthOptions: YEAR_OF_BIRTH_OPTIONS.map(({ value, label }) => (
|
||||
<option className="data-hj-suppress" key={value} value={value}>{label}</option>
|
||||
)),
|
||||
educationLevelOptions: EDUCATION_LEVELS.map(key => (
|
||||
<option className="data-hj-suppress" key={key} value={key}>
|
||||
{intl.formatMessage(messages[`registration.field.education.levels.${key || 'label'}`])}
|
||||
</option>
|
||||
)),
|
||||
genderOptions: GENDER_OPTIONS.map(key => (
|
||||
<option className="data-hj-suppress" key={key} value={key}>
|
||||
{intl.formatMessage(messages[`registration.field.gender.options.${key || 'label'}`])}
|
||||
</option>
|
||||
)),
|
||||
});
|
||||
|
||||
return (
|
||||
<div className="mt-3">
|
||||
{optionalFields.includes('gender') && (
|
||||
<Form.Group controlId="gender">
|
||||
<Form.Control
|
||||
as="select"
|
||||
name="gender"
|
||||
value={values.gender}
|
||||
onChange={(e) => onChangeHandler('gender', e.target.value)}
|
||||
trailingElement={<Icon src={ExpandMore} />}
|
||||
floatingLabel={intl.formatMessage(messages['registration.field.gender.options.label'])}
|
||||
>
|
||||
{getOptions().genderOptions}
|
||||
</Form.Control>
|
||||
</Form.Group>
|
||||
)}
|
||||
{optionalFields.includes('yearOfBirth') && (
|
||||
<Form.Group controlId="yearOfBirth">
|
||||
<Form.Control
|
||||
as="select"
|
||||
name="yearOfBirth"
|
||||
value={values.yearOfBirth}
|
||||
onChange={(e) => onChangeHandler('yearOfBirth', e.target.value)}
|
||||
trailingElement={<Icon src={ExpandMore} />}
|
||||
floatingLabel={intl.formatMessage(messages['registration.year.of.birth.label'])}
|
||||
>
|
||||
<option value="">{intl.formatMessage(messages['registration.year.of.birth.label'])}</option>
|
||||
{getOptions().yearOfBirthOptions}
|
||||
</Form.Control>
|
||||
</Form.Group>
|
||||
)}
|
||||
{optionalFields.includes('levelOfEducation') && (
|
||||
<Form.Group controlId="levelOfEducation">
|
||||
<Form.Control
|
||||
as="select"
|
||||
name="levelOfEducation"
|
||||
value={values.levelOfEducation}
|
||||
onChange={(e) => onChangeHandler('levelOfEducation', e.target.value)}
|
||||
trailingElement={<Icon src={ExpandMore} />}
|
||||
floatingLabel={intl.formatMessage(messages['registration.field.education.levels.label'])}
|
||||
>
|
||||
{getOptions().educationLevelOptions}
|
||||
</Form.Control>
|
||||
</Form.Group>
|
||||
)}
|
||||
{optionalFields.includes('goals') && (
|
||||
<Form.Group controlId="goals">
|
||||
<Form.Control
|
||||
as="textarea"
|
||||
name="goals"
|
||||
value={values.goals}
|
||||
onChange={(e) => onChangeHandler('goals', e.target.value)}
|
||||
floatingLabel={intl.formatMessage(messages['registration.goals.label'])}
|
||||
/>
|
||||
</Form.Group>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
OptionalFields.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
optionalFields: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
onChangeHandler: PropTypes.func.isRequired,
|
||||
values: PropTypes.shape({
|
||||
gender: PropTypes.string,
|
||||
goals: PropTypes.string,
|
||||
levelOfEducation: PropTypes.string,
|
||||
yearOfBirth: PropTypes.string,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(OptionalFields);
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
import snakeCase from 'lodash.snakecase';
|
||||
import { connect } from 'react-redux';
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
import { Helmet } from 'react-helmet';
|
||||
@@ -26,7 +25,6 @@ import {
|
||||
registrationErrorSelector, registrationRequestSelector, validationsSelector, usernameSuggestionsSelector,
|
||||
} from './data/selectors';
|
||||
import messages from './messages';
|
||||
import OptionalFields from './OptionalFields';
|
||||
import RegistrationFailure from './RegistrationFailure';
|
||||
import UsernameField from './UsernameField';
|
||||
|
||||
@@ -50,7 +48,6 @@ class RegistrationPage extends React.Component {
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
sendPageEvent('login_and_registration', 'register');
|
||||
const optionalFields = getConfig().REGISTRATION_OPTIONAL_FIELDS ? getConfig().REGISTRATION_OPTIONAL_FIELDS.split(',') : [];
|
||||
this.handleOnClose = this.handleOnClose.bind(this);
|
||||
|
||||
this.queryParams = getAllPossibleQueryParam();
|
||||
@@ -73,9 +70,6 @@ class RegistrationPage extends React.Component {
|
||||
emailWarningSuggestion: null,
|
||||
errorCode: null,
|
||||
failureCount: 0,
|
||||
optionalFields,
|
||||
optionalFieldsState: {},
|
||||
showOptionalField: false,
|
||||
startTime: Date.now(),
|
||||
totalRegistrationTime: 0,
|
||||
optimizelyExperimentName: '', // eslint-disable-line react/no-unused-state
|
||||
@@ -185,27 +179,11 @@ class RegistrationPage extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
getOptionalFields() {
|
||||
return (
|
||||
<OptionalFields
|
||||
optionalFields={this.state.optionalFields}
|
||||
values={this.state.optionalFieldsState}
|
||||
onChangeHandler={
|
||||
(fieldName, value) => {
|
||||
this.setState(prevState => ({
|
||||
optionalFieldsState: { ...prevState.optionalFieldsState, [fieldName]: value },
|
||||
}));
|
||||
}
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
const { startTime } = this.state;
|
||||
const totalRegistrationTime = (Date.now() - startTime) / 1000;
|
||||
let payload = {
|
||||
const payload = {
|
||||
name: this.state.name,
|
||||
username: this.state.username,
|
||||
email: this.state.email,
|
||||
@@ -232,15 +210,6 @@ class RegistrationPage extends React.Component {
|
||||
payload.marketing_emails_opt_in = this.state.marketingOptIn;
|
||||
}
|
||||
|
||||
// Since optional fields and query params are not validated we can add it to payload after
|
||||
// required fields have been validated.
|
||||
payload = { ...payload, ...this.queryParams };
|
||||
this.state.optionalFields.forEach((key) => {
|
||||
if (this.state.optionalFieldsState[key]) {
|
||||
payload[snakeCase(key)] = this.state.optionalFieldsState[key];
|
||||
}
|
||||
});
|
||||
|
||||
payload.totalRegistrationTime = totalRegistrationTime;
|
||||
this.setState({
|
||||
totalRegistrationTime,
|
||||
@@ -269,12 +238,7 @@ class RegistrationPage extends React.Component {
|
||||
}
|
||||
|
||||
handleOnChange = (e) => {
|
||||
if (e.target.name === 'optionalFields') {
|
||||
sendTrackEvent('edx.bi.user.register.optional_fields_selected', {});
|
||||
this.setState({
|
||||
showOptionalField: e.target.checked,
|
||||
});
|
||||
} else if (!(e.target.name === 'username' && e.target.value.length > 30)) {
|
||||
if (!(e.target.name === 'username' && e.target.value.length > 30)) {
|
||||
this.setState({
|
||||
[e.target.name]: e.target.value,
|
||||
});
|
||||
@@ -692,20 +656,6 @@ class RegistrationPage extends React.Component {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{getConfig().REGISTRATION_OPTIONAL_FIELDS ? (
|
||||
<Form.Group className="mb-0 mt-2 small">
|
||||
<Form.Check
|
||||
id="optional-field-checkbox"
|
||||
type="checkbox"
|
||||
name="optionalFields"
|
||||
value={this.state.showOptionalField}
|
||||
onClick={this.handleOnChange}
|
||||
onChange={this.handleOnChange}
|
||||
label={intl.formatMessage(messages['support.education.research'])}
|
||||
/>
|
||||
</Form.Group>
|
||||
) : null}
|
||||
{ this.state.showOptionalField ? this.getOptionalFields() : null }
|
||||
<StatefulButton
|
||||
type="submit"
|
||||
variant="brand"
|
||||
|
||||
@@ -34,7 +34,6 @@ const mockStore = configureStore();
|
||||
describe('RegistrationPage', () => {
|
||||
mergeConfig({
|
||||
PRIVACY_POLICY: 'http://privacy-policy.com',
|
||||
REGISTRATION_OPTIONAL_FIELDS: 'gender,goals,levelOfEducation,yearOfBirth',
|
||||
TOS_AND_HONOR_CODE: 'http://tos-and-honot-code.com',
|
||||
USER_SURVEY_COOKIE_NAME: process.env.USER_SURVEY_COOKIE_NAME,
|
||||
REGISTER_CONVERSION_COOKIE_NAME: process.env.REGISTER_CONVERSION_COOKIE_NAME,
|
||||
@@ -774,86 +773,4 @@ describe('RegistrationPage', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('TestOptionalFields', () => {
|
||||
it('should toggle optional fields state', () => {
|
||||
const registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||
|
||||
registrationPage.find('input#optional-field-checkbox').simulate('click', { target: { name: 'optionalFields', checked: true } });
|
||||
expect(registrationPage.find('RegistrationPage').state('showOptionalField')).toEqual(true);
|
||||
|
||||
// it should also works when change is made directly instead of click
|
||||
registrationPage.find('input#optional-field-checkbox').simulate('change', { target: { name: 'optionalFields', checked: false } });
|
||||
expect(registrationPage.find('RegistrationPage').state('showOptionalField')).toEqual(false);
|
||||
});
|
||||
|
||||
it('should show optional fields section on optional check enabled', () => {
|
||||
const registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||
registrationPage.find('input#optional-field-checkbox').simulate('change', { target: { name: 'optionalFields', checked: true } });
|
||||
registrationPage.update();
|
||||
|
||||
expect(registrationPage.find('textarea#goals').length).toEqual(1);
|
||||
expect(registrationPage.find('select#levelOfEducation').length).toEqual(1);
|
||||
expect(registrationPage.find('select#yearOfBirth').length).toEqual(1);
|
||||
expect(registrationPage.find('select#gender').length).toEqual(1);
|
||||
});
|
||||
|
||||
it('should show optional field check based on environment variable', () => {
|
||||
mergeConfig({
|
||||
REGISTRATION_OPTIONAL_FIELDS: '',
|
||||
});
|
||||
let registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||
expect(registrationPage.find('input#optional-field-checkbox').length).toEqual(0);
|
||||
|
||||
mergeConfig({
|
||||
REGISTRATION_OPTIONAL_FIELDS: 'gender,goals,levelOfEducation,yearOfBirth',
|
||||
});
|
||||
|
||||
registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||
expect(registrationPage.find('input#optional-field-checkbox').length).toEqual(1);
|
||||
});
|
||||
|
||||
it('send tracking event on optional checkbox enabled', () => {
|
||||
const registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||
|
||||
registrationPage.find('input#optional-field-checkbox').simulate('change', { target: { name: 'optionalFields', checked: true } });
|
||||
expect(analytics.sendTrackEvent).toHaveBeenCalledWith('edx.bi.user.register.optional_fields_selected', {});
|
||||
});
|
||||
|
||||
it('should submit form with optional fields', () => {
|
||||
jest.spyOn(global.Date, 'now').mockImplementation(() => 0);
|
||||
|
||||
const payload = {
|
||||
name: 'John Doe',
|
||||
username: 'john_doe',
|
||||
email: 'john.doe@example.com',
|
||||
password: 'password1',
|
||||
country: 'Pakistan',
|
||||
gender: 'm',
|
||||
year_of_birth: '1997',
|
||||
level_of_education: 'other',
|
||||
goals: 'edX goals',
|
||||
honor_code: true,
|
||||
totalRegistrationTime: 0,
|
||||
is_authn_mfe: true,
|
||||
};
|
||||
|
||||
store.dispatch = jest.fn(store.dispatch);
|
||||
delete window.location;
|
||||
window.location = { href: getConfig().BASE_URL };
|
||||
|
||||
const registerPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||
populateRequiredFields(registerPage, payload);
|
||||
|
||||
// submit optional fields
|
||||
registerPage.find('input#optional-field-checkbox').simulate('change', { target: { name: 'optionalFields', checked: true } });
|
||||
registerPage.find('select#gender').simulate('change', { target: { value: 'm', name: 'gender' } });
|
||||
registerPage.find('select#yearOfBirth').simulate('change', { target: { value: '1997', name: 'yearOfBirth' } });
|
||||
registerPage.find('select#levelOfEducation').simulate('change', { target: { value: 'other', name: 'levelOfEducation' } });
|
||||
registerPage.find('textarea#goals').simulate('change', { target: { value: 'edX goals', name: 'goals' } });
|
||||
|
||||
registerPage.find('button.btn-brand').simulate('click');
|
||||
expect(store.dispatch).toHaveBeenCalledWith(registerNewUser({ ...payload, country: 'PK' }));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user