refactor: move fields specific code to their wrappers

This commit is contained in:
Syed Sajjad Hussain Shah
2023-08-24 17:19:42 +05:00
parent aeec576d8c
commit e57994bd85
3 changed files with 91 additions and 79 deletions

View File

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

View File

@@ -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,

View File

@@ -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,