diff --git a/src/profile/__mocks__/invalidUser.mockStore.js b/src/profile/__mocks__/invalidUser.mockStore.js
index 5e45bb2ac..ec62b98cc 100644
--- a/src/profile/__mocks__/invalidUser.mockStore.js
+++ b/src/profile/__mocks__/invalidUser.mockStore.js
@@ -29,6 +29,7 @@ module.exports = {
drafts: {},
isLoadingProfile: false,
isAuthenticatedUserProfile: true,
+ countriesCodesList: [],
},
router: {
location: {
diff --git a/src/profile/__mocks__/savingEditedBio.mockStore.js b/src/profile/__mocks__/savingEditedBio.mockStore.js
index 63551e823..7ba9a52f0 100644
--- a/src/profile/__mocks__/savingEditedBio.mockStore.js
+++ b/src/profile/__mocks__/savingEditedBio.mockStore.js
@@ -125,7 +125,8 @@ module.exports = {
}
],
drafts: {},
- isLoadingProfile: false
+ isLoadingProfile: false,
+ countriesCodesList: [],
},
router: {
location: {
diff --git a/src/profile/__mocks__/viewOtherProfile.mockStore.js b/src/profile/__mocks__/viewOtherProfile.mockStore.js
index e894d483e..b35f61d4e 100644
--- a/src/profile/__mocks__/viewOtherProfile.mockStore.js
+++ b/src/profile/__mocks__/viewOtherProfile.mockStore.js
@@ -86,6 +86,7 @@ module.exports = {
drafts: {},
isLoadingProfile: false,
learningGoal: 'advance_career',
+ countriesCodesList: [],
},
router: {
location: {
diff --git a/src/profile/__mocks__/viewOwnProfile.mockStore.js b/src/profile/__mocks__/viewOwnProfile.mockStore.js
index 9e9419eb0..b40d8f093 100644
--- a/src/profile/__mocks__/viewOwnProfile.mockStore.js
+++ b/src/profile/__mocks__/viewOwnProfile.mockStore.js
@@ -124,6 +124,7 @@ module.exports = {
createdDate: '2019-03-04T19:31:39.896806Z'
}
],
+ countriesCodesList:[{code:"AX"},{code:"AL"}],
drafts: {},
isLoadingProfile: false
},
diff --git a/src/profile/__snapshots__/ProfilePage.test.jsx.snap b/src/profile/__snapshots__/ProfilePage.test.jsx.snap
index 7635041d8..6958f2e83 100644
--- a/src/profile/__snapshots__/ProfilePage.test.jsx.snap
+++ b/src/profile/__snapshots__/ProfilePage.test.jsx.snap
@@ -976,1256 +976,11 @@ exports[` Renders correctly in various states test country edit w
>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
({
type: FETCH_PROFILE.SUCCESS,
account,
preferences,
courseCertificates,
isAuthenticatedUserProfile,
+ countriesCodesList,
});
export const fetchProfileReset = () => ({
diff --git a/src/profile/data/constants.js b/src/profile/data/constants.js
index 1db167380..de97069fc 100644
--- a/src/profile/data/constants.js
+++ b/src/profile/data/constants.js
@@ -22,7 +22,12 @@ const SOCIAL = {
},
};
+const FIELD_LABELS = {
+ COUNTRY: 'country',
+};
+
export {
EDUCATION_LEVELS,
SOCIAL,
+ FIELD_LABELS,
};
diff --git a/src/profile/data/reducers.js b/src/profile/data/reducers.js
index 33b29fb18..0d374c68b 100644
--- a/src/profile/data/reducers.js
+++ b/src/profile/data/reducers.js
@@ -22,6 +22,7 @@ export const initialState = {
drafts: {},
isLoadingProfile: true,
isAuthenticatedUserProfile: false,
+ countriesCodesList: [],
};
const profilePage = (state = initialState, action = {}) => {
@@ -42,6 +43,7 @@ const profilePage = (state = initialState, action = {}) => {
courseCertificates: action.courseCertificates,
isLoadingProfile: false,
isAuthenticatedUserProfile: action.isAuthenticatedUserProfile,
+ countriesCodesList: action.countriesCodesList,
};
case SAVE_PROFILE.BEGIN:
return {
diff --git a/src/profile/data/sagas.js b/src/profile/data/sagas.js
index 7c9e409f8..e2ebba5c7 100644
--- a/src/profile/data/sagas.js
+++ b/src/profile/data/sagas.js
@@ -41,6 +41,7 @@ export function* handleFetchProfile(action) {
let preferences = {};
let account = userAccount;
let courseCertificates = null;
+ let countriesCodesList = [];
try {
yield put(fetchProfileBegin());
@@ -49,6 +50,7 @@ export function* handleFetchProfile(action) {
const calls = [
call(ProfileApiService.getAccount, username),
call(ProfileApiService.getCourseCertificates, username),
+ call(ProfileApiService.getCountryList),
];
if (isAuthenticatedUserProfile) {
@@ -61,9 +63,9 @@ export function* handleFetchProfile(action) {
const result = yield all(calls);
if (isAuthenticatedUserProfile) {
- [account, courseCertificates, preferences] = result;
+ [account, courseCertificates, countriesCodesList, preferences] = result;
} else {
- [account, courseCertificates] = result;
+ [account, courseCertificates, countriesCodesList] = result;
}
// Set initial visibility values for account
@@ -89,6 +91,7 @@ export function* handleFetchProfile(action) {
preferences,
courseCertificates,
isAuthenticatedUserProfile,
+ countriesCodesList,
));
yield put(fetchProfileReset());
diff --git a/src/profile/data/sagas.test.js b/src/profile/data/sagas.test.js
index 379c0cfd6..291ab1b11 100644
--- a/src/profile/data/sagas.test.js
+++ b/src/profile/data/sagas.test.js
@@ -19,6 +19,7 @@ jest.mock('./services', () => ({
getPreferences: jest.fn(),
getAccount: jest.fn(),
getCourseCertificates: jest.fn(),
+ getCountryList: jest.fn(),
}));
jest.mock('@edx/frontend-platform/auth', () => ({
@@ -68,17 +69,19 @@ describe('RootSaga', () => {
const action = profileActions.fetchProfile('gonzo');
const gen = handleFetchProfile(action);
- const result = [userAccount, [1, 2, 3], { preferences: 'stuff' }];
+ const result = [userAccount, [1, 2, 3], [], { preferences: 'stuff' }];
expect(gen.next().value).toEqual(select(userAccountSelector));
expect(gen.next(selectorData).value).toEqual(put(profileActions.fetchProfileBegin()));
expect(gen.next().value).toEqual(all([
call(ProfileApiService.getAccount, 'gonzo'),
call(ProfileApiService.getCourseCertificates, 'gonzo'),
+ call(ProfileApiService.getCountryList),
call(ProfileApiService.getPreferences, 'gonzo'),
+
]));
expect(gen.next(result).value)
- .toEqual(put(profileActions.fetchProfileSuccess(userAccount, result[2], result[1], true)));
+ .toEqual(put(profileActions.fetchProfileSuccess(userAccount, result[3], result[1], true, [])));
expect(gen.next().value).toEqual(put(profileActions.fetchProfileReset()));
expect(gen.next().value).toBeUndefined();
});
@@ -88,6 +91,7 @@ describe('RootSaga', () => {
username: 'gonzo',
other: 'data',
};
+ const countriesCodesList = [{ code: 'AX' }, { code: 'AL' }];
getAuthenticatedUser.mockReturnValue(userAccount);
const selectorData = {
userAccount,
@@ -96,16 +100,17 @@ describe('RootSaga', () => {
const action = profileActions.fetchProfile('booyah');
const gen = handleFetchProfile(action);
- const result = [{}, [1, 2, 3]];
+ const result = [{}, [1, 2, 3], countriesCodesList];
expect(gen.next().value).toEqual(select(userAccountSelector));
expect(gen.next(selectorData).value).toEqual(put(profileActions.fetchProfileBegin()));
expect(gen.next().value).toEqual(all([
call(ProfileApiService.getAccount, 'booyah'),
call(ProfileApiService.getCourseCertificates, 'booyah'),
+ call(ProfileApiService.getCountryList),
]));
expect(gen.next(result).value)
- .toEqual(put(profileActions.fetchProfileSuccess(result[0], {}, result[1], false)));
+ .toEqual(put(profileActions.fetchProfileSuccess(result[0], {}, result[1], false, countriesCodesList)));
expect(gen.next().value).toEqual(put(profileActions.fetchProfileReset()));
expect(gen.next().value).toBeUndefined();
});
diff --git a/src/profile/data/selectors.js b/src/profile/data/selectors.js
index b04493e6e..41341f5f0 100644
--- a/src/profile/data/selectors.js
+++ b/src/profile/data/selectors.js
@@ -23,6 +23,7 @@ export const isLoadingProfileSelector = state => state.profilePage.isLoadingProf
export const currentlyEditingFieldSelector = state => state.profilePage.currentlyEditingField;
export const accountErrorsSelector = state => state.profilePage.errors;
export const isAuthenticatedUserProfileSelector = state => state.profilePage.isAuthenticatedUserProfile;
+export const countriesCodesListSelector = state => state.profilePage.countriesCodesList;
export const editableFormModeSelector = createSelector(
profileAccountSelector,
@@ -112,7 +113,14 @@ export const sortedLanguagesSelector = createSelector(
export const sortedCountriesSelector = createSelector(
localeSelector,
- locale => getCountryList(locale),
+ countriesCodesListSelector,
+ profileAccountSelector,
+ (locale, countriesCodesList, profileAccount) => {
+ const countryList = getCountryList(locale);
+ const userCountry = profileAccount.country;
+
+ return countryList.filter(({ code }) => code === userCountry || countriesCodesList.find(x => x === code));
+ },
);
export const preferredLanguageSelector = createSelector(
@@ -130,10 +138,12 @@ export const countrySelector = createSelector(
editableFormSelector,
sortedCountriesSelector,
countryMessagesSelector,
- (editableForm, sortedCountries, countryMessages) => ({
+ countriesCodesListSelector,
+ (editableForm, translatedCountries, countryMessages, countriesCodesList) => ({
...editableForm,
- sortedCountries,
+ translatedCountries,
countryMessages,
+ countriesCodesList,
}),
);
diff --git a/src/profile/data/services.js b/src/profile/data/services.js
index 45bf68777..17f15a49b 100644
--- a/src/profile/data/services.js
+++ b/src/profile/data/services.js
@@ -2,6 +2,7 @@ import { ensureConfig, getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient as getHttpClient } from '@edx/frontend-platform/auth';
import { logError } from '@edx/frontend-platform/logging';
import { camelCaseObject, convertKeyNames, snakeCaseObject } from '../utils';
+import { FIELD_LABELS } from './constants';
ensureConfig(['LMS_BASE_URL'], 'Profile API service');
@@ -147,3 +148,21 @@ export async function getCourseCertificates(username) {
return [];
}
}
+
+function extractCountryList(data) {
+ return data?.fields
+ .find(({ name }) => name === FIELD_LABELS.COUNTRY)
+ ?.options?.map(({ value }) => (value)) || [];
+}
+
+export async function getCountryList() {
+ const url = `${getConfig().LMS_BASE_URL}/user_api/v1/account/registration/`;
+
+ try {
+ const { data } = await getHttpClient().get(url);
+ return extractCountryList(data);
+ } catch (e) {
+ logError(e);
+ return [];
+ }
+}
diff --git a/src/profile/forms/Country.jsx b/src/profile/forms/Country.jsx
index 0048017e6..2803145ec 100644
--- a/src/profile/forms/Country.jsx
+++ b/src/profile/forms/Country.jsx
@@ -23,6 +23,7 @@ class Country extends React.Component {
this.handleSubmit = this.handleSubmit.bind(this);
this.handleClose = this.handleClose.bind(this);
this.handleOpen = this.handleOpen.bind(this);
+ this.isDisabledCountry = this.isDisabledCountry.bind(this);
}
handleChange(e) {
@@ -46,6 +47,12 @@ class Country extends React.Component {
this.props.openHandler(this.props.formId);
}
+ isDisabledCountry = (country) => {
+ const { countriesCodesList } = this.props;
+
+ return countriesCodesList.length > 0 && !countriesCodesList.find(code => code === country);
+ };
+
render() {
const {
formId,
@@ -55,7 +62,7 @@ class Country extends React.Component {
saveState,
error,
intl,
- sortedCountries,
+ translatedCountries,
countryMessages,
} = this.props;
@@ -84,8 +91,8 @@ class Country extends React.Component {
onChange={this.handleChange}
>
- {sortedCountries.map(({ code, name }) => (
-
+ {translatedCountries.map(({ code, name }) => (
+
))}
{error !== null && (
@@ -153,7 +160,11 @@ Country.propTypes = {
editMode: PropTypes.oneOf(['editing', 'editable', 'empty', 'static']),
saveState: PropTypes.string,
error: PropTypes.string,
- sortedCountries: PropTypes.arrayOf(PropTypes.shape({
+ translatedCountries: PropTypes.arrayOf(PropTypes.shape({
+ code: PropTypes.string.isRequired,
+ name: PropTypes.string.isRequired,
+ })).isRequired,
+ countriesCodesList: PropTypes.arrayOf(PropTypes.shape({
code: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
})).isRequired,