refactor: move fields specific code to their wrappers
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { FIELDS } from './data/constants';
|
||||
import { COUNTRY_CODE_KEY, COUNTRY_DISPLAY_KEY, FIELDS } from './data/constants';
|
||||
import { validateCountryField } from './data/utils';
|
||||
import messages from './messages';
|
||||
import { HonorCode, TermsOfService } from './registrationFields';
|
||||
@@ -36,6 +37,7 @@ const ConfigurableRegistrationForm = (props) => {
|
||||
setFormFields,
|
||||
registrationEmbedded,
|
||||
} = props;
|
||||
const backendCountryCode = useSelector(state => state.register.backendCountryCode);
|
||||
|
||||
let showTermsOfServiceAndHonorCode = false;
|
||||
let showCountryField = false;
|
||||
@@ -54,6 +56,29 @@ const ConfigurableRegistrationForm = (props) => {
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (backendCountryCode && backendCountryCode !== formFields?.country?.countryCode) {
|
||||
let countryCode = '';
|
||||
let countryDisplayValue = '';
|
||||
|
||||
const selectedCountry = countryList.find(
|
||||
(country) => (country[COUNTRY_CODE_KEY].toLowerCase() === backendCountryCode.toLowerCase()),
|
||||
);
|
||||
if (selectedCountry) {
|
||||
countryCode = selectedCountry[COUNTRY_CODE_KEY];
|
||||
countryDisplayValue = selectedCountry[COUNTRY_DISPLAY_KEY];
|
||||
}
|
||||
setFormFields(prevState => (
|
||||
{
|
||||
...prevState,
|
||||
country: {
|
||||
countryCode, displayValue: countryDisplayValue,
|
||||
},
|
||||
}
|
||||
));
|
||||
}
|
||||
}, [backendCountryCode, countryList]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
const handleOnChange = (event, countryValue = null) => {
|
||||
const { name } = event.target;
|
||||
let value;
|
||||
@@ -218,7 +243,7 @@ ConfigurableRegistrationForm.propTypes = {
|
||||
marketingEmailsOptIn: PropTypes.bool,
|
||||
}).isRequired,
|
||||
setFieldErrors: PropTypes.func.isRequired,
|
||||
setFocusedField: PropTypes.func.isRequired,
|
||||
setFocusedField: PropTypes.func,
|
||||
setFormFields: PropTypes.func.isRequired,
|
||||
registrationEmbedded: PropTypes.bool,
|
||||
};
|
||||
@@ -226,6 +251,7 @@ ConfigurableRegistrationForm.propTypes = {
|
||||
ConfigurableRegistrationForm.defaultProps = {
|
||||
fieldDescriptions: {},
|
||||
registrationEmbedded: false,
|
||||
setFocusedField: () => {},
|
||||
};
|
||||
|
||||
export default ConfigurableRegistrationForm;
|
||||
|
||||
@@ -23,8 +23,6 @@ import {
|
||||
setUserPipelineDataLoaded,
|
||||
} from './data/actions';
|
||||
import {
|
||||
COUNTRY_CODE_KEY,
|
||||
COUNTRY_DISPLAY_KEY,
|
||||
FIELDS,
|
||||
FORM_SUBMISSION_ERROR,
|
||||
TPA_AUTHENTICATION_FAILURE,
|
||||
@@ -55,7 +53,6 @@ import {
|
||||
const RegistrationPage = (props) => {
|
||||
const {
|
||||
backedUpFormData,
|
||||
backendCountryCode,
|
||||
backendValidations,
|
||||
fieldDescriptions,
|
||||
handleInstitutionLogin,
|
||||
@@ -68,7 +65,6 @@ const RegistrationPage = (props) => {
|
||||
submitState,
|
||||
thirdPartyAuthApiStatus,
|
||||
thirdPartyAuthContext,
|
||||
usernameSuggestions,
|
||||
validationApiRateLimited,
|
||||
// Actions
|
||||
backupFormState,
|
||||
@@ -185,39 +181,6 @@ const RegistrationPage = (props) => {
|
||||
}
|
||||
}, [registrationErrorCode]);
|
||||
|
||||
useEffect(() => {
|
||||
if (backendCountryCode && backendCountryCode !== configurableFormFields?.country?.countryCode) {
|
||||
let countryCode = '';
|
||||
let countryDisplayValue = '';
|
||||
|
||||
const selectedCountry = countryList.find(
|
||||
(country) => (country[COUNTRY_CODE_KEY].toLowerCase() === backendCountryCode.toLowerCase()),
|
||||
);
|
||||
if (selectedCountry) {
|
||||
countryCode = selectedCountry[COUNTRY_CODE_KEY];
|
||||
countryDisplayValue = selectedCountry[COUNTRY_DISPLAY_KEY];
|
||||
}
|
||||
setConfigurableFormFields(prevState => (
|
||||
{
|
||||
...prevState,
|
||||
country: {
|
||||
countryCode, displayValue: countryDisplayValue,
|
||||
},
|
||||
}
|
||||
));
|
||||
}
|
||||
}, [backendCountryCode, countryList]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
/**
|
||||
* We need to remove the placeholder from the field, adding a space will do that.
|
||||
* This is needed because we are placing the username suggestions on top of the field.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (usernameSuggestions.length && !formFields.username) {
|
||||
setFormFields(prevState => ({ ...prevState, username: ' ' }));
|
||||
}
|
||||
}, [usernameSuggestions, formFields]);
|
||||
|
||||
useEffect(() => {
|
||||
if (registrationResult.success) {
|
||||
// Optimizely registration conversion event
|
||||
@@ -393,23 +356,14 @@ const RegistrationPage = (props) => {
|
||||
|
||||
const handleEmailSuggestionClosed = () => setEmailSuggestion({ suggestion: '', type: '' });
|
||||
|
||||
const handleUsernameSuggestionClosed = () => props.resetUsernameSuggestions();
|
||||
|
||||
const handleOnChange = (event) => {
|
||||
console.log('test handleOnChange', event.target);
|
||||
const { name } = event.target;
|
||||
let value = event.target.type === 'checkbox' ? event.target.checked : event.target.value;
|
||||
const value = event.target.type === 'checkbox' ? event.target.checked : event.target.value;
|
||||
if (registrationError[name]) {
|
||||
clearBackendError(name);
|
||||
setErrors(prevErrors => ({ ...prevErrors, [name]: '' }));
|
||||
}
|
||||
if (name === 'username') {
|
||||
if (value.length > 30) {
|
||||
return;
|
||||
}
|
||||
if (value.startsWith(' ')) {
|
||||
value = value.trim();
|
||||
}
|
||||
}
|
||||
|
||||
setFormFields(prevState => ({ ...prevState, [name]: value }));
|
||||
};
|
||||
@@ -430,22 +384,12 @@ const RegistrationPage = (props) => {
|
||||
};
|
||||
|
||||
const handleOnFocus = (event) => {
|
||||
const { name, value } = event.target;
|
||||
const { name } = event.target;
|
||||
setErrors(prevErrors => ({ ...prevErrors, [name]: '' }));
|
||||
clearBackendError(name);
|
||||
// Since we are removing the form errors from the focused field, we will
|
||||
// need to rerun the validation for focused field on form submission.
|
||||
setFocusedField(name);
|
||||
|
||||
if (name === 'username') {
|
||||
props.resetUsernameSuggestions();
|
||||
// If we added a space character to username field to display the suggestion
|
||||
// remove it before user enters the input. This is to ensure user doesn't
|
||||
// have a space prefixed to the username.
|
||||
if (value === ' ') {
|
||||
setFormFields(prevState => ({ ...prevState, [name]: '' }));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const registerUser = () => {
|
||||
@@ -579,8 +523,6 @@ const RegistrationPage = (props) => {
|
||||
handleChange={handleOnChange}
|
||||
handleFocus={handleOnFocus}
|
||||
handleSuggestionClick={handleSuggestionClick}
|
||||
handleUsernameSuggestionClose={handleUsernameSuggestionClosed}
|
||||
usernameSuggestions={usernameSuggestions}
|
||||
errorMessage={errors.username}
|
||||
helpText={[formatMessage(messages['help.text.username.1']), formatMessage(messages['help.text.username.2'])]}
|
||||
floatingLabel={formatMessage(messages['registration.username.label'])}
|
||||
@@ -679,7 +621,6 @@ RegistrationPage.propTypes = {
|
||||
errors: PropTypes.shape({}),
|
||||
emailSuggestion: PropTypes.shape({}),
|
||||
}),
|
||||
backendCountryCode: PropTypes.string,
|
||||
backendValidations: PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
email: PropTypes.string,
|
||||
@@ -719,7 +660,6 @@ RegistrationPage.propTypes = {
|
||||
PropTypes.shape({}),
|
||||
),
|
||||
}),
|
||||
usernameSuggestions: PropTypes.arrayOf(PropTypes.string),
|
||||
userPipelineDataLoaded: PropTypes.bool,
|
||||
validationApiRateLimited: PropTypes.bool,
|
||||
// Actions
|
||||
@@ -728,7 +668,6 @@ RegistrationPage.propTypes = {
|
||||
getRegistrationDataFromBackend: PropTypes.func.isRequired,
|
||||
handleInstitutionLogin: PropTypes.func,
|
||||
registerNewUser: PropTypes.func.isRequired,
|
||||
resetUsernameSuggestions: PropTypes.func.isRequired,
|
||||
setUserPipelineDetailsLoaded: PropTypes.func.isRequired,
|
||||
validateFromBackend: PropTypes.func.isRequired,
|
||||
};
|
||||
@@ -748,7 +687,6 @@ RegistrationPage.defaultProps = {
|
||||
suggestion: '', type: '',
|
||||
},
|
||||
},
|
||||
backendCountryCode: '',
|
||||
backendValidations: null,
|
||||
fieldDescriptions: {},
|
||||
handleInstitutionLogin: null,
|
||||
@@ -770,7 +708,6 @@ RegistrationPage.defaultProps = {
|
||||
providers: [],
|
||||
secondaryProviders: [],
|
||||
},
|
||||
usernameSuggestions: [],
|
||||
userPipelineDataLoaded: false,
|
||||
validationApiRateLimited: false,
|
||||
};
|
||||
@@ -781,7 +718,6 @@ export default connect(
|
||||
backupFormState: backupRegistrationFormBegin,
|
||||
clearBackendError: clearRegistertionBackendError,
|
||||
getRegistrationDataFromBackend: getThirdPartyAuthContext,
|
||||
resetUsernameSuggestions: clearUsernameSuggestions,
|
||||
validateFromBackend: fetchRealtimeValidations,
|
||||
registerNewUser,
|
||||
setUserPipelineDetailsLoaded: setUserPipelineDataLoaded,
|
||||
|
||||
@@ -1,21 +1,72 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { Button, Icon, IconButton } from '@edx/paragon';
|
||||
import { Close } from '@edx/paragon/icons';
|
||||
import PropTypes, { string } from 'prop-types';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { FormGroup } from '../../common-components';
|
||||
import { clearUsernameSuggestions } from '../data/actions';
|
||||
import messages from '../messages';
|
||||
|
||||
const UsernameField = (props) => {
|
||||
const { formatMessage } = useIntl();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const {
|
||||
handleSuggestionClick, handleUsernameSuggestionClose, usernameSuggestions, errorMessage,
|
||||
value,
|
||||
errorMessage,
|
||||
handleChange,
|
||||
handleFocus,
|
||||
} = props;
|
||||
let className = '';
|
||||
let suggestedUsernameDiv = null;
|
||||
let iconButton = null;
|
||||
const usernameSuggestions = useSelector(state => state.register.usernameSuggestions);
|
||||
|
||||
/**
|
||||
* We need to remove the placeholder from the field, adding a space will do that.
|
||||
* This is needed because we are placing the username suggestions on top of the field.
|
||||
*/
|
||||
useEffect(() => {
|
||||
if (usernameSuggestions.length && !value) {
|
||||
handleChange({ target: { name: 'username', value: ' ' } });
|
||||
}
|
||||
}, [handleChange, usernameSuggestions, value]);
|
||||
|
||||
const handleOnChange = (event) => {
|
||||
let username = event.target.value;
|
||||
if (username.length > 30) {
|
||||
return;
|
||||
}
|
||||
if (event.target.value.startsWith(' ')) {
|
||||
username = username.trim();
|
||||
}
|
||||
handleChange({ target: { name: 'username', value: username } });
|
||||
};
|
||||
|
||||
const handleOnFocus = (event) => {
|
||||
const username = event.target.value;
|
||||
dispatch(clearUsernameSuggestions());
|
||||
// If we added a space character to username field to display the suggestion
|
||||
// remove it before user enters the input. This is to ensure user doesn't
|
||||
// have a space prefixed to the username.
|
||||
if (username === ' ') {
|
||||
handleChange({ target: { name: 'username', value: '' } });
|
||||
}
|
||||
handleFocus({ target: { name: 'username', value: username } });
|
||||
};
|
||||
|
||||
const handleSuggestionClick = (event, fieldName, suggestion = '') => {
|
||||
event.preventDefault();
|
||||
handleFocus({ target: { name: 'username', value: suggestion } }); // to clear the error if any
|
||||
handleChange({ target: { name: 'username', value: suggestion } }); // to set suggestion as value
|
||||
dispatch(clearUsernameSuggestions());
|
||||
};
|
||||
|
||||
const handleUsernameSuggestionClose = () => dispatch(clearUsernameSuggestions());
|
||||
|
||||
const suggestedUsernames = () => (
|
||||
<div className={className}>
|
||||
<span className="text-gray username-suggestion--label">{formatMessage(messages['registration.username.suggestion.label'])}</span>
|
||||
@@ -37,11 +88,12 @@ const UsernameField = (props) => {
|
||||
{iconButton}
|
||||
</div>
|
||||
);
|
||||
if (usernameSuggestions.length > 0 && errorMessage && props.value === ' ') {
|
||||
|
||||
if (usernameSuggestions.length > 0 && errorMessage && value === ' ') {
|
||||
className = 'username-suggestions__error';
|
||||
iconButton = <IconButton src={Close} iconAs={Icon} alt="Close" onClick={() => handleUsernameSuggestionClose()} variant="black" size="sm" className="username-suggestions__close__button" />;
|
||||
suggestedUsernameDiv = suggestedUsernames();
|
||||
} else if (usernameSuggestions.length > 0 && props.value === ' ') {
|
||||
} else if (usernameSuggestions.length > 0 && value === ' ') {
|
||||
className = 'username-suggestions d-flex align-items-center';
|
||||
iconButton = <IconButton src={Close} iconAs={Icon} alt="Close" onClick={() => handleUsernameSuggestionClose()} variant="black" size="sm" className="username-suggestions__close__button" />;
|
||||
suggestedUsernameDiv = suggestedUsernames();
|
||||
@@ -49,22 +101,20 @@ const UsernameField = (props) => {
|
||||
suggestedUsernameDiv = suggestedUsernames();
|
||||
}
|
||||
return (
|
||||
<FormGroup {...props}>
|
||||
<FormGroup {...props} handleChange={handleOnChange} handleFocus={handleOnFocus}>
|
||||
{suggestedUsernameDiv}
|
||||
</FormGroup>
|
||||
);
|
||||
};
|
||||
|
||||
UsernameField.defaultProps = {
|
||||
usernameSuggestions: [],
|
||||
errorMessage: '',
|
||||
autoComplete: null,
|
||||
};
|
||||
|
||||
UsernameField.propTypes = {
|
||||
usernameSuggestions: PropTypes.arrayOf(string),
|
||||
handleSuggestionClick: PropTypes.func.isRequired,
|
||||
handleUsernameSuggestionClose: PropTypes.func.isRequired,
|
||||
handleChange: PropTypes.func.isRequired,
|
||||
handleFocus: PropTypes.func.isRequired,
|
||||
errorMessage: PropTypes.string,
|
||||
name: PropTypes.string.isRequired,
|
||||
value: PropTypes.string.isRequired,
|
||||
|
||||
Reference in New Issue
Block a user