This reverts commit 3c89afea4a.
This commit is contained in:
committed by
GitHub
parent
3c89afea4a
commit
f0105f0094
@@ -54,13 +54,13 @@ const ConfigurableRegistrationForm = (props) => {
|
||||
});
|
||||
|
||||
const handleOnChange = (event, countryValue = null) => {
|
||||
const { name } = event.target;
|
||||
const { name, type } = event.target;
|
||||
let value;
|
||||
if (countryValue) {
|
||||
value = { ...countryValue };
|
||||
} else {
|
||||
value = event.target.type === 'checkbox' ? event.target.checked : event.target.value;
|
||||
if (event.target.type === 'checkbox') {
|
||||
if (type === 'checkbox') {
|
||||
setFieldErrors(prevErrors => ({ ...prevErrors, [name]: '' }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,3 +173,6 @@ export const DEFAULT_TOP_LEVEL_DOMAINS = [
|
||||
|
||||
export const COUNTRY_CODE_KEY = 'code';
|
||||
export const COUNTRY_DISPLAY_KEY = 'name';
|
||||
|
||||
export const EXPAND_MORE_ICON = 'expand-more';
|
||||
export const EXPAND_LESS_ICON = 'expand-less';
|
||||
|
||||
@@ -93,10 +93,14 @@ export function validateCountryField(value, countryList, errorMessage) {
|
||||
|
||||
if (value) {
|
||||
const normalizedValue = value.toLowerCase();
|
||||
// Handling a case here where user enters a valid country code that needs to be
|
||||
// evaluated and set its value as a valid value.
|
||||
const selectedCountry = countryList.find(
|
||||
(country) => (
|
||||
// When translations are applied, extra space added in country value, so we should trim that.
|
||||
// When translations apply extra space added in country value so we should
|
||||
// trim that.
|
||||
country[COUNTRY_DISPLAY_KEY].toLowerCase().trim() === normalizedValue
|
||||
|| country[COUNTRY_CODE_KEY].toLowerCase().trim() === normalizedValue
|
||||
),
|
||||
);
|
||||
if (selectedCountry) {
|
||||
|
||||
@@ -1,62 +1,154 @@
|
||||
import React from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { FormAutosuggest, FormAutosuggestOption, FormControlFeedback } from '@edx/paragon';
|
||||
import { Icon, IconButton } from '@edx/paragon';
|
||||
import { ExpandLess, ExpandMore } from '@edx/paragon/icons';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { COUNTRY_CODE_KEY, COUNTRY_DISPLAY_KEY } from '../data/constants';
|
||||
import { FormGroup } from '../../common-components';
|
||||
import {
|
||||
COUNTRY_CODE_KEY, COUNTRY_DISPLAY_KEY, EXPAND_LESS_ICON, EXPAND_MORE_ICON,
|
||||
} from '../data/constants';
|
||||
import messages from '../messages';
|
||||
|
||||
const CountryField = (props) => {
|
||||
const { countryList, selectedCountry } = props;
|
||||
const { formatMessage } = useIntl();
|
||||
|
||||
const handleSelected = (value) => {
|
||||
if (props.onBlurHandler) { props.onBlurHandler({ target: { name: 'country', value } }); }
|
||||
const dropdownRef = useRef(null);
|
||||
const { formatMessage } = useIntl();
|
||||
const [errorMessage, setErrorMessage] = useState(props.errorMessage);
|
||||
const [dropDownItems, setDropDownItems] = useState([]);
|
||||
const [displayValue, setDisplayValue] = useState('');
|
||||
const [trailingIcon, setTrailingIcon] = useState(EXPAND_MORE_ICON);
|
||||
|
||||
const onBlurHandler = (event, itemClicked = false, countryName = '') => {
|
||||
const { name } = event.target;
|
||||
const relatedName = event.relatedTarget ? event.relatedTarget.name : '';
|
||||
// For a better user experience, do not validate when focus out from 'country' field
|
||||
// and focus on 'countryItem' or 'countryExpand' button.
|
||||
if ((relatedName === 'countryItem' || relatedName === 'countryExpand') && name === 'country') {
|
||||
return;
|
||||
}
|
||||
const countryValue = itemClicked ? countryName : displayValue;
|
||||
if (props.onBlurHandler) {
|
||||
props.onBlurHandler({ target: { name: 'country', value: countryValue } });
|
||||
}
|
||||
setTrailingIcon(EXPAND_MORE_ICON);
|
||||
setDropDownItems([]);
|
||||
};
|
||||
|
||||
const getDropdownItems = (countryToFind = null) => {
|
||||
let updatedCountryList = countryList;
|
||||
if (countryToFind) {
|
||||
updatedCountryList = countryList.filter(
|
||||
(option) => (option.name.toLowerCase().includes(countryToFind.toLowerCase())),
|
||||
);
|
||||
}
|
||||
|
||||
return updatedCountryList.map((country) => {
|
||||
const countryName = country[COUNTRY_DISPLAY_KEY];
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
name="countryItem"
|
||||
className="dropdown-item data-hj-suppress"
|
||||
value={countryName}
|
||||
key={country[COUNTRY_CODE_KEY]}
|
||||
onClick={(event) => onBlurHandler(event, true, countryName)}
|
||||
/* This event will prevent the blur event to be fired,
|
||||
as blur event is having higher priority than click event and restricts the click event.
|
||||
*/
|
||||
onMouseDown={(event) => event.preventDefault()}
|
||||
>
|
||||
{countryName.length > 30 ? countryName.substring(0, 30).concat('...') : countryName}
|
||||
</button>
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
const onFocusHandler = (event) => {
|
||||
const { name, value } = event.target;
|
||||
setDropDownItems(getDropdownItems(name === 'country' ? value : displayValue));
|
||||
setTrailingIcon(EXPAND_LESS_ICON);
|
||||
setErrorMessage('');
|
||||
if (props.onFocusHandler) { props.onFocusHandler(event); }
|
||||
};
|
||||
|
||||
const onChangeHandler = (value) => {
|
||||
if (props.onChangeHandler) {
|
||||
props.onChangeHandler({ target: { name: 'country' } }, { countryCode: '', displayValue: value });
|
||||
const onChangeHandler = (event) => {
|
||||
const filteredItems = getDropdownItems(event.target.value);
|
||||
setDropDownItems(filteredItems);
|
||||
setDisplayValue(event.target.value);
|
||||
if (props.onChangeHandler) { props.onChangeHandler(event, { countryCode: '', displayValue: event.target.value }); }
|
||||
};
|
||||
|
||||
const handleOnClickOutside = () => {
|
||||
setTrailingIcon(EXPAND_MORE_ICON);
|
||||
setDropDownItems([]);
|
||||
};
|
||||
|
||||
const handleTrailingIconClick = () => {
|
||||
if (trailingIcon === EXPAND_MORE_ICON) {
|
||||
setDropDownItems(getDropdownItems());
|
||||
setTrailingIcon(EXPAND_LESS_ICON);
|
||||
} else {
|
||||
setDropDownItems([]);
|
||||
setTrailingIcon(EXPAND_MORE_ICON);
|
||||
}
|
||||
};
|
||||
|
||||
const getCountryList = () => countryList.map((country) => (
|
||||
<FormAutosuggestOption key={country[COUNTRY_CODE_KEY]}>
|
||||
{country[COUNTRY_DISPLAY_KEY]}
|
||||
</FormAutosuggestOption>
|
||||
));
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event) => {
|
||||
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
||||
handleOnClickOutside();
|
||||
}
|
||||
};
|
||||
document.addEventListener('click', handleClickOutside, true);
|
||||
return () => {
|
||||
document.removeEventListener('click', handleClickOutside, true);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (selectedCountry.displayValue) {
|
||||
setDisplayValue(selectedCountry.displayValue);
|
||||
}
|
||||
}, [selectedCountry]);
|
||||
|
||||
useEffect(() => {
|
||||
setErrorMessage(props.errorMessage);
|
||||
}, [props.errorMessage]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormAutosuggest
|
||||
floatingLabel={formatMessage(messages['registration.country.label'])}
|
||||
aria-label="form autosuggest"
|
||||
<div ref={dropdownRef} className="mb-4">
|
||||
<FormGroup
|
||||
as="input"
|
||||
name="country"
|
||||
value={selectedCountry.displayValue || ''}
|
||||
onSelected={(value) => handleSelected(value)}
|
||||
onFocus={(e) => onFocusHandler(e)}
|
||||
onBlur={(e) => handleSelected(e.target.value)}
|
||||
onChange={(value) => onChangeHandler(value)}
|
||||
>
|
||||
{getCountryList()}
|
||||
</FormAutosuggest>
|
||||
{props.errorMessage !== '' && (
|
||||
<FormControlFeedback
|
||||
key="error"
|
||||
className="form-text-size"
|
||||
hasIcon={false}
|
||||
feedback-for="country"
|
||||
type="invalid"
|
||||
>
|
||||
{props.errorMessage}
|
||||
</FormControlFeedback>
|
||||
)}
|
||||
</>
|
||||
autoComplete="chrome-off"
|
||||
className="mb-0"
|
||||
floatingLabel={formatMessage(messages['registration.country.label'])}
|
||||
trailingElement={(
|
||||
<IconButton
|
||||
name="countryExpand"
|
||||
size="sm"
|
||||
variant="secondary"
|
||||
alt="expand-dropdown"
|
||||
iconAs={Icon}
|
||||
src={trailingIcon === EXPAND_MORE_ICON ? ExpandMore : ExpandLess}
|
||||
onBlur={() => {}}
|
||||
onClick={handleTrailingIconClick}
|
||||
onFocus={() => {}}
|
||||
/>
|
||||
)}
|
||||
value={displayValue}
|
||||
errorMessage={errorMessage}
|
||||
handleChange={onChangeHandler}
|
||||
handleBlur={onBlurHandler}
|
||||
handleFocus={onFocusHandler}
|
||||
/>
|
||||
<div className="dropdown-container">
|
||||
{ dropDownItems?.length > 0 ? dropDownItems : null }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -121,8 +121,8 @@ describe('RegistrationPage', () => {
|
||||
registrationPage.find('input#username').simulate('change', { target: { value: payload.username, name: 'username' } });
|
||||
registrationPage.find('input#email').simulate('change', { target: { value: payload.email, name: 'email' } });
|
||||
|
||||
registrationPage.find('input[name="country"]').simulate('change', { target: { value: payload.country, name: 'country' } });
|
||||
registrationPage.find('input[name="country"]').simulate('blur', { target: { value: payload.country, name: 'country' } });
|
||||
registrationPage.find('input#country').simulate('change', { target: { value: payload.country, name: 'country' } });
|
||||
registrationPage.find('input#country').simulate('blur', { target: { value: payload.country, name: 'country' } });
|
||||
|
||||
if (!isThirdPartyAuth) {
|
||||
registrationPage.find('input#password').simulate('change', { target: { value: payload.password, name: 'password' } });
|
||||
@@ -307,7 +307,7 @@ describe('RegistrationPage', () => {
|
||||
registrationPage.find('input#password').simulate('blur', { target: { value: '', name: 'password' } });
|
||||
expect(registrationPage.find('div[feedback-for="password"]').text()).toContain(emptyFieldValidation.password);
|
||||
|
||||
registrationPage.find('input[name="country"]').simulate('blur', { target: { value: '', name: 'country' } });
|
||||
registrationPage.find('input#country').simulate('blur', { target: { value: '', name: 'country' } });
|
||||
expect(registrationPage.find('div[feedback-for="country"]').text()).toEqual(emptyFieldValidation.country);
|
||||
});
|
||||
|
||||
@@ -332,7 +332,7 @@ describe('RegistrationPage', () => {
|
||||
|
||||
it('should run validations for focused field on form submission', () => {
|
||||
const registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||
registrationPage.find('input[name="country"]').simulate('focus');
|
||||
registrationPage.find('input#country').simulate('focus');
|
||||
registrationPage.find('button.btn-brand').simulate('click');
|
||||
|
||||
expect(registrationPage.find('div[feedback-for="country"]').text()).toEqual(emptyFieldValidation.country);
|
||||
@@ -443,7 +443,7 @@ describe('RegistrationPage', () => {
|
||||
expect(registrationPage.find('div[feedback-for="password"]').exists()).toBeFalsy();
|
||||
|
||||
expect(registrationPage.find('div[feedback-for="country"]').text()).toEqual(emptyFieldValidation.country);
|
||||
registrationPage.find('input[name="country"]').simulate('focus');
|
||||
registrationPage.find('input#country').simulate('focus');
|
||||
expect(registrationPage.find('div[feedback-for="country"]').exists()).toBeFalsy();
|
||||
});
|
||||
|
||||
@@ -986,11 +986,9 @@ describe('RegistrationPage', () => {
|
||||
backendCountryCode: 'PK',
|
||||
},
|
||||
});
|
||||
const expectedPropValue = { countryCode: 'PK', displayValue: 'Pakistan' };
|
||||
|
||||
const registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />)).find('RegistrationPage');
|
||||
expect(registrationPage.find('CountryField').prop('selectedCountry')).toEqual(expectedPropValue);
|
||||
expect(registrationPage.find('input[name="country"]').exists()).toBeTruthy();
|
||||
const registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||
expect(registrationPage.find('input#country').props().value).toEqual('Pakistan');
|
||||
});
|
||||
|
||||
it('should display error message based on the error code returned by API', () => {
|
||||
@@ -1043,7 +1041,7 @@ describe('RegistrationPage', () => {
|
||||
getLocale.mockImplementation(() => ('ar-ae'));
|
||||
|
||||
const registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||
registrationPage.find('input[name="country"]').simulate('click');
|
||||
registrationPage.find('input#country').simulate('focus');
|
||||
registrationPage.find('button.dropdown-item').at(0).simulate('click', { target: { value: 'أفغانستان ', name: 'countryItem' } });
|
||||
expect(registrationPage.find('div[feedback-for="country"]').exists()).toBeFalsy();
|
||||
});
|
||||
@@ -1066,6 +1064,18 @@ describe('RegistrationPage', () => {
|
||||
registrationPage.find('input#email').simulate('change', { target: { value: 'a@gmail.com', name: 'email' } });
|
||||
expect(registrationPage.find('div[feedback-for="email"]').exists()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should set country in component state when form is translated using browser translations', () => {
|
||||
getLocale.mockImplementation(() => ('en-us'));
|
||||
|
||||
store.dispatch = jest.fn(store.dispatch);
|
||||
|
||||
const registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||
registrationPage.find('input#country').simulate('focus');
|
||||
registrationPage.find('button.dropdown-item').at(0).simulate('click', { target: { value: undefined, name: undefined, parentElement: { parentElement: { value: 'Afghanistan' } } } });
|
||||
expect(registrationPage.find('input#country').props().value).toEqual('Afghanistan');
|
||||
expect(registrationPage.find('div[feedback-for="country"]').exists()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test Configurable Fields', () => {
|
||||
@@ -1094,7 +1104,6 @@ describe('RegistrationPage', () => {
|
||||
});
|
||||
|
||||
it('should submit form with fields returned by backend in payload', () => {
|
||||
getLocale.mockImplementation(() => ('en-us'));
|
||||
jest.spyOn(global.Date, 'now').mockImplementation(() => 0);
|
||||
store = mockStore({
|
||||
...initialState,
|
||||
@@ -1195,6 +1204,16 @@ describe('RegistrationPage', () => {
|
||||
expect(registrationPage.find('#profession-error').last().text()).toEqual(professionError);
|
||||
});
|
||||
|
||||
it('should not remove errors from form fields when country is selected by clicking on expand button', () => {
|
||||
const registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||
registrationPage.find('button.btn-brand').simulate('click');
|
||||
expect(registrationPage.find('div[feedback-for="name"]').exists()).toBeTruthy();
|
||||
|
||||
registrationPage.find('button[name="countryExpand"]').simulate('click');
|
||||
registrationPage.find('button.dropdown-item').at(0).simulate('click', { target: { value: 'Pakistan', name: 'countryItem' } });
|
||||
expect(registrationPage.find('div[feedback-for="name"]').exists()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should check TOS and honor code fields if they exist when auto submitting register form', () => {
|
||||
getLocale.mockImplementation(() => ('en-us'));
|
||||
store = mockStore({
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
.register-stateful-button-width {
|
||||
min-width: 14.4rem;
|
||||
}
|
||||
|
||||
.pgn__form-autosuggest__wrapper > .pgn__form-group {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user