diff --git a/src/register/RegistrationFields/CountryField/CountryField.jsx b/src/register/RegistrationFields/CountryField/CountryField.jsx
index a3d01ad5..c1ffc4de 100644
--- a/src/register/RegistrationFields/CountryField/CountryField.jsx
+++ b/src/register/RegistrationFields/CountryField/CountryField.jsx
@@ -3,6 +3,7 @@ import { useDispatch, useSelector } from 'react-redux';
import { useIntl } from '@edx/frontend-platform/i18n';
import { FormAutosuggest, FormAutosuggestOption, FormControlFeedback } from '@edx/paragon';
+import classNames from 'classnames';
import PropTypes from 'prop-types';
import validateCountryField, { COUNTRY_CODE_KEY, COUNTRY_DISPLAY_KEY } from './validator';
@@ -60,7 +61,7 @@ const CountryField = (props) => {
const { value } = event.target;
const { countryCode, displayValue, error } = validateCountryField(
- value.trim(), countryList, formatMessage(messages['empty.country.field.error']),
+ value.trim(), countryList, formatMessage(messages['empty.country.field.error']), formatMessage(messages['invalid.country.field.error']),
);
onChangeHandler({ target: { name: 'country' } }, { countryCode, displayValue });
@@ -94,6 +95,7 @@ const CountryField = (props) => {
aria-label="form autosuggest"
name="country"
value={selectedCountry.displayValue || ''}
+ className={classNames({ 'form-field-error': props.errorMessage })}
onSelected={(value) => handleSelected(value)}
onFocus={(e) => handleOnFocus(e)}
onBlur={(e) => handleOnBlur(e)}
diff --git a/src/register/RegistrationFields/CountryField/CountryField.test.jsx b/src/register/RegistrationFields/CountryField/CountryField.test.jsx
index f442fd38..66a09aba 100644
--- a/src/register/RegistrationFields/CountryField/CountryField.test.jsx
+++ b/src/register/RegistrationFields/CountryField/CountryField.test.jsx
@@ -91,6 +91,16 @@ describe('CountryField', () => {
);
});
+ it('should run country field validation when country name is invalid', () => {
+ const countryField = mount(routerWrapper(reduxWrapper()));
+ countryField.find('input[name="country"]').simulate('blur', { target: { value: 'Pak', name: 'country' } });
+ expect(props.handleErrorChange).toHaveBeenCalledTimes(1);
+ expect(props.handleErrorChange).toHaveBeenCalledWith(
+ 'country',
+ 'Country must match with an option available in the dropdown.',
+ );
+ });
+
it('should not run country field validation when onBlur is fired by drop-down arrow icon click', () => {
const countryField = mount(routerWrapper(reduxWrapper()));
countryField.find('input[name="country"]').simulate('blur', {
diff --git a/src/register/RegistrationFields/CountryField/validator.js b/src/register/RegistrationFields/CountryField/validator.js
index 0dd4c265..cb28bed4 100644
--- a/src/register/RegistrationFields/CountryField/validator.js
+++ b/src/register/RegistrationFields/CountryField/validator.js
@@ -1,10 +1,10 @@
export const COUNTRY_CODE_KEY = 'code';
export const COUNTRY_DISPLAY_KEY = 'name';
-const validateCountryField = (value, countryList, errorMessage) => {
+const validateCountryField = (value, countryList, emptyErrorMessage, invalidCountryErrorMessage) => {
let countryCode = '';
let displayValue = value;
- let error = errorMessage;
+ let error = '';
if (value) {
const normalizedValue = value.toLowerCase();
@@ -20,8 +20,11 @@ const validateCountryField = (value, countryList, errorMessage) => {
if (selectedCountry) {
countryCode = selectedCountry[COUNTRY_CODE_KEY];
displayValue = selectedCountry[COUNTRY_DISPLAY_KEY];
- error = '';
+ } else {
+ error = invalidCountryErrorMessage;
}
+ } else {
+ error = emptyErrorMessage;
}
return { error, countryCode, displayValue };
};
diff --git a/src/register/components/ConfigurableRegistrationForm.test.jsx b/src/register/components/ConfigurableRegistrationForm.test.jsx
index eb6028f1..19aaff0f 100644
--- a/src/register/components/ConfigurableRegistrationForm.test.jsx
+++ b/src/register/components/ConfigurableRegistrationForm.test.jsx
@@ -285,6 +285,26 @@ describe('ConfigurableRegistrationForm', () => {
expect(registrationPage.find('div[feedback-for="country"]').text()).toEqual(countryError);
expect(registrationPage.find('#confirm_email-error').last().text()).toEqual(confirmEmailError);
});
+
+ it('should show country field validation when country name is invalid', () => {
+ const invalidCountryError = 'Country must match with an option available in the dropdown.';
+
+ store = mockStore({
+ ...initialState,
+ commonComponents: {
+ ...initialState.commonComponents,
+ fieldDescriptions: {
+ country: { name: 'country' },
+ },
+ },
+ });
+ const registrationPage = mount(routerWrapper(reduxWrapper()));
+ registrationPage.find('input[name="country"]').simulate('blur', { target: { value: 'Pak', name: 'country' } });
+
+ registrationPage.find('button.btn-brand').simulate('click');
+ expect(registrationPage.find('div[feedback-for="country"]').text()).toEqual(invalidCountryError);
+ });
+
it('should show error if email and confirm email fields do not match', () => {
store = mockStore({
...initialState,
diff --git a/src/register/data/utils.js b/src/register/data/utils.js
index ca060512..591640eb 100644
--- a/src/register/data/utils.js
+++ b/src/register/data/utils.js
@@ -48,6 +48,9 @@ export const isFormValid = (
if (!configurableFormFields?.country?.displayValue) {
fieldErrors.country = formatMessage(messages['empty.country.field.error']);
isValid = false;
+ } else if (!configurableFormFields?.country?.countryCode) {
+ fieldErrors.country = formatMessage(messages['invalid.country.field.error']);
+ isValid = false;
}
}
Object.keys(fieldDescriptions).forEach(key => {
diff --git a/src/register/messages.jsx b/src/register/messages.jsx
index 0e7c41d1..463a6a2b 100644
--- a/src/register/messages.jsx
+++ b/src/register/messages.jsx
@@ -111,6 +111,11 @@ const messages = defineMessages({
defaultMessage: 'Select your country or region of residence',
description: 'Error message when no country/region is selected',
},
+ 'invalid.country.field.error': {
+ id: 'invalid.country.field.error',
+ defaultMessage: 'Country must match with an option available in the dropdown.',
+ description: 'Error message when country is invalid',
+ },
'email.do.not.match': {
id: 'email.do.not.match',
defaultMessage: 'The email addresses do not match.',
diff --git a/src/sass/_registration.scss b/src/sass/_registration.scss
index f11af0e0..6d3cb1bb 100644
--- a/src/sass/_registration.scss
+++ b/src/sass/_registration.scss
@@ -107,3 +107,11 @@
right: 0.5rem;
}
}
+
+.form-field-error {
+ border: 2px solid var(--danger-300, #CA3A2F) !important;
+
+ input {
+ border: none;
+ }
+}