diff --git a/src/profile-v2/__snapshots__/ProfilePage.test.jsx.snap b/src/profile-v2/__snapshots__/ProfilePage.test.jsx.snap
index 4b246a7..204043b 100644
--- a/src/profile-v2/__snapshots__/ProfilePage.test.jsx.snap
+++ b/src/profile-v2/__snapshots__/ProfilePage.test.jsx.snap
@@ -48,10 +48,9 @@ exports[` Renders correctly in various states viewing other profi
-
({
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 1db1673..de97069 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 4f10bc0..0d374c6 100644
--- a/src/profile/data/reducers.js
+++ b/src/profile/data/reducers.js
@@ -22,7 +22,7 @@ export const initialState = {
drafts: {},
isLoadingProfile: true,
isAuthenticatedUserProfile: false,
- disabledCountries: ['RU'],
+ countriesCodesList: [],
};
const profilePage = (state = initialState, action = {}) => {
@@ -43,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 7c9e409..e2ebba5 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 379c0cf..291ab1b 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 211cdf0..41d69fa 100644
--- a/src/profile/data/selectors.js
+++ b/src/profile/data/selectors.js
@@ -23,7 +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 disabledCountriesSelector = state => state.profilePage.disabledCountries;
+export const countriesCodesListSelector = state => state.profilePage.countriesCodesList;
export const editableFormModeSelector = createSelector(
profileAccountSelector,
@@ -113,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(
@@ -131,13 +138,13 @@ export const countrySelector = createSelector(
editableFormSelector,
sortedCountriesSelector,
countryMessagesSelector,
- disabledCountriesSelector,
+ countriesCodesListSelector,
profileAccountSelector,
- (editableForm, sortedCountries, countryMessages, disabledCountries, account) => ({
+ (editableForm, translatedCountries, countryMessages, countriesCodesList, account) => ({
...editableForm,
- sortedCountries,
+ translatedCountries,
countryMessages,
- disabledCountries,
+ countriesCodesList,
committedCountry: account.country,
}),
);
diff --git a/src/profile/data/services.js b/src/profile/data/services.js
index 45bf687..17f15a4 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 ffeaa24..bfe5ad3 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) {
@@ -35,13 +36,7 @@ class Country extends React.Component {
handleSubmit(e) {
e.preventDefault();
- const {
- country, disabledCountries, formId, submitHandler,
- } = this.props;
-
- if (!disabledCountries.includes(country)) {
- submitHandler(formId);
- }
+ this.props.submitHandler(this.props.formId);
}
handleClose() {
@@ -53,23 +48,9 @@ class Country extends React.Component {
}
isDisabledCountry = (country) => {
- const { disabledCountries } = this.props;
- return disabledCountries.includes(country);
- };
+ const { countriesCodesList } = this.props;
- filteredCountries = (countryList) => {
- const { disabledCountries, committedCountry } = this.props;
-
- if (!disabledCountries.length) {
- return countryList;
- }
-
- return countryList.filter(({ code }) => {
- const isDisabled = this.isDisabledCountry(code);
- const isUserCountry = code === committedCountry;
-
- return !isDisabled || isUserCountry;
- });
+ return countriesCodesList.length > 0 && !countriesCodesList.find(code => code === country);
};
render() {
@@ -81,10 +62,9 @@ class Country extends React.Component {
saveState,
error,
intl,
- sortedCountries,
+ translatedCountries,
countryMessages,
} = this.props;
- const filteredCountries = this.filteredCountries(sortedCountries);
return (
- {filteredCountries.map(({ code, name }) => (
+ {translatedCountries.map(({ code, name }) => (
{name}
))}
@@ -180,13 +160,12 @@ 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,
- disabledCountries: PropTypes.arrayOf(PropTypes.string),
+ countriesCodesList: PropTypes.arrayOf(PropTypes.string).isRequired,
countryMessages: PropTypes.objectOf(PropTypes.string).isRequired,
- committedCountry: PropTypes.string,
// Actions
changeHandler: PropTypes.func.isRequired,
@@ -204,8 +183,6 @@ Country.defaultProps = {
country: null,
visibilityCountry: 'private',
error: null,
- disabledCountries: [],
- committedCountry: '',
};
export default connect(