Compare commits
41 Commits
open-relea
...
djoy/dev_a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e91cdacfc9 | ||
|
|
6740ad3672 | ||
|
|
ca14d3d279 | ||
|
|
9dbe58b10f | ||
|
|
50d80ef614 | ||
|
|
fe6b76da7e | ||
|
|
7fff13137d | ||
|
|
91282cff74 | ||
|
|
45be830f18 | ||
|
|
122affbb6d | ||
|
|
48a97b769f | ||
|
|
bdcc09f6ba | ||
|
|
ac4fb6a340 | ||
|
|
409d365125 | ||
|
|
53985e94d8 | ||
|
|
0d9a39afd7 | ||
|
|
cbb860bb16 | ||
|
|
695df9aa0b | ||
|
|
603304b799 | ||
|
|
d3e5931d05 | ||
|
|
6804f7e127 | ||
|
|
4b16673780 | ||
|
|
6674025bd4 | ||
|
|
0dab2d03eb | ||
|
|
df1a84feb7 | ||
|
|
334a9b090e | ||
|
|
5d06276838 | ||
|
|
e391e427f1 | ||
|
|
b71328fd3f | ||
|
|
3b9b3f8840 | ||
|
|
30e837306f | ||
|
|
c7a0c1d799 | ||
|
|
337c97e3a0 | ||
|
|
0de4496953 | ||
|
|
359ae7f1fb | ||
|
|
8d467f01dc | ||
|
|
20debcd79e | ||
|
|
6a7cbf88df | ||
|
|
1b3880ee1b | ||
|
|
79cebaf6df | ||
|
|
8686af563e |
1
.env
1
.env
@@ -25,6 +25,7 @@ FAVICON_URL=''
|
|||||||
COLLECT_YEAR_OF_BIRTH=true
|
COLLECT_YEAR_OF_BIRTH=true
|
||||||
APP_ID=''
|
APP_ID=''
|
||||||
MFE_CONFIG_API_URL=''
|
MFE_CONFIG_API_URL=''
|
||||||
|
SEARCH_CATALOG_URL=''
|
||||||
ENABLE_SKILLS_BUILDER=''
|
ENABLE_SKILLS_BUILDER=''
|
||||||
ENABLE_SKILLS_BUILDER_PROFILE=''
|
ENABLE_SKILLS_BUILDER_PROFILE=''
|
||||||
ALGOLIA_APP_ID=''
|
ALGOLIA_APP_ID=''
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico
|
|||||||
COLLECT_YEAR_OF_BIRTH=true
|
COLLECT_YEAR_OF_BIRTH=true
|
||||||
APP_ID=''
|
APP_ID=''
|
||||||
MFE_CONFIG_API_URL=''
|
MFE_CONFIG_API_URL=''
|
||||||
|
SEARCH_CATALOG_URL='http://localhost:18000/courses'
|
||||||
ENABLE_SKILLS_BUILDER='true'
|
ENABLE_SKILLS_BUILDER='true'
|
||||||
ENABLE_SKILLS_BUILDER_PROFILE=''
|
ENABLE_SKILLS_BUILDER_PROFILE=''
|
||||||
ALGOLIA_APP_ID=''
|
ALGOLIA_APP_ID=''
|
||||||
|
|||||||
6
.github/workflows/ci.yml
vendored
6
.github/workflows/ci.yml
vendored
@@ -13,13 +13,13 @@ jobs:
|
|||||||
- i18n_extract
|
- i18n_extract
|
||||||
- lint
|
- lint
|
||||||
- test
|
- test
|
||||||
node: [16]
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
- name: Setup Nodejs Env
|
||||||
|
run: echo "NODE_VER=`cat .nvmrc`" >> $GITHUB_ENV
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ env.NODE_VER }}
|
||||||
- run: npm install -g npm@8.x.x
|
|
||||||
- run: make requirements
|
- run: make requirements
|
||||||
- run: make test NPM_TESTS=build
|
- run: make test NPM_TESTS=build
|
||||||
- run: make test NPM_TESTS=${{ matrix.npm-test }}
|
- run: make test NPM_TESTS=${{ matrix.npm-test }}
|
||||||
|
|||||||
2
.github/workflows/lockfileversion-check.yml
vendored
2
.github/workflows/lockfileversion-check.yml
vendored
@@ -10,4 +10,4 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
version-check:
|
version-check:
|
||||||
uses: openedx/.github/.github/workflows/lockfileversion-check.yml@master
|
uses: openedx/.github/.github/workflows/lockfileversion-check-v3.yml@master
|
||||||
|
|||||||
15
Makefile
15
Makefile
@@ -2,6 +2,7 @@ export TRANSIFEX_RESOURCE = frontend-app-profile
|
|||||||
transifex_resource = frontend-app-profile
|
transifex_resource = frontend-app-profile
|
||||||
transifex_langs = "ar,fr,es_419,zh_CN,pt,it,de,uk,ru,hi,fr_CA"
|
transifex_langs = "ar,fr,es_419,zh_CN,pt,it,de,uk,ru,hi,fr_CA"
|
||||||
|
|
||||||
|
intl_imports = ./node_modules/.bin/intl-imports.js
|
||||||
transifex_utils = ./node_modules/.bin/transifex-utils.js
|
transifex_utils = ./node_modules/.bin/transifex-utils.js
|
||||||
i18n = ./src/i18n
|
i18n = ./src/i18n
|
||||||
transifex_input = $(i18n)/transifex_input.json
|
transifex_input = $(i18n)/transifex_input.json
|
||||||
@@ -49,9 +50,23 @@ push_translations:
|
|||||||
# Pushing comments to Transifex...
|
# Pushing comments to Transifex...
|
||||||
./node_modules/@edx/reactifex/bash_scripts/put_comments_v3.sh
|
./node_modules/@edx/reactifex/bash_scripts/put_comments_v3.sh
|
||||||
|
|
||||||
|
ifeq ($(OPENEDX_ATLAS_PULL),)
|
||||||
# Pulls translations from Transifex.
|
# Pulls translations from Transifex.
|
||||||
pull_translations:
|
pull_translations:
|
||||||
tx pull -t -f --mode reviewed --languages=$(transifex_langs)
|
tx pull -t -f --mode reviewed --languages=$(transifex_langs)
|
||||||
|
else
|
||||||
|
# Experimental: OEP-58 Pulls translations using atlas
|
||||||
|
pull_translations:
|
||||||
|
rm -rf src/i18n/messages
|
||||||
|
mkdir src/i18n/messages
|
||||||
|
cd src/i18n/messages \
|
||||||
|
&& atlas pull --filter=$(transifex_langs) \
|
||||||
|
translations/frontend-component-header/src/i18n/messages:frontend-component-header \
|
||||||
|
translations/frontend-component-footer/src/i18n/messages:frontend-component-footer \
|
||||||
|
translations/frontend-app-profile/src/i18n/messages:frontend-app-profile
|
||||||
|
|
||||||
|
$(intl_imports) frontend-component-header frontend-component-footer frontend-app-profile
|
||||||
|
endif
|
||||||
|
|
||||||
# This target is used by Travis.
|
# This target is used by Travis.
|
||||||
validate-no-uncommitted-package-lock-changes:
|
validate-no-uncommitted-package-lock-changes:
|
||||||
|
|||||||
23439
package-lock.json
generated
23439
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
23
package.json
23
package.json
@@ -28,18 +28,19 @@
|
|||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@edx/brand": "npm:@edx/brand-openedx@1.2.0",
|
"@edx/brand": "npm:@edx/brand-openedx@1.2.0",
|
||||||
"@edx/frontend-component-footer": "11.7.1",
|
"@edx/frontend-component-footer": "12.0.0",
|
||||||
"@edx/frontend-component-header": "3.6.5",
|
"@edx/frontend-component-header": "4.0.0",
|
||||||
"@edx/frontend-platform": "3.3.1",
|
"@edx/frontend-platform": "4.5.1",
|
||||||
"@edx/paragon": "^20.20.0",
|
"@edx/paragon": "^20.43.0",
|
||||||
"@fortawesome/fontawesome-svg-core": "1.2.36",
|
"@fortawesome/fontawesome-svg-core": "1.2.36",
|
||||||
"@fortawesome/free-brands-svg-icons": "5.15.4",
|
"@fortawesome/free-brands-svg-icons": "5.15.4",
|
||||||
"@fortawesome/free-regular-svg-icons": "5.15.4",
|
"@fortawesome/free-regular-svg-icons": "5.15.4",
|
||||||
"@fortawesome/free-solid-svg-icons": "5.15.4",
|
"@fortawesome/free-solid-svg-icons": "5.15.4",
|
||||||
"@fortawesome/react-fontawesome": "0.2.0",
|
"@fortawesome/react-fontawesome": "0.2.0",
|
||||||
"algoliasearch": "4.6.0",
|
"@pact-foundation/pact": "^11.0.2",
|
||||||
|
"algoliasearch": "4.17.2",
|
||||||
"classnames": "2.3.2",
|
"classnames": "2.3.2",
|
||||||
"core-js": "3.27.2",
|
"core-js": "3.30.2",
|
||||||
"history": "4.10.1",
|
"history": "4.10.1",
|
||||||
"lodash.camelcase": "4.3.0",
|
"lodash.camelcase": "4.3.0",
|
||||||
"lodash.get": "4.4.2",
|
"lodash.get": "4.4.2",
|
||||||
@@ -59,15 +60,15 @@
|
|||||||
"redux-saga": "1.2.3",
|
"redux-saga": "1.2.3",
|
||||||
"redux-thunk": "2.4.2",
|
"redux-thunk": "2.4.2",
|
||||||
"regenerator-runtime": "0.13.11",
|
"regenerator-runtime": "0.13.11",
|
||||||
"reselect": "4.1.7",
|
"reselect": "4.1.8",
|
||||||
"universal-cookie": "4.0.4"
|
"universal-cookie": "4.0.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "17.5.1",
|
"@commitlint/cli": "17.6.5",
|
||||||
"@commitlint/config-angular": "17.4.4",
|
"@commitlint/config-angular": "17.6.5",
|
||||||
"@edx/browserslist-config": "^1.1.1",
|
"@edx/browserslist-config": "^1.1.1",
|
||||||
"@edx/frontend-build": "12.8.6",
|
"@edx/frontend-build": "12.8.51",
|
||||||
"@edx/reactifex": "2.1.1",
|
"@edx/reactifex": "2.2.0",
|
||||||
"@testing-library/react": "11.2.7",
|
"@testing-library/react": "11.2.7",
|
||||||
"codecov": "3.8.3",
|
"codecov": "3.8.3",
|
||||||
"enzyme": "3.11.0",
|
"enzyme": "3.11.0",
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
import { messages as headerMessages } from '@edx/frontend-component-header';
|
||||||
|
import { messages as footerMessages } from '@edx/frontend-component-footer';
|
||||||
|
|
||||||
import arMessages from './messages/ar.json';
|
import arMessages from './messages/ar.json';
|
||||||
import frMessages from './messages/fr.json';
|
import frMessages from './messages/fr.json';
|
||||||
import es419Messages from './messages/es_419.json';
|
import es419Messages from './messages/es_419.json';
|
||||||
@@ -11,7 +14,7 @@ import hiMessages from './messages/hi.json';
|
|||||||
import frCAMessages from './messages/fr_CA.json';
|
import frCAMessages from './messages/fr_CA.json';
|
||||||
// no need to import en messages-- they are in the defaultMessage field
|
// no need to import en messages-- they are in the defaultMessage field
|
||||||
|
|
||||||
const messages = {
|
const appMessages = {
|
||||||
ar: arMessages,
|
ar: arMessages,
|
||||||
'es-419': es419Messages,
|
'es-419': es419Messages,
|
||||||
fr: frMessages,
|
fr: frMessages,
|
||||||
@@ -25,4 +28,8 @@ const messages = {
|
|||||||
uk: ukMessages,
|
uk: ukMessages,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default messages;
|
export default [
|
||||||
|
headerMessages,
|
||||||
|
footerMessages,
|
||||||
|
appMessages,
|
||||||
|
];
|
||||||
@@ -34,11 +34,11 @@
|
|||||||
"profile.formcontrols.button.saved": "تم الحفظ",
|
"profile.formcontrols.button.saved": "تم الحفظ",
|
||||||
"profile.visibility.who.just.me": "أنا فقط",
|
"profile.visibility.who.just.me": "أنا فقط",
|
||||||
"profile.visibility.who.everyone": "جميع من على {siteName}",
|
"profile.visibility.who.everyone": "جميع من على {siteName}",
|
||||||
"profile.learningGoal.learningGoal": "Learning Goal",
|
"profile.learningGoal.learningGoal": "هدف التعلم",
|
||||||
"profile.learningGoal.options.start_career": "I want to start my career",
|
"profile.learningGoal.options.start_career": "أريد أن أبدأ مسيرتي المهنية",
|
||||||
"profile.learningGoal.options.advance_career": "I want to advance my career",
|
"profile.learningGoal.options.advance_career": "أريد أن ارتقي في مسيرتي المهنية",
|
||||||
"profile.learningGoal.options.learn_something_new": "I want to learn something new",
|
"profile.learningGoal.options.learn_something_new": "أريد أن أتعلم شيئًا جديدًا",
|
||||||
"profile.learningGoal.options.something_else": "Something else",
|
"profile.learningGoal.options.something_else": "شيء آخر",
|
||||||
"profile.name.full.name": "الاسم الكامل",
|
"profile.name.full.name": "الاسم الكامل",
|
||||||
"profile.name.details": "هذا هو الاسم الذي يظهر في حسابك وفي شهاداتك",
|
"profile.name.details": "هذا هو الاسم الذي يظهر في حسابك وفي شهاداتك",
|
||||||
"profile.name.empty": "إضافة الاسم",
|
"profile.name.empty": "إضافة الاسم",
|
||||||
@@ -54,31 +54,32 @@
|
|||||||
"profile.viewMyRecords": "عرض سجلّاتي",
|
"profile.viewMyRecords": "عرض سجلّاتي",
|
||||||
"profile.loading": "يتم تحميل الملف الشخصي...",
|
"profile.loading": "يتم تحميل الملف الشخصي...",
|
||||||
"profile.username.description": "معلومات ملفك الشخصي تظهر لك فقط. وحده اسم المستخدم الخاص بك يظهر للآخرين على {siteName}.",
|
"profile.username.description": "معلومات ملفك الشخصي تظهر لك فقط. وحده اسم المستخدم الخاص بك يظهر للآخرين على {siteName}.",
|
||||||
"skills.builder.header.title": "Skills Builder",
|
"skills.builder.header.title": "باني المهارات",
|
||||||
"skills.builder.header.subheading": "Let edX be your guide",
|
"skills.builder.header.subheading": "دع (المنصة التعليمية أو edX) ان تكون دليلك",
|
||||||
"go.back.button": "Go Back",
|
"skills.builder.header.title.is.medium": "edX Skills builder",
|
||||||
"next.step.button": "Next Step",
|
"go.back.button": "العودة إلى الخلف",
|
||||||
"exit.button": "Exit",
|
"next.step.button": "الخطوة التالية",
|
||||||
"select.preferences": "Select preferences",
|
"exit.button": "خروج",
|
||||||
"review.results": "Review results",
|
"select.preferences": "حدد التفضيلاتك",
|
||||||
"skills.builder.description": "Find the right courses and programs that help you reach your goals.",
|
"review.results": "مراجعة النتائج",
|
||||||
"learning.goal.prompt": "First, tell us what you want to achieve",
|
"skills.builder.description": "ابحث عن الدورات والبرامج المناسبة التي تساعدك في الوصول إلى أهدافك.",
|
||||||
"select.learning.goal": "Select a goal",
|
"learning.goal.prompt": "أولاً، أخبرنا بما تريد تحقيقه",
|
||||||
"learning.goal.start_career": "I want to start my career",
|
"select.learning.goal": "اختر هدفًا",
|
||||||
"learning.goal.advance_career": "I want to advance my career",
|
"learning.goal.start_career": "أريد أن أبدأ مسيرتي المهنية",
|
||||||
"learning.goal.change_career": "I want to change careers",
|
"learning.goal.advance_career": "أريد أن ارتقي في مهنتي",
|
||||||
"learning.goal.something.new": "I want to learn something new",
|
"learning.goal.change_career": "اريد تغيير المهنتي",
|
||||||
"learning.goal.something.else": "Something else",
|
"learning.goal.something.new": "أريد أن أتعلم شيئًا جديدًا",
|
||||||
"job.title.prompt": "Next, search and select your current job title",
|
"learning.goal.something.else": "شيء آخر",
|
||||||
"job.title.input.placeholder.text": "Search and select a job title",
|
"job.title.prompt": "بعد ذلك، ابحث وحدد المسمى الوظيفي الحالي الخاص بك",
|
||||||
"student.checkbox.prompt": "I'm a student",
|
"job.title.input.placeholder.text": "أبحث واختار مسمى وظيفي",
|
||||||
"currently.looking.checkbox.prompt": "I'm currently looking for work",
|
"student.checkbox.prompt": "أنا طالب",
|
||||||
"career.interest.prompt": "What careers are you interested in?",
|
"currently.looking.checkbox.prompt": "أنا حاليا أبحث عن عمل",
|
||||||
"career.interest.input.placeholder.text": "Select up to 3 new job titles",
|
"career.interest.prompt": "ما هي المهن التي تثير اهتمامك؟",
|
||||||
"career.interest.remove.button.alt.text": "Remove career interest: ",
|
"career.interest.input.placeholder.text": "حدد ما يصل إلى ثلاث عناوين وظيفية جديدة",
|
||||||
"matches.found.success.alert": "We found skills and courses that match your preferences!",
|
"career.interest.remove.button.alt.text": "إزالة الاهتمام الوظيفي:",
|
||||||
"matches.not.found.danger.alert": "We were not able to retrieve recommendations at this time. Please try again later.",
|
"matches.found.success.alert": "وجدنا المهارات والدورات التي تناسب تفضيلاتك!",
|
||||||
"related.skills.heading": "Related Skills",
|
"matches.not.found.danger.alert": "لم نتمكن من استرداد التوصيات في هذا الوقت. الرجاء معاودة المحاولة في وقت لاحق.",
|
||||||
"related.skills.selectable.box.label.text": "Related skills:",
|
"related.skills.heading": "مهارات ذات الصلة",
|
||||||
|
"related.skills.selectable.box.label.text": "مهارات ذات الصلة:",
|
||||||
"product.recommendations.header.text": "{productType} recommendations for {jobName}"
|
"product.recommendations.header.text": "{productType} recommendations for {jobName}"
|
||||||
}
|
}
|
||||||
@@ -56,6 +56,7 @@
|
|||||||
"profile.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}.",
|
"profile.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}.",
|
||||||
"skills.builder.header.title": "Skills Builder",
|
"skills.builder.header.title": "Skills Builder",
|
||||||
"skills.builder.header.subheading": "Let edX be your guide",
|
"skills.builder.header.subheading": "Let edX be your guide",
|
||||||
|
"skills.builder.header.title.is.medium": "edX Skills builder",
|
||||||
"go.back.button": "Go Back",
|
"go.back.button": "Go Back",
|
||||||
"next.step.button": "Next Step",
|
"next.step.button": "Next Step",
|
||||||
"exit.button": "Exit",
|
"exit.button": "Exit",
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
"profile.username.description": "La información del perfil solo la visualiza usted. Solo el nombre de usuario es visible para los demás en {siteName}.",
|
"profile.username.description": "La información del perfil solo la visualiza usted. Solo el nombre de usuario es visible para los demás en {siteName}.",
|
||||||
"skills.builder.header.title": "Constructor de habilidades",
|
"skills.builder.header.title": "Constructor de habilidades",
|
||||||
"skills.builder.header.subheading": "Dejanos ser tu guía",
|
"skills.builder.header.subheading": "Dejanos ser tu guía",
|
||||||
|
"skills.builder.header.title.is.medium": "edX Skills builder",
|
||||||
"go.back.button": "Volver Atrás",
|
"go.back.button": "Volver Atrás",
|
||||||
"next.step.button": "Próximo paso",
|
"next.step.button": "Próximo paso",
|
||||||
"exit.button": "Salida",
|
"exit.button": "Salida",
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
"profile.username.description": "Les informations de votre profil ne sont visibles que par vous. Seul votre nom d'utilisateur est visible par les autres sur {siteName}.",
|
"profile.username.description": "Les informations de votre profil ne sont visibles que par vous. Seul votre nom d'utilisateur est visible par les autres sur {siteName}.",
|
||||||
"skills.builder.header.title": "Skills Builder",
|
"skills.builder.header.title": "Skills Builder",
|
||||||
"skills.builder.header.subheading": "Let edX be your guide",
|
"skills.builder.header.subheading": "Let edX be your guide",
|
||||||
|
"skills.builder.header.title.is.medium": "edX Skills builder",
|
||||||
"go.back.button": "Go Back",
|
"go.back.button": "Go Back",
|
||||||
"next.step.button": "Next Step",
|
"next.step.button": "Next Step",
|
||||||
"exit.button": "Exit",
|
"exit.button": "Exit",
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
"profile.username.description": "Les informations de votre profil ne sont visibles que par vous. Seul votre nom d'utilisateur est visible par les autres sur {siteName}.",
|
"profile.username.description": "Les informations de votre profil ne sont visibles que par vous. Seul votre nom d'utilisateur est visible par les autres sur {siteName}.",
|
||||||
"skills.builder.header.title": "Constructeur de compétences",
|
"skills.builder.header.title": "Constructeur de compétences",
|
||||||
"skills.builder.header.subheading": "Laissez EDUlib être votre guide",
|
"skills.builder.header.subheading": "Laissez EDUlib être votre guide",
|
||||||
|
"skills.builder.header.title.is.medium": "Générateur de compétences edX",
|
||||||
"go.back.button": "Retour",
|
"go.back.button": "Retour",
|
||||||
"next.step.button": "Prochaine étape",
|
"next.step.button": "Prochaine étape",
|
||||||
"exit.button": "Sortie",
|
"exit.button": "Sortie",
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
"profile.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}.",
|
"profile.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}.",
|
||||||
"skills.builder.header.title": "Skills Builder",
|
"skills.builder.header.title": "Skills Builder",
|
||||||
"skills.builder.header.subheading": "Let edX be your guide",
|
"skills.builder.header.subheading": "Let edX be your guide",
|
||||||
|
"skills.builder.header.title.is.medium": "edX Skills builder",
|
||||||
"go.back.button": "Go Back",
|
"go.back.button": "Go Back",
|
||||||
"next.step.button": "Next Step",
|
"next.step.button": "Next Step",
|
||||||
"exit.button": "Exit",
|
"exit.button": "Exit",
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
"profile.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}.",
|
"profile.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}.",
|
||||||
"skills.builder.header.title": "Skills Builder",
|
"skills.builder.header.title": "Skills Builder",
|
||||||
"skills.builder.header.subheading": "Let edX be your guide",
|
"skills.builder.header.subheading": "Let edX be your guide",
|
||||||
|
"skills.builder.header.title.is.medium": "edX Skills builder",
|
||||||
"go.back.button": "Go Back",
|
"go.back.button": "Go Back",
|
||||||
"next.step.button": "Next Step",
|
"next.step.button": "Next Step",
|
||||||
"exit.button": "Exit",
|
"exit.button": "Exit",
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
"profile.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}.",
|
"profile.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}.",
|
||||||
"skills.builder.header.title": "Skills Builder",
|
"skills.builder.header.title": "Skills Builder",
|
||||||
"skills.builder.header.subheading": "Let edX be your guide",
|
"skills.builder.header.subheading": "Let edX be your guide",
|
||||||
|
"skills.builder.header.title.is.medium": "edX Skills builder",
|
||||||
"go.back.button": "Go Back",
|
"go.back.button": "Go Back",
|
||||||
"next.step.button": "Next Step",
|
"next.step.button": "Next Step",
|
||||||
"exit.button": "Exit",
|
"exit.button": "Exit",
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
"profile.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}.",
|
"profile.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}.",
|
||||||
"skills.builder.header.title": "Skills Builder",
|
"skills.builder.header.title": "Skills Builder",
|
||||||
"skills.builder.header.subheading": "Let edX be your guide",
|
"skills.builder.header.subheading": "Let edX be your guide",
|
||||||
|
"skills.builder.header.title.is.medium": "edX Skills builder",
|
||||||
"go.back.button": "Go Back",
|
"go.back.button": "Go Back",
|
||||||
"next.step.button": "Next Step",
|
"next.step.button": "Next Step",
|
||||||
"exit.button": "Exit",
|
"exit.button": "Exit",
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
"profile.certificate.organization.label": "From",
|
"profile.certificate.organization.label": "From",
|
||||||
"profile.certificate.completion.date.label": "Completed on {date}",
|
"profile.certificate.completion.date.label": "Completed on {date}",
|
||||||
"profile.no.certificates": "You don't have any certificates yet.",
|
"profile.no.certificates": "You don't have any certificates yet.",
|
||||||
"profile.certificates.my.certificates": "My Certificates",
|
"profile.certificates.my.certificates": "Мої сертифікати",
|
||||||
"profile.certificates.view.certificate": "View Certificate",
|
"profile.certificates.view.certificate": "View Certificate",
|
||||||
"profile.certificates.types.verified": "Verified Certificate",
|
"profile.certificates.types.verified": "Verified Certificate",
|
||||||
"profile.certificates.types.professional": "Professional Certificate",
|
"profile.certificates.types.professional": "Professional Certificate",
|
||||||
@@ -56,6 +56,7 @@
|
|||||||
"profile.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}.",
|
"profile.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}.",
|
||||||
"skills.builder.header.title": "Skills Builder",
|
"skills.builder.header.title": "Skills Builder",
|
||||||
"skills.builder.header.subheading": "Let edX be your guide",
|
"skills.builder.header.subheading": "Let edX be your guide",
|
||||||
|
"skills.builder.header.title.is.medium": "edX Skills builder",
|
||||||
"go.back.button": "Go Back",
|
"go.back.button": "Go Back",
|
||||||
"next.step.button": "Next Step",
|
"next.step.button": "Next Step",
|
||||||
"exit.button": "Exit",
|
"exit.button": "Exit",
|
||||||
|
|||||||
@@ -56,6 +56,7 @@
|
|||||||
"profile.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}.",
|
"profile.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}.",
|
||||||
"skills.builder.header.title": "Skills Builder",
|
"skills.builder.header.title": "Skills Builder",
|
||||||
"skills.builder.header.subheading": "Let edX be your guide",
|
"skills.builder.header.subheading": "Let edX be your guide",
|
||||||
|
"skills.builder.header.title.is.medium": "edX Skills builder",
|
||||||
"go.back.button": "Go Back",
|
"go.back.button": "Go Back",
|
||||||
"next.step.button": "Next Step",
|
"next.step.button": "Next Step",
|
||||||
"exit.button": "Exit",
|
"exit.button": "Exit",
|
||||||
|
|||||||
@@ -16,10 +16,10 @@ import {
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
|
|
||||||
import Header, { messages as headerMessages } from '@edx/frontend-component-header';
|
import Header from '@edx/frontend-component-header';
|
||||||
import Footer, { messages as footerMessages } from '@edx/frontend-component-footer';
|
import Footer from '@edx/frontend-component-footer';
|
||||||
|
|
||||||
import appMessages from './i18n';
|
import messages from './i18n';
|
||||||
import configureStore from './data/configureStore';
|
import configureStore from './data/configureStore';
|
||||||
|
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
@@ -46,11 +46,7 @@ subscribe(APP_INIT_ERROR, (error) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
initialize({
|
initialize({
|
||||||
messages: [
|
messages,
|
||||||
appMessages,
|
|
||||||
headerMessages,
|
|
||||||
footerMessages,
|
|
||||||
],
|
|
||||||
hydrateAuthenticatedUser: true,
|
hydrateAuthenticatedUser: true,
|
||||||
handlers: {
|
handlers: {
|
||||||
config: () => {
|
config: () => {
|
||||||
@@ -62,6 +58,7 @@ initialize({
|
|||||||
ALGOLIA_JOBS_INDEX_NAME: process.env.ALGOLIA_JOBS_INDEX_NAME || null,
|
ALGOLIA_JOBS_INDEX_NAME: process.env.ALGOLIA_JOBS_INDEX_NAME || null,
|
||||||
ALGOLIA_PRODUCT_INDEX_NAME: process.env.ALGOLIA_PRODUCT_INDEX_NAME || null,
|
ALGOLIA_PRODUCT_INDEX_NAME: process.env.ALGOLIA_PRODUCT_INDEX_NAME || null,
|
||||||
ALGOLIA_SEARCH_API_KEY: process.env.ALGOLIA_SEARCH_API_KEY || null,
|
ALGOLIA_SEARCH_API_KEY: process.env.ALGOLIA_SEARCH_API_KEY || null,
|
||||||
|
MARKETING_SITE_SEARCH_URL: process.env.SEARCH_CATALOG_URL || null,
|
||||||
}, 'App loadConfig override handler');
|
}, 'App loadConfig override handler');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
94
src/pacts/frontend-app-profile-edx-platform.json
Normal file
94
src/pacts/frontend-app-profile-edx-platform.json
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
{
|
||||||
|
"consumer": {
|
||||||
|
"name": "frontend-app-profile"
|
||||||
|
},
|
||||||
|
"interactions": [
|
||||||
|
{
|
||||||
|
"description": "a request for a user's account information",
|
||||||
|
"providerStates": [
|
||||||
|
{
|
||||||
|
"name": "I have user account information"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"request": {
|
||||||
|
"headers": {
|
||||||
|
"Accept": "application/json"
|
||||||
|
},
|
||||||
|
"method": "GET",
|
||||||
|
"path": "/api/user/v1/accounts/edx"
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"body": {
|
||||||
|
"accomplishments_shared": false,
|
||||||
|
"account_privacy": "private",
|
||||||
|
"activation_key": null,
|
||||||
|
"bio": null,
|
||||||
|
"country": null,
|
||||||
|
"course_certificates": null,
|
||||||
|
"date_joined": "2021-07-30T20:01:46Z",
|
||||||
|
"email": "edx@example.com",
|
||||||
|
"extended_profile": [],
|
||||||
|
"gender": null,
|
||||||
|
"goals": null,
|
||||||
|
"id": 3,
|
||||||
|
"is_active": true,
|
||||||
|
"language_proficiencies": [],
|
||||||
|
"last_login": "2023-04-10T18:28:01.771896Z",
|
||||||
|
"level_of_education": null,
|
||||||
|
"mailing_address": null,
|
||||||
|
"name": "",
|
||||||
|
"pending_name_change": null,
|
||||||
|
"phone_number": null,
|
||||||
|
"profile_image": {
|
||||||
|
"has_image": false,
|
||||||
|
"image_url_full": "http://localhost:18000/static/images/profiles/default_500.png",
|
||||||
|
"image_url_large": "http://localhost:18000/static/images/profiles/default_120.png",
|
||||||
|
"image_url_medium": "http://localhost:18000/static/images/profiles/default_50.png",
|
||||||
|
"image_url_small": "http://localhost:18000/static/images/profiles/default_30.png"
|
||||||
|
},
|
||||||
|
"requires_parental_consent": true,
|
||||||
|
"secondary_email": null,
|
||||||
|
"secondary_email_enabled": null,
|
||||||
|
"social_links": [],
|
||||||
|
"state": null,
|
||||||
|
"time_zone": null,
|
||||||
|
"username": "edx",
|
||||||
|
"verified_name": null,
|
||||||
|
"year_of_birth": null
|
||||||
|
},
|
||||||
|
"headers": {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
"matchingRules": {
|
||||||
|
"body": {
|
||||||
|
"$": {
|
||||||
|
"combine": "AND",
|
||||||
|
"matchers": [
|
||||||
|
{
|
||||||
|
"match": "type"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"header": {}
|
||||||
|
},
|
||||||
|
"status": 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"pact-js": {
|
||||||
|
"version": "11.0.2"
|
||||||
|
},
|
||||||
|
"pactRust": {
|
||||||
|
"ffi": "0.4.0",
|
||||||
|
"models": "1.0.4"
|
||||||
|
},
|
||||||
|
"pactSpecification": {
|
||||||
|
"version": "3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"provider": {
|
||||||
|
"name": "edx-platform"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -163,7 +163,7 @@ exports[`<ProfilePage /> Renders correctly in various states test country edit w
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
|
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -241,7 +241,7 @@ exports[`<ProfilePage /> Renders correctly in various states test country edit w
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
|
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -1745,7 +1745,7 @@ exports[`<ProfilePage /> Renders correctly in various states test country edit w
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M22 12A10 10 0 116.122 3.91l1.176 1.618A8 8 0 1020 12h2z"
|
d="M22 12A10 10 0 1 1 6.122 3.91l1.176 1.618A8 8 0 1 0 20 12h2Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -2387,7 +2387,7 @@ exports[`<ProfilePage /> Renders correctly in various states test country edit w
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
|
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -2544,7 +2544,7 @@ exports[`<ProfilePage /> Renders correctly in various states test education edit
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
|
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -2622,7 +2622,7 @@ exports[`<ProfilePage /> Renders correctly in various states test education edit
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
|
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -3100,7 +3100,7 @@ exports[`<ProfilePage /> Renders correctly in various states test education edit
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M22 12A10 10 0 116.122 3.91l1.176 1.618A8 8 0 1020 12h2z"
|
d="M22 12A10 10 0 1 1 6.122 3.91l1.176 1.618A8 8 0 1 0 20 12h2Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -3562,7 +3562,7 @@ exports[`<ProfilePage /> Renders correctly in various states test education edit
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
|
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -3719,7 +3719,7 @@ exports[`<ProfilePage /> Renders correctly in various states test preferreded la
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
|
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -3797,7 +3797,7 @@ exports[`<ProfilePage /> Renders correctly in various states test preferreded la
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
|
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -5060,7 +5060,7 @@ exports[`<ProfilePage /> Renders correctly in various states test preferreded la
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M22 12A10 10 0 116.122 3.91l1.176 1.618A8 8 0 1020 12h2z"
|
d="M22 12A10 10 0 1 1 6.122 3.91l1.176 1.618A8 8 0 1 0 20 12h2Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -5612,7 +5612,7 @@ exports[`<ProfilePage /> Renders correctly in various states test preferreded la
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
|
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -5723,7 +5723,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M12 6.5c2.76 0 5 2.24 5 5 0 .51-.1 1-.24 1.46l3.06 3.06c1.39-1.23 2.49-2.77 3.18-4.53C21.27 7.11 17 4 12 4c-1.27 0-2.49.2-3.64.57l2.17 2.17c.47-.14.96-.24 1.47-.24zM3.42 2.45L2.01 3.87l2.68 2.68A11.738 11.738 0 001 11.5C2.73 15.89 7 19 12 19c1.52 0 2.97-.3 4.31-.82l3.43 3.43 1.41-1.41L3.42 2.45zM12 16.5c-2.76 0-5-2.24-5-5 0-.77.18-1.5.49-2.14l1.57 1.57c-.03.18-.06.37-.06.57 0 1.66 1.34 3 3 3 .2 0 .38-.03.57-.07L14.14 16c-.65.32-1.37.5-2.14.5zm2.97-5.33a2.97 2.97 0 00-2.64-2.64l2.64 2.64z"
|
d="M12 6.5c2.76 0 5 2.24 5 5 0 .51-.1 1-.24 1.46l3.06 3.06c1.39-1.23 2.49-2.77 3.18-4.53C21.27 7.11 17 4 12 4c-1.27 0-2.49.2-3.64.57l2.17 2.17c.47-.14.96-.24 1.47-.24ZM3.42 2.45 2.01 3.87l2.68 2.68A11.738 11.738 0 0 0 1 11.5C2.73 15.89 7 19 12 19c1.52 0 2.97-.3 4.31-.82l3.43 3.43 1.41-1.41L3.42 2.45ZM12 16.5c-2.76 0-5-2.24-5-5 0-.77.18-1.5.49-2.14l1.57 1.57c-.03.18-.06.37-.06.57 0 1.66 1.34 3 3 3 .2 0 .38-.03.57-.07L14.14 16c-.65.32-1.37.5-2.14.5Zm2.97-5.33a2.97 2.97 0 0 0-2.64-2.64l2.64 2.64Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -5784,7 +5784,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M12 6.5c2.76 0 5 2.24 5 5 0 .51-.1 1-.24 1.46l3.06 3.06c1.39-1.23 2.49-2.77 3.18-4.53C21.27 7.11 17 4 12 4c-1.27 0-2.49.2-3.64.57l2.17 2.17c.47-.14.96-.24 1.47-.24zM3.42 2.45L2.01 3.87l2.68 2.68A11.738 11.738 0 001 11.5C2.73 15.89 7 19 12 19c1.52 0 2.97-.3 4.31-.82l3.43 3.43 1.41-1.41L3.42 2.45zM12 16.5c-2.76 0-5-2.24-5-5 0-.77.18-1.5.49-2.14l1.57 1.57c-.03.18-.06.37-.06.57 0 1.66 1.34 3 3 3 .2 0 .38-.03.57-.07L14.14 16c-.65.32-1.37.5-2.14.5zm2.97-5.33a2.97 2.97 0 00-2.64-2.64l2.64 2.64z"
|
d="M12 6.5c2.76 0 5 2.24 5 5 0 .51-.1 1-.24 1.46l3.06 3.06c1.39-1.23 2.49-2.77 3.18-4.53C21.27 7.11 17 4 12 4c-1.27 0-2.49.2-3.64.57l2.17 2.17c.47-.14.96-.24 1.47-.24ZM3.42 2.45 2.01 3.87l2.68 2.68A11.738 11.738 0 0 0 1 11.5C2.73 15.89 7 19 12 19c1.52 0 2.97-.3 4.31-.82l3.43 3.43 1.41-1.41L3.42 2.45ZM12 16.5c-2.76 0-5-2.24-5-5 0-.77.18-1.5.49-2.14l1.57 1.57c-.03.18-.06.37-.06.57 0 1.66 1.34 3 3 3 .2 0 .38-.03.57-.07L14.14 16c-.65.32-1.37.5-2.14.5Zm2.97-5.33a2.97 2.97 0 0 0-2.64-2.64l2.64 2.64Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -6001,7 +6001,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
|
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -6079,7 +6079,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
|
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -6894,7 +6894,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
|
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -7051,7 +7051,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
|
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -7129,7 +7129,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
|
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -7818,7 +7818,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M22 12A10 10 0 116.122 3.91l1.176 1.618A8 8 0 1020 12h2z"
|
d="M22 12A10 10 0 1 1 6.122 3.91l1.176 1.618A8 8 0 1 0 20 12h2Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -8011,7 +8011,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
|
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -8168,7 +8168,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
|
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -8246,7 +8246,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
|
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -8941,7 +8941,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M22 12A10 10 0 116.122 3.91l1.176 1.618A8 8 0 1020 12h2z"
|
d="M22 12A10 10 0 1 1 6.122 3.91l1.176 1.618A8 8 0 1 0 20 12h2Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -9134,7 +9134,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
|
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -10094,7 +10094,7 @@ exports[`<ProfilePage /> Renders correctly in various states without credentials
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
|
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
0
src/profile/data/api.pact.data.js
Normal file
0
src/profile/data/api.pact.data.js
Normal file
90
src/profile/data/api.pact.test.js
Normal file
90
src/profile/data/api.pact.test.js
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
import path from 'path';
|
||||||
|
import { PactV3, MatchersV3 } from '@pact-foundation/pact';
|
||||||
|
|
||||||
|
import { getAccountRequest } from './services';
|
||||||
|
import { getConfig, initializeMockApp, setConfig } from '@edx/frontend-platform';
|
||||||
|
|
||||||
|
// Create a 'pact' between the two applications in the integration we are testing
|
||||||
|
const provider = new PactV3({
|
||||||
|
log: path.resolve(process.cwd(), 'src/pact-logs/pact.log'),
|
||||||
|
dir: path.resolve(process.cwd(), 'src/pacts'),
|
||||||
|
consumer: 'frontend-app-profile',
|
||||||
|
provider: 'edx-platform',
|
||||||
|
});
|
||||||
|
|
||||||
|
const getAccountBody = {
|
||||||
|
"account_privacy":"private",
|
||||||
|
"profile_image":{
|
||||||
|
"has_image":false,
|
||||||
|
"image_url_full":"http://localhost:18000/static/images/profiles/default_500.png",
|
||||||
|
"image_url_large":"http://localhost:18000/static/images/profiles/default_120.png",
|
||||||
|
"image_url_medium":"http://localhost:18000/static/images/profiles/default_50.png",
|
||||||
|
"image_url_small":"http://localhost:18000/static/images/profiles/default_30.png"
|
||||||
|
},"username":"edx",
|
||||||
|
"bio":null,
|
||||||
|
"course_certificates":null,
|
||||||
|
"country":null,
|
||||||
|
"date_joined":"2021-07-30T20:01:46Z",
|
||||||
|
"language_proficiencies":[],
|
||||||
|
"level_of_education":null,
|
||||||
|
"social_links":[],
|
||||||
|
"time_zone":null,
|
||||||
|
"accomplishments_shared":false,
|
||||||
|
"name":"",
|
||||||
|
"email":"edx@example.com",
|
||||||
|
"id":3,
|
||||||
|
"verified_name":null,
|
||||||
|
"extended_profile":[],
|
||||||
|
"gender":null,
|
||||||
|
"state":null,
|
||||||
|
"goals":null,
|
||||||
|
"is_active":true,
|
||||||
|
"last_login":"2023-04-10T18:28:01.771896Z",
|
||||||
|
"mailing_address":null,
|
||||||
|
"requires_parental_consent":true,
|
||||||
|
"secondary_email":null,
|
||||||
|
"secondary_email_enabled":null,
|
||||||
|
"year_of_birth":null,
|
||||||
|
"phone_number":null,
|
||||||
|
"activation_key":null,
|
||||||
|
"pending_name_change":null
|
||||||
|
}
|
||||||
|
|
||||||
|
const EXPECTED_GET_ACCOUNT_BODY = MatchersV3.like(getAccountBody);
|
||||||
|
|
||||||
|
describe('getAccount', () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
initializeMockApp();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an HTTP 200 and user account information', () => {
|
||||||
|
const username = 'edx';
|
||||||
|
provider
|
||||||
|
.given("I have user account information")
|
||||||
|
.uponReceiving(`a request for a user's account information`)
|
||||||
|
.withRequest({
|
||||||
|
method: 'GET',
|
||||||
|
path: `/api/user/v1/accounts/${username}`,
|
||||||
|
headers: { Accept: 'application/json' },
|
||||||
|
})
|
||||||
|
.willRespondWith({
|
||||||
|
status: 200,
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: EXPECTED_GET_ACCOUNT_BODY
|
||||||
|
});
|
||||||
|
|
||||||
|
return provider.executeTest(async (mockserver) => {
|
||||||
|
|
||||||
|
const port = mockserver.port;
|
||||||
|
|
||||||
|
setConfig({
|
||||||
|
...getConfig(),
|
||||||
|
LMS_BASE_URL: `http://localhost:${port}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const response = await getAccountRequest(username);
|
||||||
|
|
||||||
|
expect(response.data).toEqual(getAccountBody);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ensureConfig, getConfig } from '@edx/frontend-platform';
|
import { ensureConfig, getConfig } from '@edx/frontend-platform';
|
||||||
import { getAuthenticatedHttpClient as getHttpClient } from '@edx/frontend-platform/auth';
|
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||||
import { logError } from '@edx/frontend-platform/logging';
|
import { logError } from '@edx/frontend-platform/logging';
|
||||||
import { camelCaseObject, convertKeyNames, snakeCaseObject } from '../utils';
|
import { camelCaseObject, convertKeyNames, snakeCaseObject } from '../utils';
|
||||||
|
|
||||||
@@ -21,17 +21,24 @@ function processAndThrowError(error, errorDataProcessor) {
|
|||||||
|
|
||||||
// GET ACCOUNT
|
// GET ACCOUNT
|
||||||
export async function getAccount(username) {
|
export async function getAccount(username) {
|
||||||
const { data } = await getHttpClient().get(`${getConfig().LMS_BASE_URL}/api/user/v1/accounts/${username}`);
|
const { data } = await getAccountRequest(username);
|
||||||
|
|
||||||
// Process response data
|
// Process response data
|
||||||
return processAccountData(data);
|
return processAccountData(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getAccountRequest(username) {
|
||||||
|
return await getAuthenticatedHttpClient().get(
|
||||||
|
`${getConfig().LMS_BASE_URL}/api/user/v1/accounts/${username}`,
|
||||||
|
{ mockApiId: 'getAccount' }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// PATCH PROFILE
|
// PATCH PROFILE
|
||||||
export async function patchProfile(username, params) {
|
export async function patchProfile(username, params) {
|
||||||
const processedParams = snakeCaseObject(params);
|
const processedParams = snakeCaseObject(params);
|
||||||
|
|
||||||
const { data } = await getHttpClient()
|
const { data } = await getAuthenticatedHttpClient()
|
||||||
.patch(`${getConfig().LMS_BASE_URL}/api/user/v1/accounts/${username}`, processedParams, {
|
.patch(`${getConfig().LMS_BASE_URL}/api/user/v1/accounts/${username}`, processedParams, {
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/merge-patch+json',
|
'Content-Type': 'application/merge-patch+json',
|
||||||
@@ -49,7 +56,7 @@ export async function patchProfile(username, params) {
|
|||||||
|
|
||||||
export async function postProfilePhoto(username, formData) {
|
export async function postProfilePhoto(username, formData) {
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
const { data } = await getHttpClient().post(
|
const { data } = await getAuthenticatedHttpClient().post(
|
||||||
`${getConfig().LMS_BASE_URL}/api/user/v1/accounts/${username}/image`,
|
`${getConfig().LMS_BASE_URL}/api/user/v1/accounts/${username}/image`,
|
||||||
formData,
|
formData,
|
||||||
{
|
{
|
||||||
@@ -74,7 +81,7 @@ export async function postProfilePhoto(username, formData) {
|
|||||||
|
|
||||||
export async function deleteProfilePhoto(username) {
|
export async function deleteProfilePhoto(username) {
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
const { data } = await getHttpClient().delete(`${getConfig().LMS_BASE_URL}/api/user/v1/accounts/${username}/image`);
|
const { data } = await getAuthenticatedHttpClient().delete(`${getConfig().LMS_BASE_URL}/api/user/v1/accounts/${username}/image`);
|
||||||
|
|
||||||
// TODO: Someday in the future the POST photo endpoint
|
// TODO: Someday in the future the POST photo endpoint
|
||||||
// will return the new values. At that time we should
|
// will return the new values. At that time we should
|
||||||
@@ -87,7 +94,7 @@ export async function deleteProfilePhoto(username) {
|
|||||||
|
|
||||||
// GET PREFERENCES
|
// GET PREFERENCES
|
||||||
export async function getPreferences(username) {
|
export async function getPreferences(username) {
|
||||||
const { data } = await getHttpClient().get(`${getConfig().LMS_BASE_URL}/api/user/v1/preferences/${username}`);
|
const { data } = await getAuthenticatedHttpClient().get(`${getConfig().LMS_BASE_URL}/api/user/v1/preferences/${username}`, { mockApiId: 'getPreferences'});
|
||||||
|
|
||||||
return camelCaseObject(data);
|
return camelCaseObject(data);
|
||||||
}
|
}
|
||||||
@@ -107,7 +114,7 @@ export async function patchPreferences(username, params) {
|
|||||||
visibility_time_zone: 'visibility.time_zone',
|
visibility_time_zone: 'visibility.time_zone',
|
||||||
});
|
});
|
||||||
|
|
||||||
await getHttpClient().patch(`${getConfig().LMS_BASE_URL}/api/user/v1/preferences/${username}`, processedParams, {
|
await getAuthenticatedHttpClient().patch(`${getConfig().LMS_BASE_URL}/api/user/v1/preferences/${username}`, processedParams, {
|
||||||
headers: { 'Content-Type': 'application/merge-patch+json' },
|
headers: { 'Content-Type': 'application/merge-patch+json' },
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -140,7 +147,7 @@ function transformCertificateData(data) {
|
|||||||
export async function getCourseCertificates(username) {
|
export async function getCourseCertificates(username) {
|
||||||
const url = `${getConfig().LMS_BASE_URL}/api/certificates/v0/certificates/${username}/`;
|
const url = `${getConfig().LMS_BASE_URL}/api/certificates/v0/certificates/${username}/`;
|
||||||
try {
|
try {
|
||||||
const { data } = await getHttpClient().get(url);
|
const { data } = await getAuthenticatedHttpClient().get(url, { mockApiId: 'getCourseCertificates' });
|
||||||
return transformCertificateData(data);
|
return transformCertificateData(data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logError(e);
|
logError(e);
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ exports[`<SocialLinks /> calls social links with edit mode bio 1`] = `
|
|||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M22 12A10 10 0 116.122 3.91l1.176 1.618A8 8 0 1020 12h2z"
|
d="M22 12A10 10 0 1 1 6.122 3.91l1.176 1.618A8 8 0 1 0 20 12h2Z"
|
||||||
fill="currentColor"
|
fill="currentColor"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|||||||
@@ -1,11 +1,21 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import PropTypes from 'prop-types';
|
||||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||||
import edXLogo from '../images/edX-logo.svg';
|
import edXLogo from '../images/edX-logo.svg';
|
||||||
import messages from './messages';
|
import messages from './messages';
|
||||||
|
|
||||||
const SkillsBuilderHeader = () => {
|
const SkillsBuilderHeader = ({ isMedium }) => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
|
||||||
|
if (isMedium) {
|
||||||
|
return (
|
||||||
|
<div className="d-flex">
|
||||||
|
<h1 className="h3 mb-0 text-white">
|
||||||
|
{formatMessage(messages.skillsBuilderHeaderTitleIsMedium)}
|
||||||
|
</h1>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className="d-flex">
|
<div className="d-flex">
|
||||||
<img src={edXLogo} alt="edx-logo" className="mt-2 h-50" />
|
<img src={edXLogo} alt="edx-logo" className="mt-2 h-50" />
|
||||||
@@ -22,4 +32,8 @@ const SkillsBuilderHeader = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
SkillsBuilderHeader.propTypes = {
|
||||||
|
isMedium: PropTypes.func.isRequired,
|
||||||
|
};
|
||||||
|
|
||||||
export default SkillsBuilderHeader;
|
export default SkillsBuilderHeader;
|
||||||
|
|||||||
@@ -11,6 +11,11 @@ const messages = defineMessages({
|
|||||||
defaultMessage: 'Let edX be your guide',
|
defaultMessage: 'Let edX be your guide',
|
||||||
description: 'Subheading to the Skills Builder title in the header component',
|
description: 'Subheading to the Skills Builder title in the header component',
|
||||||
},
|
},
|
||||||
|
skillsBuilderHeaderTitleIsMedium: {
|
||||||
|
id: 'skills.builder.header.title.is.medium',
|
||||||
|
defaultMessage: 'edX Skills builder',
|
||||||
|
description: 'Title for the Skills Builder feature when screen size is medium or less',
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default messages;
|
export default messages;
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
import React, { useState, useContext } from 'react';
|
import React, { useState, useContext } from 'react';
|
||||||
import {
|
import {
|
||||||
Button, Container, Stepper, ModalDialog,
|
Button, Container, Stepper, ModalDialog, Form, Hyperlink, useMediaQuery, breakpoints,
|
||||||
} from '@edx/paragon';
|
} from '@edx/paragon';
|
||||||
|
import { getConfig } from '@edx/frontend-platform';
|
||||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||||
import { useHistory } from 'react-router';
|
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||||
import {
|
import {
|
||||||
STEP1, STEP2,
|
STEP1, STEP2,
|
||||||
} from '../data/constants';
|
} from '../data/constants';
|
||||||
@@ -18,14 +19,36 @@ import headerImage from '../images/headerImage.png';
|
|||||||
|
|
||||||
const SkillsBuilderModal = () => {
|
const SkillsBuilderModal = () => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
|
const isMedium = useMediaQuery({ maxWidth: breakpoints.medium.maxWidth });
|
||||||
const { state } = useContext(SkillsBuilderContext);
|
const { state } = useContext(SkillsBuilderContext);
|
||||||
const { careerInterests } = state;
|
const { currentGoal, currentJobTitle, careerInterests } = state;
|
||||||
const [currentStep, setCurrentStep] = useState(STEP1);
|
const [currentStep, setCurrentStep] = useState(STEP1);
|
||||||
|
|
||||||
const history = useHistory();
|
const sendActionButtonEvent = (eventSuffix) => {
|
||||||
|
sendTrackEvent(
|
||||||
|
`edx.skills_builder.${eventSuffix}`,
|
||||||
|
{
|
||||||
|
app_name: 'skills_builder',
|
||||||
|
category: 'skills_builder',
|
||||||
|
learner_data: {
|
||||||
|
current_goal: currentGoal,
|
||||||
|
current_job_title: currentJobTitle,
|
||||||
|
career_interests: careerInterests,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const onCloseHandle = () => {
|
const nextStepHandle = () => {
|
||||||
history.goBack();
|
setCurrentStep(STEP2);
|
||||||
|
sendActionButtonEvent('next_step');
|
||||||
|
};
|
||||||
|
const exitButtonHandle = () => {
|
||||||
|
sendActionButtonEvent('exit');
|
||||||
|
};
|
||||||
|
const closeButtonHandle = () => {
|
||||||
|
sendActionButtonEvent('close');
|
||||||
|
window.location.href = getConfig().MARKETING_SITE_SEARCH_URL;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -35,55 +58,52 @@ const SkillsBuilderModal = () => {
|
|||||||
size="fullscreen"
|
size="fullscreen"
|
||||||
className="skills-builder-modal bg-light-200"
|
className="skills-builder-modal bg-light-200"
|
||||||
isOpen
|
isOpen
|
||||||
onClose={onCloseHandle}
|
onClose={closeButtonHandle}
|
||||||
>
|
>
|
||||||
<ModalDialog.Hero>
|
<ModalDialog.Hero className="med-min-height">
|
||||||
<ModalDialog.Hero.Background className="bg-primary-500">
|
<ModalDialog.Hero.Background className="bg-primary-500">
|
||||||
<img src={headerImage} alt="" className="h-100" />
|
{ !isMedium && <img src={headerImage} alt="" className="h-100" /> }
|
||||||
</ModalDialog.Hero.Background>
|
</ModalDialog.Hero.Background>
|
||||||
<ModalDialog.Hero.Content>
|
<ModalDialog.Hero.Content>
|
||||||
<SkillsBuilderHeader />
|
<SkillsBuilderHeader isMedium={isMedium} />
|
||||||
</ModalDialog.Hero.Content>
|
</ModalDialog.Hero.Content>
|
||||||
</ModalDialog.Hero>
|
</ModalDialog.Hero>
|
||||||
|
|
||||||
<Stepper.Header />
|
<Stepper.Header compactWidth="md" />
|
||||||
|
|
||||||
<ModalDialog.Body>
|
<ModalDialog.Body>
|
||||||
<Container size="md" className="p-4.5">
|
<Container size="md" className="p-4.5">
|
||||||
<Stepper.Step eventKey={STEP1} title={formatMessage(messages.selectPreferences)}>
|
<Form>
|
||||||
<SelectPreferences />
|
<Stepper.Step eventKey={STEP1} title={formatMessage(messages.selectPreferences)}>
|
||||||
</Stepper.Step>
|
<SelectPreferences />
|
||||||
|
</Stepper.Step>
|
||||||
|
|
||||||
<Stepper.Step eventKey={STEP2} title={formatMessage(messages.reviewResults)}>
|
<Stepper.Step eventKey={STEP2} title={formatMessage(messages.reviewResults)}>
|
||||||
<ViewResults />
|
<ViewResults />
|
||||||
</Stepper.Step>
|
</Stepper.Step>
|
||||||
|
</Form>
|
||||||
</Container>
|
</Container>
|
||||||
</ModalDialog.Body>
|
</ModalDialog.Body>
|
||||||
|
|
||||||
<ModalDialog.Footer>
|
<ModalDialog.Footer>
|
||||||
<Stepper.ActionRow eventKey={STEP1}>
|
<Stepper.ActionRow eventKey={STEP1}>
|
||||||
<Button variant="outline-primary" onClick={onCloseHandle}>
|
|
||||||
{formatMessage(messages.goBackButton)}
|
|
||||||
</Button>
|
|
||||||
<Stepper.ActionRow.Spacer />
|
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setCurrentStep(STEP2)}
|
onClick={nextStepHandle}
|
||||||
disabled={careerInterests.length === 0}
|
disabled={careerInterests.length === 0}
|
||||||
>
|
>
|
||||||
{formatMessage(messages.nextStepButton)}
|
{formatMessage(messages.nextStepButton)}
|
||||||
</Button>
|
</Button>
|
||||||
</Stepper.ActionRow>
|
</Stepper.ActionRow>
|
||||||
<Stepper.ActionRow eventKey={STEP2}>
|
<Stepper.ActionRow eventKey={STEP2}>
|
||||||
<Button
|
<Button variant="outline-primary" onClick={() => setCurrentStep(STEP1)}>
|
||||||
variant="outline-primary"
|
|
||||||
onClick={() => setCurrentStep(STEP1)}
|
|
||||||
>
|
|
||||||
{formatMessage(messages.goBackButton)}
|
{formatMessage(messages.goBackButton)}
|
||||||
</Button>
|
</Button>
|
||||||
<Stepper.ActionRow.Spacer />
|
<Stepper.ActionRow.Spacer />
|
||||||
<Button onClick={onCloseHandle}>
|
<Hyperlink destination={getConfig().MARKETING_SITE_SEARCH_URL}>
|
||||||
{formatMessage(messages.exitButton)}
|
<Button onClick={exitButtonHandle}>
|
||||||
</Button>
|
{formatMessage(messages.exitButton)}
|
||||||
|
</Button>
|
||||||
|
</Hyperlink>
|
||||||
</Stepper.ActionRow>
|
</Stepper.ActionRow>
|
||||||
</ModalDialog.Footer>
|
</ModalDialog.Footer>
|
||||||
</ModalDialog>
|
</ModalDialog>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
IconButton, Icon,
|
IconButton, Icon,
|
||||||
} from '@edx/paragon';
|
} from '@edx/paragon';
|
||||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||||
|
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||||
import { Close } from '@edx/paragon/icons';
|
import { Close } from '@edx/paragon/icons';
|
||||||
import { SkillsBuilderContext } from '../../skills-builder-context';
|
import { SkillsBuilderContext } from '../../skills-builder-context';
|
||||||
import { removeCareerInterest } from '../../data/actions';
|
import { removeCareerInterest } from '../../data/actions';
|
||||||
@@ -13,6 +14,21 @@ const CareerInterestCard = ({ interest }) => {
|
|||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const { dispatch } = useContext(SkillsBuilderContext);
|
const { dispatch } = useContext(SkillsBuilderContext);
|
||||||
|
|
||||||
|
const handleRemoveCareerInterest = () => {
|
||||||
|
dispatch(removeCareerInterest(interest));
|
||||||
|
|
||||||
|
sendTrackEvent(
|
||||||
|
'edx.skills_builder.career_interest.removed',
|
||||||
|
{
|
||||||
|
app_name: 'skills_builder',
|
||||||
|
category: 'skills_builder',
|
||||||
|
learner_data: {
|
||||||
|
career_interest: interest,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="d-flex justify-content-between align-items-center pb-2 pr-2 pl-4 rounded shadow-sm">
|
<div className="d-flex justify-content-between align-items-center pb-2 pr-2 pl-4 rounded shadow-sm">
|
||||||
<p className="pt-4">
|
<p className="pt-4">
|
||||||
@@ -22,7 +38,7 @@ const CareerInterestCard = ({ interest }) => {
|
|||||||
iconAs={Icon}
|
iconAs={Icon}
|
||||||
src={Close}
|
src={Close}
|
||||||
alt={`${formatMessage(messages.removeCareerInterestButtonAltText)} ${interest}`}
|
alt={`${formatMessage(messages.removeCareerInterestButtonAltText)} ${interest}`}
|
||||||
onClick={() => dispatch(removeCareerInterest(interest))}
|
onClick={handleRemoveCareerInterest}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import React, { useContext } from 'react';
|
import React, { useContext } from 'react';
|
||||||
import { getConfig } from '@edx/frontend-platform';
|
import { getConfig } from '@edx/frontend-platform';
|
||||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||||
|
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||||
import {
|
import {
|
||||||
Stack, Row, Col, Form,
|
Stack, Row, Col, Form,
|
||||||
} from '@edx/paragon';
|
} from '@edx/paragon';
|
||||||
import { InstantSearch } from 'react-instantsearch-hooks-web';
|
import { Configure, InstantSearch } from 'react-instantsearch-hooks-web';
|
||||||
import JobTitleInstantSearch from './JobTitleInstantSearch';
|
import JobTitleInstantSearch from './JobTitleInstantSearch';
|
||||||
import CareerInterestCard from './CareerInterestCard';
|
import CareerInterestCard from './CareerInterestCard';
|
||||||
import { addCareerInterest } from '../../data/actions';
|
import { addCareerInterest } from '../../data/actions';
|
||||||
@@ -20,6 +21,17 @@ const CareerInterestSelect = () => {
|
|||||||
const handleCareerInterestSelect = (value) => {
|
const handleCareerInterestSelect = (value) => {
|
||||||
if (!careerInterests.includes(value) && careerInterests.length < 3) {
|
if (!careerInterests.includes(value) && careerInterests.length < 3) {
|
||||||
dispatch(addCareerInterest(value));
|
dispatch(addCareerInterest(value));
|
||||||
|
|
||||||
|
sendTrackEvent(
|
||||||
|
'edx.skills_builder.career_interest.added',
|
||||||
|
{
|
||||||
|
app_name: 'skills_builder',
|
||||||
|
category: 'skills_builder',
|
||||||
|
learner_data: {
|
||||||
|
career_interest: value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -30,9 +42,11 @@ const CareerInterestSelect = () => {
|
|||||||
{formatMessage(messages.careerInterestPrompt)}
|
{formatMessage(messages.careerInterestPrompt)}
|
||||||
</h4>
|
</h4>
|
||||||
<InstantSearch searchClient={searchClient} indexName={getConfig().ALGOLIA_JOBS_INDEX_NAME}>
|
<InstantSearch searchClient={searchClient} indexName={getConfig().ALGOLIA_JOBS_INDEX_NAME}>
|
||||||
|
<Configure filters="b2c_opt_in:true" />
|
||||||
<JobTitleInstantSearch
|
<JobTitleInstantSearch
|
||||||
onSelected={handleCareerInterestSelect}
|
onSelected={handleCareerInterestSelect}
|
||||||
placeholder={formatMessage(messages.careerInterestInputPlaceholderText)}
|
placeholder={formatMessage(messages.careerInterestInputPlaceholderText)}
|
||||||
|
data-testid="career-interest-select"
|
||||||
/>
|
/>
|
||||||
</InstantSearch>
|
</InstantSearch>
|
||||||
</Form.Label>
|
</Form.Label>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
Form,
|
Form,
|
||||||
} from '@edx/paragon';
|
} from '@edx/paragon';
|
||||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||||
|
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||||
import { setGoal } from '../../data/actions';
|
import { setGoal } from '../../data/actions';
|
||||||
import { SkillsBuilderContext } from '../../skills-builder-context';
|
import { SkillsBuilderContext } from '../../skills-builder-context';
|
||||||
import messages from './messages';
|
import messages from './messages';
|
||||||
@@ -12,6 +13,22 @@ const GoalDropdown = () => {
|
|||||||
const { state, dispatch } = useContext(SkillsBuilderContext);
|
const { state, dispatch } = useContext(SkillsBuilderContext);
|
||||||
const { currentGoal } = state;
|
const { currentGoal } = state;
|
||||||
|
|
||||||
|
const handleGoalSelect = (e) => {
|
||||||
|
const { value } = e.target;
|
||||||
|
dispatch(setGoal(value));
|
||||||
|
|
||||||
|
sendTrackEvent(
|
||||||
|
'edx.skills_builder.goal.select',
|
||||||
|
{
|
||||||
|
app_name: 'skills_builder',
|
||||||
|
category: 'skills_builder',
|
||||||
|
learner_data: {
|
||||||
|
current_goal: value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form.Group>
|
<Form.Group>
|
||||||
<Form.Label>
|
<Form.Label>
|
||||||
@@ -22,10 +39,10 @@ const GoalDropdown = () => {
|
|||||||
<Form.Control
|
<Form.Control
|
||||||
as="select"
|
as="select"
|
||||||
value={currentGoal}
|
value={currentGoal}
|
||||||
onChange={(e) => dispatch(setGoal(e.target.value))}
|
onChange={handleGoalSelect}
|
||||||
data-testid="goal-select-dropdown"
|
data-testid="goal-select-dropdown"
|
||||||
>
|
>
|
||||||
<option value="">{formatMessage(messages.selectLearningGoal)}</option>
|
<option value="" disabled={currentGoal}>{formatMessage(messages.selectLearningGoal)}</option>
|
||||||
<option>{formatMessage(messages.learningGoalStartCareer)}</option>
|
<option>{formatMessage(messages.learningGoalStartCareer)}</option>
|
||||||
<option>{formatMessage(messages.learningGoalAdvanceCareer)}</option>
|
<option>{formatMessage(messages.learningGoalAdvanceCareer)}</option>
|
||||||
<option>{formatMessage(messages.learningGoalChangeCareer)}</option>
|
<option>{formatMessage(messages.learningGoalChangeCareer)}</option>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
Form, Stack,
|
Form, Stack,
|
||||||
} from '@edx/paragon';
|
} from '@edx/paragon';
|
||||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||||
|
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||||
import { InstantSearch } from 'react-instantsearch-hooks-web';
|
import { InstantSearch } from 'react-instantsearch-hooks-web';
|
||||||
import { setCurrentJobTitle } from '../../data/actions';
|
import { setCurrentJobTitle } from '../../data/actions';
|
||||||
import { SkillsBuilderContext } from '../../skills-builder-context';
|
import { SkillsBuilderContext } from '../../skills-builder-context';
|
||||||
@@ -12,16 +13,37 @@ import messages from './messages';
|
|||||||
|
|
||||||
const JobTitleSelect = () => {
|
const JobTitleSelect = () => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const { dispatch, algolia } = useContext(SkillsBuilderContext);
|
const { state, dispatch, algolia } = useContext(SkillsBuilderContext);
|
||||||
const { searchClient } = algolia;
|
const { searchClient } = algolia;
|
||||||
|
const { currentJobTitle } = state;
|
||||||
|
|
||||||
const handleCurrentJobTitleSelect = (value) => {
|
const handleCurrentJobTitleSelect = (value) => {
|
||||||
dispatch(setCurrentJobTitle(value));
|
dispatch(setCurrentJobTitle(value));
|
||||||
|
sendTrackEvent(
|
||||||
|
'edx.skills_builder.current_job.select',
|
||||||
|
{
|
||||||
|
app_name: 'skills_builder',
|
||||||
|
category: 'skills_builder',
|
||||||
|
learner_data: {
|
||||||
|
current_job_title: value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Below implementation sets the job title to "Student" or "Looking for work" — this overwrites any previous selection
|
const handleCheckboxChange = (e) => {
|
||||||
// This will need to be revisited when we decide what to do with this data
|
const { value } = e.target;
|
||||||
const handleCheckboxChange = (e) => dispatch(setCurrentJobTitle(e.target.value));
|
// only setCurrentJobTitle if the user hasn't selected a current job as we don't want to override their selection
|
||||||
|
if (!currentJobTitle) { dispatch(setCurrentJobTitle(value)); }
|
||||||
|
|
||||||
|
sendTrackEvent(
|
||||||
|
`edx.skills_builder.current_job.${value}`,
|
||||||
|
{
|
||||||
|
app_name: 'skills_builder',
|
||||||
|
category: 'skills_builder',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
@@ -33,6 +55,7 @@ const JobTitleSelect = () => {
|
|||||||
<JobTitleInstantSearch
|
<JobTitleInstantSearch
|
||||||
onSelected={handleCurrentJobTitleSelect}
|
onSelected={handleCurrentJobTitleSelect}
|
||||||
placeholder={formatMessage(messages.jobTitleInputPlaceholderText)}
|
placeholder={formatMessage(messages.jobTitleInputPlaceholderText)}
|
||||||
|
data-testid="job-title-select"
|
||||||
/>
|
/>
|
||||||
</InstantSearch>
|
</InstantSearch>
|
||||||
</Form.Label>
|
</Form.Label>
|
||||||
@@ -41,10 +64,10 @@ const JobTitleSelect = () => {
|
|||||||
name="other-occupations"
|
name="other-occupations"
|
||||||
onChange={handleCheckboxChange}
|
onChange={handleCheckboxChange}
|
||||||
>
|
>
|
||||||
<Form.Checkbox value="Student">
|
<Form.Checkbox value="student">
|
||||||
{formatMessage(messages.studentCheckboxPrompt)}
|
{formatMessage(messages.studentCheckboxPrompt)}
|
||||||
</Form.Checkbox>
|
</Form.Checkbox>
|
||||||
<Form.Checkbox value="Looking for work">
|
<Form.Checkbox value="looking_for_work">
|
||||||
{formatMessage(messages.currentlyLookingCheckboxPrompt)}
|
{formatMessage(messages.currentlyLookingCheckboxPrompt)}
|
||||||
</Form.Checkbox>
|
</Form.Checkbox>
|
||||||
</Form.CheckboxSet>
|
</Form.CheckboxSet>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const SelectPreferences = () => {
|
|||||||
<JobTitleSelect />
|
<JobTitleSelect />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{currentJobTitle && (
|
{currentGoal && currentJobTitle && (
|
||||||
<CareerInterestSelect />
|
<CareerInterestSelect />
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|||||||
@@ -2,8 +2,13 @@ import {
|
|||||||
screen, render, cleanup, fireEvent,
|
screen, render, cleanup, fireEvent,
|
||||||
} from '@testing-library/react';
|
} from '@testing-library/react';
|
||||||
import { mergeConfig } from '@edx/frontend-platform';
|
import { mergeConfig } from '@edx/frontend-platform';
|
||||||
|
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||||
import { SkillsBuilderWrapperWithContext, dispatchMock, contextValue } from '../../../test/setupSkillsBuilder';
|
import { SkillsBuilderWrapperWithContext, dispatchMock, contextValue } from '../../../test/setupSkillsBuilder';
|
||||||
|
|
||||||
|
jest.mock('@edx/frontend-platform/analytics', () => ({
|
||||||
|
sendTrackEvent: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('select-preferences', () => {
|
describe('select-preferences', () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
mergeConfig({
|
mergeConfig({
|
||||||
@@ -29,8 +34,13 @@ describe('select-preferences', () => {
|
|||||||
payload: 'I want to advance my career',
|
payload: 'I want to advance my career',
|
||||||
type: 'SET_GOAL',
|
type: 'SET_GOAL',
|
||||||
};
|
};
|
||||||
|
const expectedStudent = {
|
||||||
|
payload: 'student',
|
||||||
|
type: 'SET_CURRENT_JOB_TITLE',
|
||||||
|
};
|
||||||
|
|
||||||
const expectedJobTitle = {
|
const expectedJobTitle = {
|
||||||
payload: 'Student',
|
payload: 'Prospector',
|
||||||
type: 'SET_CURRENT_JOB_TITLE',
|
type: 'SET_CURRENT_JOB_TITLE',
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -40,9 +50,41 @@ describe('select-preferences', () => {
|
|||||||
const checkbox = screen.getByRole('checkbox', { name: 'I\'m a student' });
|
const checkbox = screen.getByRole('checkbox', { name: 'I\'m a student' });
|
||||||
fireEvent.click(checkbox);
|
fireEvent.click(checkbox);
|
||||||
|
|
||||||
|
const jobTitleInput = screen.getByTestId('job-title-select');
|
||||||
|
fireEvent.change(jobTitleInput, { target: { value: 'Prospector' } });
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: 'Prospector' }));
|
||||||
|
|
||||||
expect(screen.getByText('Next, search and select your current job title')).toBeTruthy();
|
expect(screen.getByText('Next, search and select your current job title')).toBeTruthy();
|
||||||
expect(dispatchMock).toHaveBeenCalledWith(expectedGoal);
|
expect(dispatchMock).toHaveBeenCalledWith(expectedGoal);
|
||||||
|
expect(dispatchMock).toHaveBeenCalledWith(expectedStudent);
|
||||||
expect(dispatchMock).toHaveBeenCalledWith(expectedJobTitle);
|
expect(dispatchMock).toHaveBeenCalledWith(expectedJobTitle);
|
||||||
|
expect(sendTrackEvent).toHaveBeenCalledWith(
|
||||||
|
'edx.skills_builder.goal.select',
|
||||||
|
{
|
||||||
|
app_name: 'skills_builder',
|
||||||
|
category: 'skills_builder',
|
||||||
|
learner_data: {
|
||||||
|
current_goal: 'I want to advance my career',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(sendTrackEvent).toHaveBeenCalledWith(
|
||||||
|
'edx.skills_builder.current_job.student',
|
||||||
|
{
|
||||||
|
app_name: 'skills_builder',
|
||||||
|
category: 'skills_builder',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(sendTrackEvent).toHaveBeenCalledWith(
|
||||||
|
'edx.skills_builder.current_job.select',
|
||||||
|
{
|
||||||
|
app_name: 'skills_builder',
|
||||||
|
category: 'skills_builder',
|
||||||
|
learner_data: {
|
||||||
|
current_job_title: 'Prospector',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render the third prompt if a current job title is selected', () => {
|
it('should render the third prompt if a current job title is selected', () => {
|
||||||
@@ -70,14 +112,33 @@ describe('select-preferences', () => {
|
|||||||
...contextValue.state,
|
...contextValue.state,
|
||||||
currentGoal: 'I want to start my career',
|
currentGoal: 'I want to start my career',
|
||||||
currentJobTitle: 'Goblin Lackey',
|
currentJobTitle: 'Goblin Lackey',
|
||||||
careerInterests: ['Prospector', 'Mirror Breaker', 'Bombardment'],
|
careerInterests: ['Prospector'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(screen.getByText('Prospector')).toBeTruthy();
|
expect(screen.getByText('Prospector')).toBeTruthy();
|
||||||
expect(screen.getByText('Mirror Breaker')).toBeTruthy();
|
|
||||||
expect(screen.getByText('Bombardment')).toBeTruthy();
|
const careerInterestInput = screen.getByTestId('career-interest-select');
|
||||||
|
fireEvent.change(careerInterestInput, { target: { value: 'Mirror Breaker' } });
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: 'Mirror Breaker' }));
|
||||||
|
|
||||||
|
expect(sendTrackEvent).toHaveBeenCalledWith(
|
||||||
|
'edx.skills_builder.career_interest.added',
|
||||||
|
{
|
||||||
|
app_name: 'skills_builder',
|
||||||
|
category: 'skills_builder',
|
||||||
|
learner_data: {
|
||||||
|
career_interest: 'Mirror Breaker',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
expect(dispatchMock).toHaveBeenCalledWith(
|
||||||
|
{
|
||||||
|
payload: 'Mirror Breaker',
|
||||||
|
type: 'ADD_CAREER_INTEREST',
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -104,6 +165,16 @@ describe('select-preferences', () => {
|
|||||||
|
|
||||||
fireEvent.click(screen.getByLabelText('Remove career interest: Prospector'));
|
fireEvent.click(screen.getByLabelText('Remove career interest: Prospector'));
|
||||||
expect(dispatchMock).toHaveBeenCalledWith(expected);
|
expect(dispatchMock).toHaveBeenCalledWith(expected);
|
||||||
|
expect(sendTrackEvent).toHaveBeenCalledWith(
|
||||||
|
'edx.skills_builder.career_interest.removed',
|
||||||
|
{
|
||||||
|
app_name: 'skills_builder',
|
||||||
|
category: 'skills_builder',
|
||||||
|
learner_data: {
|
||||||
|
career_interest: 'Prospector',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,5 +1,16 @@
|
|||||||
.skills-builder-modal {
|
.skills-builder-modal {
|
||||||
button[aria-label="Close"][type="button"]{
|
button[aria-label="Close"][type="button"]{
|
||||||
color: #ffffff;
|
color: $white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$breakpoint-medium: 992px;
|
||||||
|
@media (max-width: $breakpoint-medium) {
|
||||||
|
.med-min-height {
|
||||||
|
min-height: map-get($spacers, 6);
|
||||||
|
}
|
||||||
|
.pgn__modal-close-container {
|
||||||
|
top: 1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,18 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { CardCarousel } from '@edx/paragon';
|
import { CardCarousel } from '@edx/paragon';
|
||||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||||
|
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||||
import RecommendationCard from './RecommendationCard';
|
import RecommendationCard from './RecommendationCard';
|
||||||
import messages from './messages';
|
import messages from './messages';
|
||||||
|
|
||||||
const CarouselStack = ({ selectedRecommendations }) => {
|
const CarouselStack = ({ selectedRecommendations }) => {
|
||||||
const { formatMessage } = useIntl();
|
const { formatMessage } = useIntl();
|
||||||
const { name: jobName, recommendations } = selectedRecommendations;
|
const { id: jobId, name: jobName, recommendations } = selectedRecommendations;
|
||||||
const productTypeNames = Object.keys(recommendations);
|
const productTypeNames = Object.keys(recommendations);
|
||||||
|
const courseKeys = recommendations.course?.map(rec => ({
|
||||||
|
title: rec.title,
|
||||||
|
courserun_key: rec.active_run_key,
|
||||||
|
}));
|
||||||
|
|
||||||
const normalizeProductTypeName = (productType) => {
|
const normalizeProductTypeName = (productType) => {
|
||||||
// If the productType is more than one word (i.e. boot_camp)
|
// If the productType is more than one word (i.e. boot_camp)
|
||||||
@@ -35,6 +40,24 @@ const CarouselStack = ({ selectedRecommendations }) => {
|
|||||||
</h3>
|
</h3>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleCourseCardClick = (courseKey, productType) => {
|
||||||
|
sendTrackEvent(
|
||||||
|
'edx.skills_builder.recommendation.click',
|
||||||
|
{
|
||||||
|
app_name: 'skills_builder',
|
||||||
|
category: 'skills_builder',
|
||||||
|
page: 'skills_builder',
|
||||||
|
courserun_key: courseKey,
|
||||||
|
product_type: productType,
|
||||||
|
selected_recommendations: {
|
||||||
|
job_id: jobId,
|
||||||
|
job_name: jobName,
|
||||||
|
courserun_keys: courseKeys,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
productTypeNames.map(productType => (
|
productTypeNames.map(productType => (
|
||||||
<CardCarousel
|
<CardCarousel
|
||||||
@@ -43,7 +66,12 @@ const CarouselStack = ({ selectedRecommendations }) => {
|
|||||||
title={renderCarouselTitle(productType)}
|
title={renderCarouselTitle(productType)}
|
||||||
>
|
>
|
||||||
{recommendations[productType].map(rec => (
|
{recommendations[productType].map(rec => (
|
||||||
<RecommendationCard rec={rec} key={rec.uuid} />
|
<RecommendationCard
|
||||||
|
key={rec.uuid}
|
||||||
|
handleCourseCardClick={handleCourseCardClick}
|
||||||
|
rec={rec}
|
||||||
|
productType={productType}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</CardCarousel>
|
</CardCarousel>
|
||||||
)));
|
)));
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Card, Chip, Hyperlink } from '@edx/paragon';
|
import { Card, Chip, Hyperlink } from '@edx/paragon';
|
||||||
import classNames from 'classnames';
|
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import cardImageCapFallbackSrc from '../../images/card-imagecap-fallback.png';
|
import cardImageCapFallbackSrc from '../../images/card-imagecap-fallback.png';
|
||||||
|
|
||||||
const RecommendationCard = ({ rec }) => {
|
const RecommendationCard = ({ rec, productType, handleCourseCardClick }) => {
|
||||||
const {
|
const {
|
||||||
card_image_url: cardImageUrl,
|
card_image_url: cardImageUrl,
|
||||||
marketing_url: marketingUrl,
|
marketing_url: marketingUrl,
|
||||||
|
active_run_key: courseKey,
|
||||||
owners,
|
owners,
|
||||||
partner,
|
partner,
|
||||||
title,
|
title,
|
||||||
@@ -17,12 +17,15 @@ const RecommendationCard = ({ rec }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Hyperlink destination={marketingUrl} target="_blank" showLaunchIcon={false}>
|
<Hyperlink destination={marketingUrl} target="_blank" showLaunchIcon={false}>
|
||||||
<Card className={classNames('carousel-card')}>
|
<Card
|
||||||
|
className="carousel-card"
|
||||||
|
onClick={() => handleCourseCardClick(courseKey, productType)}
|
||||||
|
>
|
||||||
<Card.ImageCap
|
<Card.ImageCap
|
||||||
src={cardImageUrl}
|
src={cardImageUrl}
|
||||||
logoSrc={logoImageUrl}
|
logoSrc={logoImageUrl}
|
||||||
fallackSrc={cardImageCapFallbackSrc}
|
fallbackSrc={cardImageCapFallbackSrc}
|
||||||
fallBackLogo={cardImageCapFallbackSrc}
|
fallbackLogoSrc={cardImageCapFallbackSrc}
|
||||||
/>
|
/>
|
||||||
<Card.Header title={title} />
|
<Card.Header title={title} />
|
||||||
<Card.Section>
|
<Card.Section>
|
||||||
@@ -48,7 +51,10 @@ RecommendationCard.propTypes = {
|
|||||||
key: PropTypes.string,
|
key: PropTypes.string,
|
||||||
logoImageUrl: PropTypes.string,
|
logoImageUrl: PropTypes.string,
|
||||||
})),
|
})),
|
||||||
|
active_run_key: PropTypes.string.isRequired,
|
||||||
}).isRequired,
|
}).isRequired,
|
||||||
|
productType: PropTypes.string.isRequired,
|
||||||
|
handleCourseCardClick: PropTypes.func.isRequired,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default RecommendationCard;
|
export default RecommendationCard;
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
import React, { useContext, useEffect, useState } from 'react';
|
import React, {
|
||||||
|
useContext, useEffect, useState,
|
||||||
|
} from 'react';
|
||||||
import {
|
import {
|
||||||
Stack, Row, Alert, Spinner,
|
Stack, Row, Alert, Spinner,
|
||||||
} from '@edx/paragon';
|
} from '@edx/paragon';
|
||||||
|
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||||
import { CheckCircle, ErrorOutline } from '@edx/paragon/icons';
|
import { CheckCircle, ErrorOutline } from '@edx/paragon/icons';
|
||||||
import { SkillsBuilderContext } from '../../skills-builder-context';
|
import { SkillsBuilderContext } from '../../skills-builder-context';
|
||||||
@@ -55,9 +58,24 @@ const ViewResults = () => {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
setJobSkillsList(jobInfo);
|
setJobSkillsList(jobInfo);
|
||||||
setSelectedJobTitle(jobInfo[0].name);
|
setSelectedJobTitle(results[0].name);
|
||||||
setProductRecommendations(results);
|
setProductRecommendations(results);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
sendTrackEvent('edx.skills_builder.recommendation.shown', {
|
||||||
|
app_name: 'skills_builder',
|
||||||
|
category: 'skills_builder',
|
||||||
|
page: 'skills_builder',
|
||||||
|
selected_recommendations: {
|
||||||
|
job_id: results[0].id,
|
||||||
|
job_name: results[0].name,
|
||||||
|
/* We extract the title and course key into an array of objects */
|
||||||
|
courserun_keys: results[0].recommendations.course?.map(rec => ({
|
||||||
|
title: rec.title,
|
||||||
|
courserun_key: rec.active_run_key,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
is_default: true,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
getRecommendations()
|
getRecommendations()
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
@@ -70,6 +88,35 @@ const ViewResults = () => {
|
|||||||
setSelectedRecommendations(productRecommendations.find(rec => rec.name === selectedJobTitle));
|
setSelectedRecommendations(productRecommendations.find(rec => rec.name === selectedJobTitle));
|
||||||
}, [productRecommendations, selectedJobTitle]);
|
}, [productRecommendations, selectedJobTitle]);
|
||||||
|
|
||||||
|
const handleJobTitleChange = (e) => {
|
||||||
|
const { value } = e.target;
|
||||||
|
setSelectedJobTitle(value);
|
||||||
|
const currentSelection = productRecommendations.find(rec => rec.name === value);
|
||||||
|
const { id: jobId, name: jobName, recommendations } = currentSelection;
|
||||||
|
const courseKeys = recommendations.course?.map(rec => ({
|
||||||
|
title: rec.title,
|
||||||
|
courserun_key: rec.active_run_key,
|
||||||
|
}));
|
||||||
|
/*
|
||||||
|
The is_default value will be set to false for any selections made by the user.
|
||||||
|
This code is intentionally duplicated from the event that fires in the useEffect for fetching recommendations.
|
||||||
|
This proved less clunky than refactoring to make things DRY as we have to ensure the first call fires only once.
|
||||||
|
The previous implementation wrapped the event in an additional useEffect that was looping unnecessarily.
|
||||||
|
We have plans to refactor all of the event code as part of APER-2392, where we will revisit this approach.
|
||||||
|
*/
|
||||||
|
sendTrackEvent('edx.skills_builder.recommendation.shown', {
|
||||||
|
app_name: 'skills_builder',
|
||||||
|
category: 'skills_builder',
|
||||||
|
page: 'skills_builder',
|
||||||
|
selected_recommendations: {
|
||||||
|
job_id: jobId,
|
||||||
|
job_name: jobName,
|
||||||
|
courserun_keys: courseKeys,
|
||||||
|
},
|
||||||
|
is_default: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
if (fetchError) {
|
if (fetchError) {
|
||||||
return (
|
return (
|
||||||
<Alert
|
<Alert
|
||||||
@@ -106,7 +153,7 @@ const ViewResults = () => {
|
|||||||
<RelatedSkillsSelectableBoxSet
|
<RelatedSkillsSelectableBoxSet
|
||||||
jobSkillsList={jobSkillsList}
|
jobSkillsList={jobSkillsList}
|
||||||
selectedJobTitle={selectedJobTitle}
|
selectedJobTitle={selectedJobTitle}
|
||||||
onChange={(e) => setSelectedJobTitle(e.target.value)}
|
onChange={handleJobTitleChange}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<CarouselStack selectedRecommendations={selectedRecommendations} />
|
<CarouselStack selectedRecommendations={selectedRecommendations} />
|
||||||
|
|||||||
@@ -2,9 +2,14 @@ import {
|
|||||||
screen, render, cleanup, fireEvent, act,
|
screen, render, cleanup, fireEvent, act,
|
||||||
} from '@testing-library/react';
|
} from '@testing-library/react';
|
||||||
import { mergeConfig } from '@edx/frontend-platform';
|
import { mergeConfig } from '@edx/frontend-platform';
|
||||||
|
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||||
import { SkillsBuilderWrapperWithContext, contextValue } from '../../../test/setupSkillsBuilder';
|
import { SkillsBuilderWrapperWithContext, contextValue } from '../../../test/setupSkillsBuilder';
|
||||||
import { getProductRecommendations } from '../../../utils/search';
|
import { getProductRecommendations } from '../../../utils/search';
|
||||||
|
|
||||||
|
jest.mock('@edx/frontend-platform/analytics', () => ({
|
||||||
|
sendTrackEvent: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
const renderSkillsBuilderWrapper = (
|
const renderSkillsBuilderWrapper = (
|
||||||
value = {
|
value = {
|
||||||
...contextValue,
|
...contextValue,
|
||||||
@@ -37,22 +42,117 @@ describe('view-results', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should render a <JobSillsSelectableBox> for each career interest the learner has submitted', async () => {
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render a <JobSillsSelectableBox> for each career interest the learner has submitted', () => {
|
||||||
expect(screen.getByText('Prospector')).toBeTruthy();
|
expect(screen.getByText('Prospector')).toBeTruthy();
|
||||||
expect(screen.getByText('Mirror Breaker')).toBeTruthy();
|
expect(screen.getByText('Mirror Breaker')).toBeTruthy();
|
||||||
|
|
||||||
const chipComponents = document.querySelectorAll('.pgn__chip');
|
const chipComponents = document.querySelectorAll('.pgn__chip');
|
||||||
expect(chipComponents[0].textContent).toEqual('finding shiny things');
|
expect(chipComponents[0].textContent).toEqual('finding shiny things');
|
||||||
expect(chipComponents[1].textContent).toEqual('mining');
|
expect(chipComponents[1].textContent).toEqual('mining');
|
||||||
|
expect(sendTrackEvent).toHaveBeenCalledWith(
|
||||||
|
'edx.skills_builder.recommendation.shown',
|
||||||
|
{
|
||||||
|
app_name: 'skills_builder',
|
||||||
|
category: 'skills_builder',
|
||||||
|
page: 'skills_builder',
|
||||||
|
selected_recommendations: {
|
||||||
|
job_id: 0,
|
||||||
|
job_name: 'Prospector',
|
||||||
|
courserun_keys: [
|
||||||
|
{
|
||||||
|
title: 'Mining with the Mons',
|
||||||
|
courserun_key: 'MONS101',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'The Art of Warren Upkeep',
|
||||||
|
courserun_key: 'WAR101',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
is_default: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
// called once when "Next Step" button is clicked and then again for above event
|
||||||
|
expect(sendTrackEvent).toHaveBeenCalledTimes(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders a carousel of <Card> components', async () => {
|
it('renders a carousel of <Card> components', () => {
|
||||||
expect(screen.getByText('Course recommendations for Prospector')).toBeTruthy();
|
expect(screen.getByText('Course recommendations for Prospector')).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('changes the recommendations based on the selected job title', () => {
|
it('changes the recommendations based on the selected job title', () => {
|
||||||
fireEvent.click(screen.getByRole('radio', { name: 'Mirror Breaker' }));
|
fireEvent.click(screen.getByRole('radio', { name: 'Mirror Breaker' }));
|
||||||
expect(screen.getByText('Course recommendations for Mirror Breaker')).toBeTruthy();
|
expect(screen.getByText('Course recommendations for Mirror Breaker')).toBeTruthy();
|
||||||
|
expect(sendTrackEvent).toHaveBeenCalledWith(
|
||||||
|
'edx.skills_builder.recommendation.shown',
|
||||||
|
{
|
||||||
|
app_name: 'skills_builder',
|
||||||
|
category: 'skills_builder',
|
||||||
|
page: 'skills_builder',
|
||||||
|
selected_recommendations: {
|
||||||
|
job_id: 1,
|
||||||
|
job_name: 'Mirror Breaker',
|
||||||
|
courserun_keys: [
|
||||||
|
{
|
||||||
|
title: 'Mining with the Mons',
|
||||||
|
courserun_key: 'MONS101',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'The Art of Warren Upkeep',
|
||||||
|
courserun_key: 'WAR101',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
is_default: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sends an event when the "Next Step" button is clicked', () => {
|
||||||
|
expect(sendTrackEvent).toHaveBeenCalledWith(
|
||||||
|
'edx.skills_builder.next_step',
|
||||||
|
{
|
||||||
|
app_name: 'skills_builder',
|
||||||
|
category: 'skills_builder',
|
||||||
|
learner_data: {
|
||||||
|
current_goal: 'I want to start my career',
|
||||||
|
current_job_title: 'Goblin Lackey',
|
||||||
|
career_interests: ['Prospector', 'Mirror Breaker', 'Bombardment'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('fires an event when a product recommendation is clicked', () => {
|
||||||
|
fireEvent.click(screen.getByText('Mining with the Mons'));
|
||||||
|
expect(sendTrackEvent).toHaveBeenCalledWith(
|
||||||
|
'edx.skills_builder.recommendation.click',
|
||||||
|
{
|
||||||
|
app_name: 'skills_builder',
|
||||||
|
category: 'skills_builder',
|
||||||
|
page: 'skills_builder',
|
||||||
|
courserun_key: 'MONS101',
|
||||||
|
product_type: 'course',
|
||||||
|
selected_recommendations: {
|
||||||
|
job_id: 0,
|
||||||
|
job_name: 'Prospector',
|
||||||
|
courserun_keys: [
|
||||||
|
{
|
||||||
|
title: 'Mining with the Mons',
|
||||||
|
courserun_key: 'MONS101',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'The Art of Warren Upkeep',
|
||||||
|
courserun_key: 'WAR101',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ export const mockData = {
|
|||||||
hits: [
|
hits: [
|
||||||
{
|
{
|
||||||
id: 0,
|
id: 0,
|
||||||
name: 'Text File Engineer'
|
name: 'Prospector'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
name: 'Screen Viewer'
|
name: 'Mirror Breaker'
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
searchJobs: [
|
searchJobs: [
|
||||||
@@ -44,6 +44,7 @@ export const mockData = {
|
|||||||
partner: ['edx'],
|
partner: ['edx'],
|
||||||
card_image_url: 'https://thisIsAUrl.ForAnImage.01.jpeg',
|
card_image_url: 'https://thisIsAUrl.ForAnImage.01.jpeg',
|
||||||
marketing_url: 'https://thisIsAUrl.ForTheRecommendedContent.01.com',
|
marketing_url: 'https://thisIsAUrl.ForTheRecommendedContent.01.com',
|
||||||
|
active_run_key: 'MONS101',
|
||||||
owners: [
|
owners: [
|
||||||
{
|
{
|
||||||
logoImageUrl: 'https://thisIsAUrl.ForALogoImage.01.jpeg',
|
logoImageUrl: 'https://thisIsAUrl.ForALogoImage.01.jpeg',
|
||||||
@@ -56,6 +57,7 @@ export const mockData = {
|
|||||||
partner: ['edx'],
|
partner: ['edx'],
|
||||||
card_image_url: 'https://thisIsAUrl.ForAnImage.02.jpeg',
|
card_image_url: 'https://thisIsAUrl.ForAnImage.02.jpeg',
|
||||||
marketing_url: 'https://thisIsAUrl.ForTheRecommendedContent.02.com',
|
marketing_url: 'https://thisIsAUrl.ForTheRecommendedContent.02.com',
|
||||||
|
active_run_key: 'WAR101',
|
||||||
owners: [
|
owners: [
|
||||||
{
|
{
|
||||||
logoImageUrl: 'https://thisIsAUrl.ForALogoImage.02.jpeg',
|
logoImageUrl: 'https://thisIsAUrl.ForALogoImage.02.jpeg',
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ jest.mock('@edx/frontend-platform/logging');
|
|||||||
jest.mock('react-instantsearch-hooks-web', () => ({
|
jest.mock('react-instantsearch-hooks-web', () => ({
|
||||||
// eslint-disable-next-line react/prop-types
|
// eslint-disable-next-line react/prop-types
|
||||||
InstantSearch: ({ children }) => (<div>{children}</div>),
|
InstantSearch: ({ children }) => (<div>{children}</div>),
|
||||||
|
Configure: jest.fn(() => (null)),
|
||||||
useSearchBox: jest.fn(() => ({ refine: jest.fn() })),
|
useSearchBox: jest.fn(() => ({ refine: jest.fn() })),
|
||||||
useHits: jest.fn(() => ({ hits: mockData.hits })),
|
useHits: jest.fn(() => ({ hits: mockData.hits })),
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ export const getProductRecommendations = async (productIndex, productType, skill
|
|||||||
const formattedSkillNames = formatFacetFilterData('skills.skill', skills);
|
const formattedSkillNames = formatFacetFilterData('skills.skill', skills);
|
||||||
try {
|
try {
|
||||||
const { hits } = await productIndex.search('', {
|
const { hits } = await productIndex.search('', {
|
||||||
filters: `product: "${productType}"`,
|
filters: `product: "${productType}" AND language: "English"`,
|
||||||
facetFilters: [
|
facetFilters: [
|
||||||
formattedSkillNames,
|
formattedSkillNames,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ describe('Algolias utility function', () => {
|
|||||||
|
|
||||||
it('getProductRecommendations() queries Algolia with the expected search parameters', async () => {
|
it('getProductRecommendations() queries Algolia with the expected search parameters', async () => {
|
||||||
const expectedSearchParameters = {
|
const expectedSearchParameters = {
|
||||||
filters: 'product: "Course"',
|
filters: 'product: "Course" AND language: "English"',
|
||||||
facetFilters: [
|
facetFilters: [
|
||||||
['skills.skill:Sword Lobbing'],
|
['skills.skill:Sword Lobbing'],
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user