Compare commits
290 Commits
aperture/P
...
release/te
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e7cddb60f7 | ||
|
|
9b6e928260 | ||
|
|
0e676ffff3 | ||
|
|
9516ee0e92 | ||
|
|
29fd7176c8 | ||
|
|
16ddd7abba | ||
|
|
577ef6ab0b | ||
|
|
8268fa4eab | ||
|
|
5665f8a0d6 | ||
|
|
4b7a3207e0 | ||
|
|
3db0289aab | ||
|
|
85d3eca9e4 | ||
|
|
f7fd2959ac | ||
|
|
8652206aa4 | ||
|
|
7a5e03967d | ||
|
|
da19dfaadc | ||
|
|
60d960276d | ||
|
|
c8a6f9fbd8 | ||
|
|
2ef5a7baff | ||
|
|
d59c641b3d | ||
|
|
4ea80a2a09 | ||
|
|
45fe50f7f7 | ||
|
|
933a177c78 | ||
|
|
14fff570a4 | ||
|
|
3d0f3806e1 | ||
|
|
b597e2cc14 | ||
|
|
22dc85470a | ||
|
|
9a26dc7088 | ||
|
|
770a248d8c | ||
|
|
70cb1803b4 | ||
|
|
e9aa787ade | ||
|
|
01e2f1af79 | ||
|
|
1e67d51394 | ||
|
|
95936419c2 | ||
|
|
00ada93994 | ||
|
|
c57a924cc3 | ||
|
|
dc2f03dfad | ||
|
|
5c7a521705 | ||
|
|
074374b2af | ||
|
|
0a3aad38dc | ||
|
|
1730b5a2c4 | ||
|
|
12269c2c8e | ||
|
|
630dbefb7e | ||
|
|
00c8697c59 | ||
|
|
8df3d06598 | ||
|
|
f6ed6ee1f5 | ||
|
|
7e8d22ec6a | ||
|
|
d0595e679d | ||
|
|
4c7e713b6e | ||
|
|
bc0683281f | ||
|
|
b954345b38 | ||
|
|
ca4f78bd1c | ||
|
|
3dc8b156fe | ||
|
|
a818cd4dc4 | ||
|
|
2e9dcd165e | ||
|
|
53a52b8f06 | ||
|
|
c66facee92 | ||
|
|
3270e27c94 | ||
|
|
01cd125d4f | ||
|
|
5fcef4edf4 | ||
|
|
d33c79a525 | ||
|
|
d36c61d44e | ||
|
|
aed6081d37 | ||
|
|
66a4eef910 | ||
|
|
6da6fedc57 | ||
|
|
a7e095f3bf | ||
|
|
9a393d8e43 | ||
|
|
1a5c4f2404 | ||
|
|
13aaca03fd | ||
|
|
422aff1915 | ||
|
|
cac4e42364 | ||
|
|
c6d39884c8 | ||
|
|
4bb3678d4c | ||
|
|
5c2951de40 | ||
|
|
35d9ae8fec | ||
|
|
ff32632411 | ||
|
|
2c97964f0b | ||
|
|
fa748719ae | ||
|
|
1fb7b96c40 | ||
|
|
e13d04a5ea | ||
|
|
e3f5129746 | ||
|
|
24b054f55f | ||
|
|
8498d9f04f | ||
|
|
0fa58d8112 | ||
|
|
312b114ef7 | ||
|
|
a05b29e406 | ||
|
|
6e3e67467a | ||
|
|
09b5c8b72d | ||
|
|
5e71737240 | ||
|
|
eb726e80ab | ||
|
|
4f1272b01c | ||
|
|
30e3fcdcba | ||
|
|
b13fc58bad | ||
|
|
31b31ba345 | ||
|
|
f7449bfdcc | ||
|
|
cb0f05955f | ||
|
|
f1586d260a | ||
|
|
b268c43978 | ||
|
|
ed58a41042 | ||
|
|
7e4ba48de8 | ||
|
|
e9e16cc595 | ||
|
|
88d786b49f | ||
|
|
a24fea54ec | ||
|
|
8980b22fff | ||
|
|
389d923a08 | ||
|
|
714851c329 | ||
|
|
b27300e63d | ||
|
|
0869a82a31 | ||
|
|
507525262c | ||
|
|
a69170a4d2 | ||
|
|
26203bb744 | ||
|
|
21fb4702b4 | ||
|
|
aa8d1f9473 | ||
|
|
b6e9df9778 | ||
|
|
a7ed0cd62a | ||
|
|
e61fada0b2 | ||
|
|
3b0b1be197 | ||
|
|
616847c6b5 | ||
|
|
95b41131fc | ||
|
|
59a3150706 | ||
|
|
774b67b208 | ||
|
|
9a855cd30e | ||
|
|
9c6647aeae | ||
|
|
ecdec9083a | ||
|
|
1c0746c907 | ||
|
|
e7f8a5d1ff | ||
|
|
d927b43c01 | ||
|
|
1a36d69bd8 | ||
|
|
db90ce5975 | ||
|
|
b22d411481 | ||
|
|
d7fff65162 | ||
|
|
cf5d69c5ba | ||
|
|
53d26746c1 | ||
|
|
048433a135 | ||
|
|
31136b0523 | ||
|
|
caaf8547dc | ||
|
|
e92332859a | ||
|
|
55529b0b74 | ||
|
|
7682971b63 | ||
|
|
6aaf615c63 | ||
|
|
3512fc56c9 | ||
|
|
fda2ef1977 | ||
|
|
3784c2a6df | ||
|
|
04d74389ed | ||
|
|
1d338d0127 | ||
|
|
0fd6e34a8e | ||
|
|
9d89c79fb2 | ||
|
|
bfc31696ee | ||
|
|
8ebdc1030f | ||
|
|
8c3d706071 | ||
|
|
ee8912530d | ||
|
|
db91e0f1a4 | ||
|
|
5f2d0c089e | ||
|
|
75be372ec9 | ||
|
|
9ef81f7485 | ||
|
|
6a32790f28 | ||
|
|
9f5f7f9a64 | ||
|
|
06f6e3537a | ||
|
|
44f0295d0d | ||
|
|
19be2e93e2 | ||
|
|
5090a62594 | ||
|
|
dc2bbee481 | ||
|
|
8dc37f9bba | ||
|
|
14d93d2f6c | ||
|
|
768351fc15 | ||
|
|
c3dc8b7330 | ||
|
|
9fd2d3f75a | ||
|
|
760fa0c6a2 | ||
|
|
cf6a62266c | ||
|
|
f16fd36a24 | ||
|
|
c30e1b3f17 | ||
|
|
9985afde2d | ||
|
|
ccd6e95f70 | ||
|
|
fb904a5cc2 | ||
|
|
a255bbe2b8 | ||
|
|
a314878cc4 | ||
|
|
61b8b0a509 | ||
|
|
f144cd857e | ||
|
|
c922a1cd29 | ||
|
|
6cefe7b3c0 | ||
|
|
a3611b026c | ||
|
|
d33d3d9a4b | ||
|
|
ec68baa74e | ||
|
|
7216b0f5cb | ||
|
|
83b21bff7d | ||
|
|
cb52875651 | ||
|
|
89bcceaadc | ||
|
|
51dc454948 | ||
|
|
c7d57e24c3 | ||
|
|
54c702fb7f | ||
|
|
da6c9ebd1e | ||
|
|
f987e45c97 | ||
|
|
1f2ac2e1e7 | ||
|
|
b13c26b06a | ||
|
|
b5ace63438 | ||
|
|
15a81ee09f | ||
|
|
fecfdc101c | ||
|
|
4fdf3ef190 | ||
|
|
88f4ac2349 | ||
|
|
deaebd3333 | ||
|
|
6b0655aed3 | ||
|
|
2901345ea6 | ||
|
|
e51677b0f0 | ||
|
|
676166a160 | ||
|
|
761995b623 | ||
|
|
abc1b682d4 | ||
|
|
3b37cbb76c | ||
|
|
6c6229916e | ||
|
|
2aeb7b0710 | ||
|
|
a36c0629e9 | ||
|
|
5a9d3887a7 | ||
|
|
3fdd68b203 | ||
|
|
a1264deda0 | ||
|
|
f9fa5360d9 | ||
|
|
b4a1ec6e60 | ||
|
|
c30eb00c8d | ||
|
|
fc75405597 | ||
|
|
5726470881 | ||
|
|
6bbae1f7f0 | ||
|
|
ef5fe2fa91 | ||
|
|
585856ad89 | ||
|
|
8d1dbfe55d | ||
|
|
8a9c78662f | ||
|
|
8eb0029970 | ||
|
|
2dc20dd3fd | ||
|
|
2fefab5311 | ||
|
|
e7ab90a778 | ||
|
|
28226e4d03 | ||
|
|
e59ab99d45 | ||
|
|
3f1fa50608 | ||
|
|
71a37ace78 | ||
|
|
d4e16347b8 | ||
|
|
76c94ef0e1 | ||
|
|
db6351560a | ||
|
|
bd06a7ada7 | ||
|
|
42810a5e36 | ||
|
|
01e1dd027a | ||
|
|
275b7f9ba5 | ||
|
|
e18b6497ba | ||
|
|
14a4efe86b | ||
|
|
5e602c9c0b | ||
|
|
e2eb4a6eae | ||
|
|
8b195f1531 | ||
|
|
650b47a631 | ||
|
|
bd5c909bbc | ||
|
|
eb347f4d68 | ||
|
|
b3b88779b4 | ||
|
|
ade2bad564 | ||
|
|
4ca59f0984 | ||
|
|
ab9b129b43 | ||
|
|
5d9ee134c1 | ||
|
|
b6e4c34b77 | ||
|
|
c36595170d | ||
|
|
cf6b4dae98 | ||
|
|
994852741e | ||
|
|
9403fd84f6 | ||
|
|
4d5562e2dd | ||
|
|
cad60cff61 | ||
|
|
a9ce16add6 | ||
|
|
8a2755751b | ||
|
|
1401c8b156 | ||
|
|
5cf7db140c | ||
|
|
860d2e6f5e | ||
|
|
a434c0a7b9 | ||
|
|
c6c5521ecf | ||
|
|
7175183d6a | ||
|
|
8566cf9095 | ||
|
|
efeedb3247 | ||
|
|
a3849495d5 | ||
|
|
47a7d77e55 | ||
|
|
943ff02a38 | ||
|
|
8b48dc8bad | ||
|
|
e195dbf1b4 | ||
|
|
7ae5956f06 | ||
|
|
f48a06b8c5 | ||
|
|
48d2766c13 | ||
|
|
d677d11558 | ||
|
|
c396800657 | ||
|
|
1baf21aad9 | ||
|
|
dc068cbf33 | ||
|
|
2f8b9963ce | ||
|
|
6957ad0401 | ||
|
|
ce616fa409 | ||
|
|
e216036d8d | ||
|
|
69cacc1e3b | ||
|
|
8f6b96983f | ||
|
|
779c5078ca | ||
|
|
e6c3d10b37 | ||
|
|
4cc9e53a4d | ||
|
|
88b583865a |
2
.env
2
.env
@@ -10,6 +10,8 @@ LOGIN_URL=''
|
||||
LOGOUT_URL=''
|
||||
MARKETING_SITE_BASE_URL=''
|
||||
ORDER_HISTORY_URL=''
|
||||
ACCOUNT_SETTINGS_URL=''
|
||||
ACCOUNT_PROFILE_URL=''
|
||||
REFRESH_ACCESS_TOKEN_ENDPOINT=''
|
||||
SEGMENT_KEY=''
|
||||
SITE_NAME=''
|
||||
|
||||
@@ -3,7 +3,9 @@ PORT=1995
|
||||
ACCESS_TOKEN_COOKIE_NAME='edx-jwt-cookie-header-payload'
|
||||
BASE_URL='localhost:1995'
|
||||
CREDENTIALS_BASE_URL='http://localhost:18150'
|
||||
ACCOUNT_SETTINGS_URL=http://localhost:1997
|
||||
CSRF_TOKEN_API_PATH='/csrf/api/v1/token'
|
||||
ACCOUNT_PROFILE_URL=http://localhost:1995
|
||||
ECOMMERCE_BASE_URL='http://localhost:18130'
|
||||
LANGUAGE_PREFERENCE_COOKIE_NAME='openedx-language-preference'
|
||||
LMS_BASE_URL='http://localhost:18000'
|
||||
|
||||
@@ -5,6 +5,8 @@ CSRF_TOKEN_API_PATH='/csrf/api/v1/token'
|
||||
ECOMMERCE_BASE_URL='http://localhost:18130'
|
||||
LANGUAGE_PREFERENCE_COOKIE_NAME='openedx-language-preference'
|
||||
LMS_BASE_URL='http://localhost:18000'
|
||||
ACCOUNT_SETTINGS_URL='http://localhost:1997'
|
||||
ACCOUNT_PROFILE_URL='http://localhost:1995'
|
||||
LOGIN_URL='http://localhost:18000/login'
|
||||
LOGOUT_URL='http://localhost:18000/logout'
|
||||
MARKETING_SITE_BASE_URL='http://localhost:18000'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
const { createConfig } = require('@edx/frontend-build');
|
||||
const { createConfig } = require('@openedx/frontend-build');
|
||||
|
||||
module.exports = createConfig('eslint');
|
||||
|
||||
1
.github/CODEOWNERS
vendored
Normal file
1
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1 @@
|
||||
* @openedx/2U-infinity
|
||||
7
.github/dependabot.yml
vendored
Normal file
7
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
version: 2
|
||||
updates:
|
||||
# Adding new check for github-actions
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
24
.github/pull_request_template.md
vendored
Normal file
24
.github/pull_request_template.md
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
### Description
|
||||
|
||||
Include a description of your changes here, along with a link to any relevant Jira tickets and/or GitHub issues.
|
||||
|
||||
#### How Has This Been Tested?
|
||||
|
||||
Please describe in detail how you tested your changes.
|
||||
|
||||
#### Screenshots/sandbox (optional):
|
||||
Include a link to the sandbox for design changes or screenshot for before and after. **Remove this section if it's not applicable.**
|
||||
|
||||
|Before|After|
|
||||
|-------|-----|
|
||||
| | |
|
||||
|
||||
#### Merge Checklist
|
||||
|
||||
* [ ] If your update includes visual changes, have they been reviewed by a designer? Send them a link to the Sandbox, if applicable.
|
||||
* [ ] Is there adequate test coverage for your changes?
|
||||
|
||||
#### Post-merge Checklist
|
||||
|
||||
* [ ] Deploy the changes to prod after verifying on stage or ask **@openedx/2u-infinity** to do it.
|
||||
* [ ] 🎉 🙌 Celebrate! Thanks for your contribution.
|
||||
16
.github/workflows/ci.yml
vendored
16
.github/workflows/ci.yml
vendored
@@ -14,16 +14,16 @@ jobs:
|
||||
- lint
|
||||
- test
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Setup Nodejs Env
|
||||
run: echo "NODE_VER=`cat .nvmrc`" >> $GITHUB_ENV
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ env.NODE_VER }}
|
||||
node-version-file: '.nvmrc'
|
||||
- run: make requirements
|
||||
- run: make test NPM_TESTS=build
|
||||
- run: make test NPM_TESTS=${{ matrix.npm-test }}
|
||||
- name: upload coverage
|
||||
uses: codecov/codecov-action@v3
|
||||
- name: Coverage
|
||||
if: ${{ matrix.npm-test == 'test' }}
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
fail_ci_if_error: false
|
||||
token: ${{ secrets.CODECOV_TOKEN }}
|
||||
fail_ci_if_error: true
|
||||
|
||||
2
.github/workflows/lockfileversion-check.yml
vendored
2
.github/workflows/lockfileversion-check.yml
vendored
@@ -10,4 +10,4 @@ on:
|
||||
|
||||
jobs:
|
||||
version-check:
|
||||
uses: openedx/.github/.github/workflows/lockfile-check.yml@master
|
||||
uses: openedx/.github/.github/workflows/lockfileversion-check-v3.yml@master
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -17,3 +17,4 @@ temp/babel-plugin-react-intl
|
||||
/temp
|
||||
/.vscode
|
||||
/module.config.js
|
||||
src/i18n/messages
|
||||
@@ -1,9 +0,0 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
|
||||
[o:open-edx:p:edx-platform:r:frontend-app-profile]
|
||||
file_filter = src/i18n/messages/<lang>.json
|
||||
source_file = src/i18n/transifex_input.json
|
||||
source_lang = en
|
||||
type = KEYVALUEJSON
|
||||
|
||||
27
Makefile
27
Makefile
@@ -1,7 +1,3 @@
|
||||
export TRANSIFEX_RESOURCE = frontend-app-profile
|
||||
transifex_resource = frontend-app-profile
|
||||
transifex_langs = "ar,de,de_DE,es_419,fa_IR,fr,fr_CA,hi,it,it_IT,pt,pt_PT,ru,uk,zh_CN"
|
||||
|
||||
intl_imports = ./node_modules/.bin/intl-imports.js
|
||||
transifex_utils = ./node_modules/.bin/transifex-utils.js
|
||||
i18n = ./src/i18n
|
||||
@@ -39,35 +35,18 @@ detect_changed_source_translations:
|
||||
# Checking for changed translations...
|
||||
git diff --exit-code $(i18n)
|
||||
|
||||
# Pushes translations to Transifex. You must run make extract_translations first.
|
||||
push_translations:
|
||||
# Pushing strings to Transifex...
|
||||
tx push -s
|
||||
# Fetching hashes from Transifex...
|
||||
./node_modules/@edx/reactifex/bash_scripts/get_hashed_strings_v3.sh
|
||||
# Writing out comments to file...
|
||||
$(transifex_utils) $(transifex_temp) --comments --v3-scripts-path
|
||||
# Pushing comments to Transifex...
|
||||
./node_modules/@edx/reactifex/bash_scripts/put_comments_v3.sh
|
||||
|
||||
ifeq ($(OPENEDX_ATLAS_PULL),)
|
||||
# Pulls translations from Transifex.
|
||||
pull_translations:
|
||||
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) \
|
||||
&& atlas pull $(ATLAS_OPTIONS) \
|
||||
translations/frontend-platform/src/i18n/messages:frontend-platform \
|
||||
translations/paragon/src/i18n/messages:paragon \
|
||||
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) paragon frontend-component-header frontend-component-footer frontend-app-profile
|
||||
endif
|
||||
$(intl_imports) frontend-platform paragon frontend-component-header frontend-component-footer frontend-app-profile
|
||||
|
||||
# This target is used by Travis.
|
||||
validate-no-uncommitted-package-lock-changes:
|
||||
|
||||
@@ -71,6 +71,12 @@ Profile MFE for local development via the `devstack`_.
|
||||
|
||||
Once the dev server is up, visit http://localhost:1995/u/staff.
|
||||
|
||||
Plugins
|
||||
=======
|
||||
This MFE can be customized using `Frontend Plugin Framework <https://github.com/openedx/frontend-plugin-framework>`_.
|
||||
|
||||
The parts of this MFE that can be customized in that manner are documented `here </src/plugin-slots>`_.
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
@@ -92,7 +98,7 @@ frontend repository, the best place to discuss it would be in the `#wg-frontend
|
||||
channel`_.
|
||||
|
||||
For anything non-trivial, the best path is to open an issue in this repository
|
||||
with as many details about the issue you are facing as you can provide. Please tag **@openedx/2u-aperture** on any PRs or issues.
|
||||
with as many details about the issue you are facing as you can provide. Please tag **@openedx/2u-infinity** on any PRs or issues.
|
||||
|
||||
https://github.com/openedx/frontend-app-profile/issues
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: 'Profile'
|
||||
description: 'This is a micro-frontend application responsible for the display and updating of user profiles.'
|
||||
name: 'frontend-app-profile'
|
||||
description: 'This is a micro-frontend application responsible for displaying and updating the user profiles.'
|
||||
links:
|
||||
- url: 'https://github.com/openedx/frontend-app-profile/blob/master/README.rst'
|
||||
title: 'Documentation'
|
||||
@@ -17,8 +17,9 @@ metadata:
|
||||
openedx.org/arch-interest-groups: ""
|
||||
# This can be multiple comma-separated projects.
|
||||
openedx.org/add-to-projects: "openedx:23"
|
||||
openedx.org/release: "master"
|
||||
spec:
|
||||
owner: group:2u-infinity
|
||||
type: 'service'
|
||||
lifecycle: 'production'
|
||||
owner: 2U-aperture
|
||||
# (Optional) An array of different components or resources.
|
||||
# (Optional) An array of different components or resources.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const { createConfig } = require('@edx/frontend-build');
|
||||
const { createConfig } = require('@openedx/frontend-build');
|
||||
|
||||
module.exports = createConfig('jest', {
|
||||
setupFiles: [
|
||||
setupFilesAfterEnv: [
|
||||
'<rootDir>/src/setupTest.js',
|
||||
],
|
||||
});
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
# This file describes this Open edX repo, as described in OEP-2:
|
||||
# https://open-edx-proposals.readthedocs.io/en/latest/oep-0002-bp-repo-metadata.html#specification
|
||||
|
||||
nick: prof
|
||||
oeps: {}
|
||||
openedx-release: {ref: master}
|
||||
18240
package-lock.json
generated
18240
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
58
package.json
58
package.json
@@ -14,6 +14,7 @@
|
||||
"lint": "fedx-scripts eslint --ext .js --ext .jsx .",
|
||||
"snapshot": "fedx-scripts jest --updateSnapshot",
|
||||
"start": "fedx-scripts webpack-dev-server --progress",
|
||||
"dev": "PUBLIC_PATH=/profile/ MFE_CONFIG_API_URL='http://localhost:8000/api/mfe_config/v1' fedx-scripts webpack-dev-server --progress --host apps.local.openedx.io",
|
||||
"test": "TZ=UTC fedx-scripts jest --coverage --passWithNoTests",
|
||||
"stubs": "pact-stub-service ./src/pacts/frontend-app-profile-edx-platform.json --port 18000"
|
||||
},
|
||||
@@ -29,52 +30,51 @@
|
||||
],
|
||||
"dependencies": {
|
||||
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
|
||||
"@edx/frontend-component-footer": "12.6.0",
|
||||
"@edx/frontend-component-header": "4.10.1",
|
||||
"@edx/frontend-platform": "5.6.1",
|
||||
"@edx/paragon": "^20.44.0",
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.36",
|
||||
"@fortawesome/free-brands-svg-icons": "5.15.4",
|
||||
"@fortawesome/free-regular-svg-icons": "5.15.4",
|
||||
"@fortawesome/free-solid-svg-icons": "5.15.4",
|
||||
"@fortawesome/react-fontawesome": "0.2.0",
|
||||
"@edx/frontend-component-footer": "^14.6.0",
|
||||
"@edx/frontend-component-header": "^6.2.0",
|
||||
"@edx/frontend-platform": "^8.3.1",
|
||||
"@edx/openedx-atlas": "^0.7.0",
|
||||
"@fortawesome/fontawesome-svg-core": "6.7.2",
|
||||
"@fortawesome/free-brands-svg-icons": "6.7.2",
|
||||
"@fortawesome/free-regular-svg-icons": "6.7.2",
|
||||
"@fortawesome/free-solid-svg-icons": "6.7.2",
|
||||
"@fortawesome/react-fontawesome": "0.2.2",
|
||||
"@openedx/frontend-plugin-framework": "^1.7.0",
|
||||
"@openedx/paragon": "^22.17.0",
|
||||
"@pact-foundation/pact": "^11.0.2",
|
||||
"classnames": "2.3.2",
|
||||
"core-js": "3.33.3",
|
||||
"@redux-devtools/extension": "3.3.0",
|
||||
"classnames": "2.5.1",
|
||||
"core-js": "3.41.0",
|
||||
"history": "5.3.0",
|
||||
"lodash.camelcase": "4.3.0",
|
||||
"lodash.get": "4.4.2",
|
||||
"lodash.pick": "4.4.0",
|
||||
"lodash.snakecase": "4.1.1",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"react-error-boundary": "^4.0.11",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react-helmet": "6.1.0",
|
||||
"react-redux": "7.2.9",
|
||||
"react-router": "6.16.0",
|
||||
"react-router-dom": "6.16.0",
|
||||
"react-router": "6.30.0",
|
||||
"react-router-dom": "6.30.0",
|
||||
"redux": "4.2.1",
|
||||
"redux-devtools-extension": "2.13.9",
|
||||
"redux-logger": "3.0.6",
|
||||
"redux-saga": "1.2.3",
|
||||
"redux-saga": "1.3.0",
|
||||
"redux-thunk": "2.4.2",
|
||||
"regenerator-runtime": "0.14.0",
|
||||
"reselect": "4.1.8",
|
||||
"regenerator-runtime": "0.14.1",
|
||||
"reselect": "5.1.1",
|
||||
"universal-cookie": "4.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "17.8.1",
|
||||
"@commitlint/config-angular": "17.8.1",
|
||||
"@commitlint/cli": "19.8.0",
|
||||
"@commitlint/config-angular": "19.8.0",
|
||||
"@edx/browserslist-config": "^1.1.1",
|
||||
"@edx/frontend-build": "13.0.8",
|
||||
"@edx/reactifex": "2.2.0",
|
||||
"@testing-library/react": "12.1.5",
|
||||
"@wojtekmaj/enzyme-adapter-react-17": "0.8.0",
|
||||
"enzyme": "3.11.0",
|
||||
"glob": "10.3.10",
|
||||
"react-test-renderer": "17.0.2",
|
||||
"@openedx/frontend-build": "^14.3.3",
|
||||
"@testing-library/jest-dom": "6.6.3",
|
||||
"@testing-library/react": "14.3.1",
|
||||
"glob": "11.0.1",
|
||||
"reactifex": "1.1.1",
|
||||
"redux-mock-store": "1.5.4"
|
||||
"redux-mock-store": "1.5.5"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import React, {
|
||||
useEffect, useMemo, useState,
|
||||
} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { ErrorBoundary } from 'react-error-boundary';
|
||||
import { logError } from '@edx/frontend-platform/logging';
|
||||
import {
|
||||
dispatchMountedEvent, dispatchReadyEvent, dispatchUnmountedEvent, useHostEvent,
|
||||
} from './data/hooks';
|
||||
import { PLUGIN_RESIZE } from './data/constants';
|
||||
|
||||
// see example-plugin-app/src/PluginOne.jsx for example of customizing errorFallback
|
||||
function errorFallbackDefault() {
|
||||
return (
|
||||
<div>
|
||||
<h2>
|
||||
Oops! An error occurred. Please refresh the screen to try again.
|
||||
</h2>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react/function-component-definition
|
||||
export default function Plugin({
|
||||
children, className, style, ready, errorFallbackProp,
|
||||
}) {
|
||||
const [dimensions, setDimensions] = useState({
|
||||
width: null,
|
||||
height: null,
|
||||
});
|
||||
|
||||
const finalStyle = useMemo(() => ({
|
||||
...dimensions,
|
||||
...style,
|
||||
}), [dimensions, style]);
|
||||
|
||||
const errorFallback = errorFallbackProp || errorFallbackDefault;
|
||||
|
||||
// Error logging function
|
||||
// Need to confirm: When an error is caught here, the logging will be sent to the child MFE's logging service
|
||||
const logErrorToService = (error, info) => {
|
||||
logError(error, { stack: info.componentStack });
|
||||
};
|
||||
|
||||
useHostEvent(PLUGIN_RESIZE, ({ payload }) => {
|
||||
setDimensions({
|
||||
width: payload.width,
|
||||
height: payload.height,
|
||||
});
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
dispatchMountedEvent();
|
||||
|
||||
return () => {
|
||||
dispatchUnmountedEvent();
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (ready) {
|
||||
dispatchReadyEvent();
|
||||
}
|
||||
}, [ready]);
|
||||
|
||||
return (
|
||||
<div className={className} style={finalStyle}>
|
||||
<ErrorBoundary
|
||||
FallbackComponent={errorFallback}
|
||||
onError={logErrorToService}
|
||||
>
|
||||
{children}
|
||||
</ErrorBoundary>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Plugin.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
className: PropTypes.string,
|
||||
errorFallbackProp: PropTypes.func,
|
||||
ready: PropTypes.bool,
|
||||
style: PropTypes.object, // eslint-disable-line
|
||||
};
|
||||
|
||||
Plugin.defaultProps = {
|
||||
className: null,
|
||||
errorFallbackProp: null,
|
||||
style: {},
|
||||
ready: true,
|
||||
};
|
||||
@@ -1,42 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import PluginContainerIframe from './PluginContainerIframe';
|
||||
|
||||
import {
|
||||
IFRAME_PLUGIN,
|
||||
} from './data/constants';
|
||||
import { pluginConfigShape } from './data/shapes';
|
||||
|
||||
// eslint-disable-next-line react/function-component-definition
|
||||
export default function PluginContainer({ config, ...props }) {
|
||||
if (config === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// this will allow for future plugin types to be inserted in the PluginErrorBoundary
|
||||
let renderer = null;
|
||||
switch (config.type) {
|
||||
case IFRAME_PLUGIN:
|
||||
renderer = (
|
||||
<PluginContainerIframe config={config} {...props} />
|
||||
);
|
||||
break;
|
||||
// istanbul ignore next: default isn't meaningful, just satisfying linter
|
||||
default:
|
||||
}
|
||||
|
||||
return (
|
||||
renderer
|
||||
);
|
||||
}
|
||||
|
||||
PluginContainer.propTypes = {
|
||||
config: pluginConfigShape,
|
||||
};
|
||||
|
||||
PluginContainer.defaultProps = {
|
||||
config: null,
|
||||
};
|
||||
@@ -1,99 +0,0 @@
|
||||
import React, {
|
||||
useEffect, useState,
|
||||
} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import {
|
||||
PLUGIN_MOUNTED,
|
||||
PLUGIN_READY,
|
||||
PLUGIN_RESIZE,
|
||||
} from './data/constants';
|
||||
import {
|
||||
dispatchPluginEvent,
|
||||
useElementSize,
|
||||
usePluginEvent,
|
||||
} from './data/hooks';
|
||||
import { pluginConfigShape } from './data/shapes';
|
||||
|
||||
/**
|
||||
* Feature policy for iframe, allowing access to certain courseware-related media.
|
||||
*
|
||||
* We must use the wildcard (*) origin for each feature, as courseware content
|
||||
* may be embedded in external iframes. Notably, xblock-lti-consumer is a popular
|
||||
* block that iframes external course content.
|
||||
|
||||
* This policy was selected in conference with the edX Security Working Group.
|
||||
* Changes to it should be vetted by them (security@edx.org).
|
||||
*/
|
||||
export const IFRAME_FEATURE_POLICY = (
|
||||
'fullscreen; microphone *; camera *; midi *; geolocation *; encrypted-media *'
|
||||
);
|
||||
|
||||
// eslint-disable-next-line react/function-component-definition
|
||||
export default function PluginContainerIframe({
|
||||
config, fallback, className, ...props
|
||||
}) {
|
||||
const { url } = config;
|
||||
const { title, scrolling } = props;
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const [ready, setReady] = useState(false);
|
||||
|
||||
const [iframeRef, iframeElement, width, height] = useElementSize();
|
||||
|
||||
useEffect(() => {
|
||||
if (mounted) {
|
||||
dispatchPluginEvent(iframeElement, {
|
||||
type: PLUGIN_RESIZE,
|
||||
payload: {
|
||||
width,
|
||||
height,
|
||||
},
|
||||
}, url);
|
||||
}
|
||||
}, [iframeElement, mounted, width, height, url]);
|
||||
|
||||
usePluginEvent(iframeElement, PLUGIN_MOUNTED, () => {
|
||||
setMounted(true);
|
||||
});
|
||||
|
||||
usePluginEvent(iframeElement, PLUGIN_READY, () => {
|
||||
setReady(true);
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<iframe
|
||||
ref={iframeRef}
|
||||
title={title}
|
||||
src={url}
|
||||
allow={IFRAME_FEATURE_POLICY}
|
||||
scrolling={scrolling}
|
||||
referrerPolicy="origin" // The sent referrer will be limited to the origin of the referring page: its scheme, host, and port.
|
||||
className={classNames(
|
||||
'border border-0',
|
||||
{ 'd-none': !ready },
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
{!ready && fallback}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
PluginContainerIframe.propTypes = {
|
||||
config: pluginConfigShape,
|
||||
fallback: PropTypes.node,
|
||||
scrolling: PropTypes.oneOf(['auto', 'yes', 'no']),
|
||||
title: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
PluginContainerIframe.defaultProps = {
|
||||
config: null,
|
||||
fallback: null,
|
||||
scrolling: 'auto',
|
||||
title: null,
|
||||
className: null,
|
||||
};
|
||||
@@ -1,45 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { logError } from '@edx/frontend-platform/logging';
|
||||
|
||||
export default class PluginErrorBoundary extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = { hasError: false };
|
||||
}
|
||||
|
||||
static getDerivedStateFromError() {
|
||||
// Update state so the next render will show the fallback UI.
|
||||
return { hasError: true };
|
||||
}
|
||||
|
||||
componentDidCatch(error, info) {
|
||||
logError(error, { stack: info.componentStack });
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
// You can render any custom fallback UI
|
||||
return (
|
||||
<FormattedMessage
|
||||
id="plugin.load.failure.text"
|
||||
defaultMessage="This content failed to load."
|
||||
description="error message when an unexpected error occurs"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
PluginErrorBoundary.propTypes = {
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
PluginErrorBoundary.defaultProps = {
|
||||
children: null,
|
||||
};
|
||||
@@ -1,75 +0,0 @@
|
||||
/* eslint-disable no-unused-vars */
|
||||
import React, { forwardRef } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { Spinner } from '@edx/paragon';
|
||||
import PropTypes from 'prop-types';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
// import { usePluginSlot } from './data/hooks';
|
||||
import PluginContainer from './PluginContainer';
|
||||
|
||||
const PluginSlot = forwardRef(({
|
||||
as, id, intl, pluginProps, children, ...props
|
||||
}, ref) => {
|
||||
/* the plugins below are obtained by the id passed into PluginSlot by the Host MFE. See example/src/PluginsPage.jsx
|
||||
for an example of how PluginSlot is populated, and example/src/index.jsx for a dummy JS config that holds all plugins
|
||||
*/
|
||||
// const { plugins, keepDefault } = usePluginSlot(id);
|
||||
|
||||
const { fallback } = pluginProps;
|
||||
|
||||
// TODO: Add internationalization to the "Loading" text on the spinner.
|
||||
let finalFallback = (
|
||||
<div className={classNames(pluginProps.className, 'd-flex justify-content-center align-items-center')}>
|
||||
<Spinner animation="border" screenReaderText="Loading" />
|
||||
</div>
|
||||
);
|
||||
if (fallback !== undefined) {
|
||||
finalFallback = fallback;
|
||||
}
|
||||
|
||||
let finalChildren = [];
|
||||
// if (plugins.length > 0) {
|
||||
// if (keepDefault) {
|
||||
// finalChildren.push(children);
|
||||
// }
|
||||
// plugins.forEach((pluginConfig) => {
|
||||
// finalChildren.push(
|
||||
// <PluginContainer
|
||||
// key={pluginConfig.url}
|
||||
// config={pluginConfig}
|
||||
// fallback={finalFallback}
|
||||
// {...pluginProps}
|
||||
// />,
|
||||
// );
|
||||
// });
|
||||
// } else {
|
||||
finalChildren = children;
|
||||
// }
|
||||
|
||||
return React.createElement(
|
||||
as,
|
||||
{
|
||||
...props,
|
||||
ref,
|
||||
},
|
||||
finalChildren,
|
||||
);
|
||||
});
|
||||
|
||||
export default injectIntl(PluginSlot);
|
||||
|
||||
PluginSlot.propTypes = {
|
||||
as: PropTypes.elementType,
|
||||
children: PropTypes.node,
|
||||
id: PropTypes.string.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
pluginProps: PropTypes.object, // eslint-disable-line
|
||||
};
|
||||
|
||||
PluginSlot.defaultProps = {
|
||||
as: 'div',
|
||||
children: null,
|
||||
pluginProps: {},
|
||||
};
|
||||
@@ -1,8 +0,0 @@
|
||||
// TODO: We expect other plugin types to be added here, such as LTI_PLUGIN and BUILD_TIME_PLUGIN.
|
||||
export const IFRAME_PLUGIN = 'IFRAME_PLUGIN'; // loads iframe at the URL, rather than loading a JS file.
|
||||
|
||||
// Plugin lifecycle events
|
||||
export const PLUGIN_MOUNTED = 'PLUGIN_MOUNTED';
|
||||
export const PLUGIN_READY = 'PLUGIN_READY';
|
||||
export const PLUGIN_UNMOUNTED = 'PLUGIN_UNMOUNTED';
|
||||
export const PLUGIN_RESIZE = 'PLUGIN_RESIZE';
|
||||
@@ -1,96 +0,0 @@
|
||||
import {
|
||||
useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState,
|
||||
} from 'react';
|
||||
import { PLUGIN_MOUNTED, PLUGIN_READY, PLUGIN_UNMOUNTED } from './constants';
|
||||
|
||||
export function useMessageEvent(srcWindow, type, callback) {
|
||||
useLayoutEffect(() => {
|
||||
const listener = (event) => {
|
||||
// Filter messages to those from our source window.
|
||||
if (event.source === srcWindow) {
|
||||
if (event.data.type === type) {
|
||||
callback({ type, payload: event.data.payload });
|
||||
}
|
||||
}
|
||||
};
|
||||
if (srcWindow !== null) {
|
||||
global.addEventListener('message', listener);
|
||||
}
|
||||
return () => {
|
||||
global.removeEventListener('message', listener);
|
||||
};
|
||||
}, [srcWindow, type, callback]);
|
||||
}
|
||||
|
||||
export function useHostEvent(type, callback) {
|
||||
useMessageEvent(global.parent, type, callback);
|
||||
}
|
||||
|
||||
export function usePluginEvent(iframeElement, type, callback) {
|
||||
const contentWindow = iframeElement ? iframeElement.contentWindow : null;
|
||||
useMessageEvent(contentWindow, type, callback);
|
||||
}
|
||||
|
||||
export function dispatchMessageEvent(targetWindow, message, targetOrigin) {
|
||||
// Checking targetOrigin falsiness here since '', null or undefined would all be reasons not to
|
||||
// try to post a message to the origin.
|
||||
if (targetOrigin) {
|
||||
targetWindow.postMessage(message, targetOrigin);
|
||||
}
|
||||
}
|
||||
|
||||
export function dispatchPluginEvent(iframeElement, message, targetOrigin) {
|
||||
dispatchMessageEvent(iframeElement.contentWindow, message, targetOrigin);
|
||||
}
|
||||
|
||||
export function dispatchHostEvent(message) {
|
||||
dispatchMessageEvent(global.parent, message, global.document.referrer);
|
||||
}
|
||||
|
||||
export function dispatchReadyEvent() {
|
||||
dispatchHostEvent({ type: PLUGIN_READY });
|
||||
}
|
||||
|
||||
export function dispatchMountedEvent() {
|
||||
dispatchHostEvent({ type: PLUGIN_MOUNTED });
|
||||
}
|
||||
|
||||
export function dispatchUnmountedEvent() {
|
||||
dispatchHostEvent({ type: PLUGIN_UNMOUNTED });
|
||||
}
|
||||
|
||||
export function useElementSize() {
|
||||
const observerRef = useRef();
|
||||
|
||||
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
|
||||
const [offset, setOffset] = useState({ x: 0, y: 0 });
|
||||
|
||||
const [element, setElement] = useState(null);
|
||||
|
||||
const measuredRef = useCallback(_element => {
|
||||
setElement(_element);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
observerRef.current = new ResizeObserver(() => {
|
||||
if (element) {
|
||||
setDimensions({
|
||||
width: element.clientWidth,
|
||||
height: element.clientHeight,
|
||||
});
|
||||
setOffset({
|
||||
x: element.offsetLeft,
|
||||
y: element.offsetTop,
|
||||
});
|
||||
}
|
||||
});
|
||||
if (element) {
|
||||
observerRef.current.observe(element);
|
||||
}
|
||||
}, [element]);
|
||||
|
||||
return useMemo(
|
||||
() => ([measuredRef, element, dimensions.width, dimensions.height, offset.x, offset.y]),
|
||||
[measuredRef, element, dimensions, offset],
|
||||
);
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import PropTypes from 'prop-types';
|
||||
import { IFRAME_PLUGIN } from './constants';
|
||||
|
||||
export const pluginConfigShape = PropTypes.shape({
|
||||
url: PropTypes.string.isRequired,
|
||||
type: PropTypes.oneOf([IFRAME_PLUGIN]).isRequired,
|
||||
// This is a place for us to put any generic props we want to pass to the component. We need it.
|
||||
props: PropTypes.object, // eslint-disable-line react/forbid-prop-types
|
||||
});
|
||||
@@ -1,18 +0,0 @@
|
||||
// export {
|
||||
// usePluginSlot,
|
||||
// } from './data/hooks';
|
||||
export {
|
||||
default as Plugin,
|
||||
} from './Plugin';
|
||||
export {
|
||||
default as PluginContainer,
|
||||
} from './PluginContainer';
|
||||
export {
|
||||
default as PluginSlot,
|
||||
} from './PluginSlot';
|
||||
export {
|
||||
IFRAME_PLUGIN,
|
||||
} from './data/constants';
|
||||
export {
|
||||
default as PluginErrorBoundary,
|
||||
} from './PluginErrorBoundary';
|
||||
@@ -1,7 +1,7 @@
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { applyMiddleware, createStore, compose } from 'redux';
|
||||
import thunkMiddleware from 'redux-thunk';
|
||||
import { composeWithDevTools } from 'redux-devtools-extension';
|
||||
import { composeWithDevTools } from '@redux-devtools/extension';
|
||||
import { createLogger } from 'redux-logger';
|
||||
import createSagaMiddleware from 'redux-saga';
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import React from 'react';
|
||||
import { IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { mount } from 'enzyme';
|
||||
import { render } from '@testing-library/react';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import Head from './Head';
|
||||
|
||||
describe('Head', () => {
|
||||
const props = {};
|
||||
it('should match render title tag and favicon with the site configuration values', () => {
|
||||
mount(<IntlProvider locale="en"><Head {...props} /></IntlProvider>);
|
||||
render(<IntlProvider locale="en"><Head {...props} /></IntlProvider>);
|
||||
const helmet = Helmet.peek();
|
||||
expect(helmet.title).toEqual(`Profile | ${getConfig().SITE_NAME}`);
|
||||
expect(helmet.linkTags[0].rel).toEqual('shortcut icon');
|
||||
|
||||
@@ -1,44 +1 @@
|
||||
import { messages as headerMessages } from '@edx/frontend-component-header';
|
||||
import { messages as footerMessages } from '@edx/frontend-component-footer';
|
||||
import { messages as paragonMessages } from '@edx/paragon';
|
||||
import arMessages from './messages/ar.json';
|
||||
import deMessages from './messages/de.json';
|
||||
import dedeCAMessages from './messages/de_DE.json';
|
||||
import es419Messages from './messages/es_419.json';
|
||||
import faIRMessages from './messages/fa_IR.json';
|
||||
import frCAMessages from './messages/fr_CA.json';
|
||||
import itMessages from './messages/it.json';
|
||||
import ititCAMessages from './messages/it_IT.json';
|
||||
import frMessages from './messages/fr.json';
|
||||
import hiMessages from './messages/hi.json';
|
||||
import ptMessages from './messages/pt.json';
|
||||
import ptptCAMessages from './messages/pt_PT.json';
|
||||
import ruMessages from './messages/ru.json';
|
||||
import ukMessages from './messages/uk.json';
|
||||
import zhcnMessages from './messages/zh_CN.json';
|
||||
// no need to import en messages-- they are in the defaultMessage field
|
||||
|
||||
const appMessages = {
|
||||
ar: arMessages,
|
||||
'es-419': es419Messages,
|
||||
'fa-ir': faIRMessages,
|
||||
fr: frMessages,
|
||||
'zh-cn': zhcnMessages,
|
||||
pt: ptMessages,
|
||||
it: itMessages,
|
||||
de: deMessages,
|
||||
hi: hiMessages,
|
||||
'fr-ca': frCAMessages,
|
||||
ru: ruMessages,
|
||||
uk: ukMessages,
|
||||
'de-de': dedeCAMessages,
|
||||
'it-it': ititCAMessages,
|
||||
'pt-pt': ptptCAMessages,
|
||||
};
|
||||
|
||||
export default [
|
||||
headerMessages,
|
||||
footerMessages,
|
||||
paragonMessages,
|
||||
appMessages,
|
||||
];
|
||||
export default [];
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
{
|
||||
"profile.page.title": "الملف الشخصي | {siteName}",
|
||||
"profile.age.details": "لمشاركة ملفك الشخصي مع بقية متعلمي {siteName}، يجب أن تؤكد أنك عمرك يفوق 13 عامًا.",
|
||||
"profile.age.set.date": "ضيط تاريخ ميلادك",
|
||||
"profile.datejoined.member.since": "عضو منذ {year}",
|
||||
"profile.bio.empty": "إضافة نبذة قصيرة",
|
||||
"profile.bio.about.me": "نبذة عنّي",
|
||||
"profile.certificate.organization.label": "من طرف",
|
||||
"profile.certificate.completion.date.label": "صدرت بتاريخ {date}",
|
||||
"profile.no.certificates": "ليست لديك أي شهادات يعد.",
|
||||
"profile.certificates.my.certificates": "شهاداتي",
|
||||
"profile.certificates.view.certificate": "معاينة الشهادة",
|
||||
"profile.certificates.types.verified": "شهادة موثقة",
|
||||
"profile.certificates.types.professional": "شهادة مهنية",
|
||||
"profile.certificates.types.unknown": "شهادة",
|
||||
"profile.country.label": "الموقع",
|
||||
"profile.country.empty": "إضافة الموقع",
|
||||
"profile.education.empty": "إضافة المستوى التعليمي",
|
||||
"profile.education.education": "المستوى التعليمي",
|
||||
"profile.education.levels.p": "دكتوراه",
|
||||
"profile.education.levels.m": "ماجستير / ماستر أو شهادة مهنيّة",
|
||||
"profile.education.levels.b": "بكالوريوس / ليسانس",
|
||||
"profile.education.levels.a": "درجة الزمالة / دبلوم الدراسات الجامعية",
|
||||
"profile.education.levels.hs": "الثانوية العامة / البكالوريا",
|
||||
"profile.education.levels.jhs": "المدرسة الإعدادية / المتوسطة",
|
||||
"profile.education.levels.el": "المدرسة الابتدائية / الأساسية",
|
||||
"profile.education.levels.none": "دون تعليم رسمي",
|
||||
"profile.education.levels.o": "نوع آخر من التعليم",
|
||||
"profile.editbutton.edit": "تعديل",
|
||||
"profile.formcontrols.who.can.see": "من يستطيع رؤية هذا:",
|
||||
"profile.formcontrols.button.cancel": "إلغاء",
|
||||
"profile.formcontrols.button.save": "حفظ",
|
||||
"profile.formcontrols.button.saving": "الحفظ جارٍ",
|
||||
"profile.formcontrols.button.saved": "تم الحفظ",
|
||||
"profile.visibility.who.just.me": "أنا فقط",
|
||||
"profile.visibility.who.everyone": "جميع من على {siteName}",
|
||||
"profile.learningGoal.learningGoal": "هدف التعلم",
|
||||
"profile.learningGoal.options.start_career": "أريد أن أبدأ مسيرتي المهنية",
|
||||
"profile.learningGoal.options.advance_career": "أريد أن ارتقي في مسيرتي المهنية",
|
||||
"profile.learningGoal.options.learn_something_new": "أريد أن أتعلم شيئًا جديدًا",
|
||||
"profile.learningGoal.options.something_else": "شيء آخر",
|
||||
"profile.name.full.name": "الاسم الكامل",
|
||||
"profile.name.details": "هذا هو الاسم الذي يظهر في حسابك وفي شهاداتك",
|
||||
"profile.name.empty": "إضافة الاسم",
|
||||
"profile.preferredlanguage.empty": "إضافة اللغة",
|
||||
"profile.preferredlanguage.label": "لغة التحدّث الأساسية",
|
||||
"profile.profileavatar.upload-button": "تحميل صورة",
|
||||
"profile.profileavatar.remove.button": "حذف",
|
||||
"profile.image.alt.attribute": "صورة الملف الشخصي",
|
||||
"profile.profileavatar.change-button": "تغيير",
|
||||
"profile.sociallinks.add": "إضافة {network}",
|
||||
"profile.sociallinks.social.links": "روابط التواصل الاجتماعي",
|
||||
"profile.notfound.message": "الصفحة التي تبحث عنها غير متوفرة أو هناك خطأ في العنوان. رجاءً تحقق من العنوان و حاول مجدّدًا.",
|
||||
"profile.viewMyRecords": "عرض سجلّاتي",
|
||||
"profile.loading": "يتم تحميل الملف الشخصي...",
|
||||
"profile.username.description": "معلومات ملفك الشخصي تظهر لك فقط. وحده اسم المستخدم الخاص بك يظهر للآخرين على {siteName}."
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
{
|
||||
"profile.page.title": "Profile | {siteName}",
|
||||
"profile.age.details": "To share your profile with other {siteName} learners, you must confirm that you are over the age of 13.",
|
||||
"profile.age.set.date": "Set your date of birth",
|
||||
"profile.datejoined.member.since": "Member since {year}",
|
||||
"profile.bio.empty": "Add a short bio",
|
||||
"profile.bio.about.me": "About Me",
|
||||
"profile.certificate.organization.label": "From",
|
||||
"profile.certificate.completion.date.label": "Completed on {date}",
|
||||
"profile.no.certificates": "You don't have any certificates yet.",
|
||||
"profile.certificates.my.certificates": "My Certificates",
|
||||
"profile.certificates.view.certificate": "View Certificate",
|
||||
"profile.certificates.types.verified": "Verified Certificate",
|
||||
"profile.certificates.types.professional": "Professional Certificate",
|
||||
"profile.certificates.types.unknown": "Certificate",
|
||||
"profile.country.label": "Location",
|
||||
"profile.country.empty": "Add location",
|
||||
"profile.education.empty": "Add education",
|
||||
"profile.education.education": "Education",
|
||||
"profile.education.levels.p": "Doctorate",
|
||||
"profile.education.levels.m": "Master's or professional degree",
|
||||
"profile.education.levels.b": "Bachelor's Degree",
|
||||
"profile.education.levels.a": "Associate's degree",
|
||||
"profile.education.levels.hs": "Secondary/high school",
|
||||
"profile.education.levels.jhs": "Junior secondary/junior high/middle school",
|
||||
"profile.education.levels.el": "Elementary/primary school",
|
||||
"profile.education.levels.none": "No formal education",
|
||||
"profile.education.levels.o": "Other education",
|
||||
"profile.editbutton.edit": "Edit",
|
||||
"profile.formcontrols.who.can.see": "Who can see this:",
|
||||
"profile.formcontrols.button.cancel": "Cancel",
|
||||
"profile.formcontrols.button.save": "Save",
|
||||
"profile.formcontrols.button.saving": "Saving",
|
||||
"profile.formcontrols.button.saved": "Saved",
|
||||
"profile.visibility.who.just.me": "Just me",
|
||||
"profile.visibility.who.everyone": "Everyone on {siteName}",
|
||||
"profile.learningGoal.learningGoal": "Learning Goal",
|
||||
"profile.learningGoal.options.start_career": "I want to start my career",
|
||||
"profile.learningGoal.options.advance_career": "I want to advance my career",
|
||||
"profile.learningGoal.options.learn_something_new": "I want to learn something new",
|
||||
"profile.learningGoal.options.something_else": "Something else",
|
||||
"profile.name.full.name": "Full Name",
|
||||
"profile.name.details": "This is the name that appears in your account and on your certificates.",
|
||||
"profile.name.empty": "Add name",
|
||||
"profile.preferredlanguage.empty": "Add language",
|
||||
"profile.preferredlanguage.label": "Primary Language Spoken",
|
||||
"profile.profileavatar.upload-button": "Upload Photo",
|
||||
"profile.profileavatar.remove.button": "Remove",
|
||||
"profile.image.alt.attribute": "profile avatar",
|
||||
"profile.profileavatar.change-button": "Change",
|
||||
"profile.sociallinks.add": "Add {network}",
|
||||
"profile.sociallinks.social.links": "Social Links",
|
||||
"profile.notfound.message": "The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again.",
|
||||
"profile.viewMyRecords": "View My Records",
|
||||
"profile.loading": "Profile loading...",
|
||||
"profile.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}."
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
{
|
||||
"profile.page.title": "Profil | {siteName}",
|
||||
"profile.age.details": "Um Ihr Profil mit anderen {siteName}-Lernern zu teilen, müssen Sie bestätigen, dass Sie über 13 Jahre alt sind.",
|
||||
"profile.age.set.date": "Legen Sie Ihr Geburtsdatum fest",
|
||||
"profile.datejoined.member.since": "Mitglied seit {year}",
|
||||
"profile.bio.empty": "Fügen Sie Ihre Kurzbiografie hinzu",
|
||||
"profile.bio.about.me": "Über mich",
|
||||
"profile.certificate.organization.label": "Von",
|
||||
"profile.certificate.completion.date.label": "Abgeschlossen am {date}",
|
||||
"profile.no.certificates": "Sie haben bisher keine Zertifikate erhalten.",
|
||||
"profile.certificates.my.certificates": "Meine Zertifikate",
|
||||
"profile.certificates.view.certificate": "Zertifikat anschauen",
|
||||
"profile.certificates.types.verified": "Beglaubigtes Zertifikat ",
|
||||
"profile.certificates.types.professional": "Professional Certificate",
|
||||
"profile.certificates.types.unknown": "Zertifikat",
|
||||
"profile.country.label": "Ort",
|
||||
"profile.country.empty": "Standort hinzufügen",
|
||||
"profile.education.empty": "Ausbildung hinzufügen",
|
||||
"profile.education.education": "Bildung",
|
||||
"profile.education.levels.p": "Doktortitel",
|
||||
"profile.education.levels.m": "Master oder gleichwertiger akademischer Bildungsgrad",
|
||||
"profile.education.levels.b": "Bachelor",
|
||||
"profile.education.levels.a": "Allgemeine Hochschulreife oder gleichwertiger Abschluss",
|
||||
"profile.education.levels.hs": "Mittlere Reife",
|
||||
"profile.education.levels.jhs": "Hauptschule",
|
||||
"profile.education.levels.el": "Grundschule",
|
||||
"profile.education.levels.none": "Keinen Bildungsabschluss",
|
||||
"profile.education.levels.o": "Sonstige Bildung",
|
||||
"profile.editbutton.edit": "Bearbeiten",
|
||||
"profile.formcontrols.who.can.see": "Wer kann das sehen:",
|
||||
"profile.formcontrols.button.cancel": "Abbrechen",
|
||||
"profile.formcontrols.button.save": "Speichern",
|
||||
"profile.formcontrols.button.saving": "Speichert",
|
||||
"profile.formcontrols.button.saved": "Gespeichert",
|
||||
"profile.visibility.who.just.me": "Nur ich",
|
||||
"profile.visibility.who.everyone": "Alle auf {siteName}",
|
||||
"profile.learningGoal.learningGoal": "Lernziel",
|
||||
"profile.learningGoal.options.start_career": "Ich möchte meine Karriere starten",
|
||||
"profile.learningGoal.options.advance_career": "Ich möchte mich beruflich weiterentwickeln",
|
||||
"profile.learningGoal.options.learn_something_new": "Ich möchte etwas Neues lernen",
|
||||
"profile.learningGoal.options.something_else": "Etwas anderes",
|
||||
"profile.name.full.name": "Vollständiger Name",
|
||||
"profile.name.details": "Dies ist der Name, der in Ihrem Konto und auf Ihren Zertifikaten erscheint.",
|
||||
"profile.name.empty": "Name hinzufügen",
|
||||
"profile.preferredlanguage.empty": "Sprache hinzufügen",
|
||||
"profile.preferredlanguage.label": "Gesprochene Primärsprache ",
|
||||
"profile.profileavatar.upload-button": "Foto hochladen",
|
||||
"profile.profileavatar.remove.button": "Entfernen",
|
||||
"profile.image.alt.attribute": "Profil Avatar",
|
||||
"profile.profileavatar.change-button": "Ändern",
|
||||
"profile.sociallinks.add": "{network} hinzufügen",
|
||||
"profile.sociallinks.social.links": "Soziale Netzwerke",
|
||||
"profile.notfound.message": "Die gesuchte Seite ist nicht verfügbar oder es liegt ein Fehler in der URL vor. Bitte überprüfen Sie die URL und versuchen Sie es erneut.",
|
||||
"profile.viewMyRecords": "Meine Aufzeichnungen anzeigen",
|
||||
"profile.loading": "Profil lädt...",
|
||||
"profile.username.description": "Ihre Profilinformationen sind nur für Sie sichtbar. Nur Ihr Benutzername ist für andere auf {siteName} sichtbar."
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
{
|
||||
"profile.page.title": "Perfil | {siteName}",
|
||||
"profile.age.details": "Para compartir el perfil con otros {siteName} estudiantes, debe confirmar que es mayor de 13 años.",
|
||||
"profile.age.set.date": "Establece tu fecha de nacimiento",
|
||||
"profile.datejoined.member.since": "Miembro desde {year}",
|
||||
"profile.bio.empty": "Añade una breve biografía",
|
||||
"profile.bio.about.me": "Sobre Mí",
|
||||
"profile.certificate.organization.label": "Desde",
|
||||
"profile.certificate.completion.date.label": "Completado el {date}",
|
||||
"profile.no.certificates": "Todavía no ha obtenido ningún certificado.",
|
||||
"profile.certificates.my.certificates": "Mis Certificados",
|
||||
"profile.certificates.view.certificate": "Ver Certificado",
|
||||
"profile.certificates.types.verified": "Certificado verificado",
|
||||
"profile.certificates.types.professional": "Certificado profesional",
|
||||
"profile.certificates.types.unknown": "Certificado",
|
||||
"profile.country.label": "Ubicación",
|
||||
"profile.country.empty": "Añade ubicación",
|
||||
"profile.education.empty": "Añade Educación",
|
||||
"profile.education.education": "Educación",
|
||||
"profile.education.levels.p": "Doctorado",
|
||||
"profile.education.levels.m": "Master o magíster",
|
||||
"profile.education.levels.b": "Pregrado o Licenciatura",
|
||||
"profile.education.levels.a": "Grado técnico - tecnológico",
|
||||
"profile.education.levels.hs": "Enseñanza secundaria",
|
||||
"profile.education.levels.jhs": "Formación media",
|
||||
"profile.education.levels.el": "Enseñanza primaria",
|
||||
"profile.education.levels.none": "Ninguna educación formal",
|
||||
"profile.education.levels.o": "Otra educación",
|
||||
"profile.editbutton.edit": "Editar",
|
||||
"profile.formcontrols.who.can.see": "Quién puede ver esto:",
|
||||
"profile.formcontrols.button.cancel": "Cancelar",
|
||||
"profile.formcontrols.button.save": "Guardar",
|
||||
"profile.formcontrols.button.saving": "Guardando",
|
||||
"profile.formcontrols.button.saved": "Guardado",
|
||||
"profile.visibility.who.just.me": "Solo yo",
|
||||
"profile.visibility.who.everyone": "Todos en {siteName}",
|
||||
"profile.learningGoal.learningGoal": "Objetivo de aprendizaje",
|
||||
"profile.learningGoal.options.start_career": "quiero empezar mi carrera",
|
||||
"profile.learningGoal.options.advance_career": "Quiero avanzar en mi carrera",
|
||||
"profile.learningGoal.options.learn_something_new": "quiero aprender algo nuevo",
|
||||
"profile.learningGoal.options.something_else": "Algo más",
|
||||
"profile.name.full.name": "Nombre completo",
|
||||
"profile.name.details": "Este es el nombre que aparecerá en tu cuenta y en tus certificados.",
|
||||
"profile.name.empty": "Añade nombre",
|
||||
"profile.preferredlanguage.empty": "Añadir idioma",
|
||||
"profile.preferredlanguage.label": "Idioma principal que hablas",
|
||||
"profile.profileavatar.upload-button": "Subir foto",
|
||||
"profile.profileavatar.remove.button": "Eliminar",
|
||||
"profile.image.alt.attribute": "avatar del perfil",
|
||||
"profile.profileavatar.change-button": "Cambiar",
|
||||
"profile.sociallinks.add": "Añade {network}",
|
||||
"profile.sociallinks.social.links": "Enlaces De Redes Sociales",
|
||||
"profile.notfound.message": "La página que estas buscando no está disponible o hay un error en la URL. Por favor, comprueba la URL y vuelve a intentarlo.",
|
||||
"profile.viewMyRecords": "Ver mis registros",
|
||||
"profile.loading": "Cargando perfil...",
|
||||
"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}."
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
{
|
||||
"profile.page.title": "پرونده کاربری {siteName}",
|
||||
"profile.age.details": "برای اشتراکگذاری پرونده کاربری خود با سایر یادگیرندگان {siteName}، باید تأیید کنید که بیش از 13 سال سن دارید.",
|
||||
"profile.age.set.date": "تنظیم تاریخ تولد",
|
||||
"profile.datejoined.member.since": "عضو شده از {year}",
|
||||
"profile.bio.empty": "بیوگرافی کوتاهی اضافه کنید",
|
||||
"profile.bio.about.me": "درباره من",
|
||||
"profile.certificate.organization.label": "از",
|
||||
"profile.certificate.completion.date.label": "تکمیل شده در {date}",
|
||||
"profile.no.certificates": "شما هنوز هیچ گواهی ندارید.",
|
||||
"profile.certificates.my.certificates": "گواهیهای من",
|
||||
"profile.certificates.view.certificate": "نمایش گواهی",
|
||||
"profile.certificates.types.verified": "گواهی تأییدشده",
|
||||
"profile.certificates.types.professional": "گواهی حرفهای",
|
||||
"profile.certificates.types.unknown": "گواهی",
|
||||
"profile.country.label": "مکان",
|
||||
"profile.country.empty": "افزودن مکان",
|
||||
"profile.education.empty": "افزودن تحصیلات",
|
||||
"profile.education.education": "تحصیلات",
|
||||
"profile.education.levels.p": "درجه دکتری",
|
||||
"profile.education.levels.m": "کارشناسی ارشد یا مدرک حرفهای",
|
||||
"profile.education.levels.b": "مدرک کارشناسی",
|
||||
"profile.education.levels.a": "مدرک کاردانی",
|
||||
"profile.education.levels.hs": "متوسطه/دبیرستان",
|
||||
"profile.education.levels.jhs": "مدرسه متوسطه دوره اول/ راهنمایی",
|
||||
"profile.education.levels.el": "مدرسه ابتدایی",
|
||||
"profile.education.levels.none": "بدون تحصیلات رسمی",
|
||||
"profile.education.levels.o": "تحصیلات متفرقه",
|
||||
"profile.editbutton.edit": " ویرایش",
|
||||
"profile.formcontrols.who.can.see": "کسانی که میتوانند این را ببینند:",
|
||||
"profile.formcontrols.button.cancel": "لغو",
|
||||
"profile.formcontrols.button.save": "ذخیره",
|
||||
"profile.formcontrols.button.saving": "در حال ذخیره",
|
||||
"profile.formcontrols.button.saved": "ذخیره شد",
|
||||
"profile.visibility.who.just.me": "فقط من",
|
||||
"profile.visibility.who.everyone": "هرکسی در {siteName}",
|
||||
"profile.learningGoal.learningGoal": "هدف یادگیری",
|
||||
"profile.learningGoal.options.start_career": "من می خواهم کارم را شروع کنم",
|
||||
"profile.learningGoal.options.advance_career": "من می خواهم حرفه ام را ارتقا دهم",
|
||||
"profile.learningGoal.options.learn_something_new": "میخواهم چیز جدیدی یاد بگیرم",
|
||||
"profile.learningGoal.options.something_else": "یک چیز دیگر",
|
||||
"profile.name.full.name": "نام و نام خانوادگی",
|
||||
"profile.name.details": "این همان نامی است که در حساب کاربری و گواهیهای شما درج میشود.",
|
||||
"profile.name.empty": "افزودن نام",
|
||||
"profile.preferredlanguage.empty": "افزودن زبان",
|
||||
"profile.preferredlanguage.label": "زبان اصلی صحبت شده",
|
||||
"profile.profileavatar.upload-button": "بارگذاری عکس",
|
||||
"profile.profileavatar.remove.button": "حذف",
|
||||
"profile.image.alt.attribute": "چهرک پرونده کاربری",
|
||||
"profile.profileavatar.change-button": "تغییر",
|
||||
"profile.sociallinks.add": "افزودن {network}",
|
||||
"profile.sociallinks.social.links": "پیوندهای رسانه اجتماعی",
|
||||
"profile.notfound.message": "صفحه مورد نظر شما در دسترس نیست یا خطایی در نشانی آن وجود دارد. لطفاً نشانی اینترنتی را بررسی کرده و دوباره امتحان کنید.",
|
||||
"profile.viewMyRecords": "مشاهده سوابق من",
|
||||
"profile.loading": "در حال بارگذاری پرونده کاربری...",
|
||||
"profile.username.description": "اطلاعات پرونده کاربری فقط برای شما قابل مشاهده است. سایرین فقط نام کاربری شما را در {siteName} میتوانند ببینند."
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
{
|
||||
"profile.page.title": "Profile | {siteName}",
|
||||
"profile.age.details": "Pour partager votre profil avec d'autres étudiants {siteName}, vous devez confirmer que vous avez plus de 13 ans.",
|
||||
"profile.age.set.date": "Définissez votre date de naissance",
|
||||
"profile.datejoined.member.since": "Membre depuis {year}",
|
||||
"profile.bio.empty": "Ajouter une courte biographie",
|
||||
"profile.bio.about.me": "À propos de moi",
|
||||
"profile.certificate.organization.label": "De",
|
||||
"profile.certificate.completion.date.label": "Terminé le {date}",
|
||||
"profile.no.certificates": "Vous n'avez pas encore de certificats.",
|
||||
"profile.certificates.my.certificates": "Mes certificats",
|
||||
"profile.certificates.view.certificate": "Voir le certificat",
|
||||
"profile.certificates.types.verified": "Certificat vérifié",
|
||||
"profile.certificates.types.professional": "Certificat professionnel",
|
||||
"profile.certificates.types.unknown": "Certificat",
|
||||
"profile.country.label": "Localisation",
|
||||
"profile.country.empty": "Ajouter localisation",
|
||||
"profile.education.empty": "Ajouter une éducation",
|
||||
"profile.education.education": "Education",
|
||||
"profile.education.levels.p": "Doctorat",
|
||||
"profile.education.levels.m": "Master ou diplôme professionnel",
|
||||
"profile.education.levels.b": "Diplôme de licence",
|
||||
"profile.education.levels.a": "Grade de l'associé",
|
||||
"profile.education.levels.hs": "Lycée / enseignement secondaire",
|
||||
"profile.education.levels.jhs": "Collège / enseignement secondaire inférieur",
|
||||
"profile.education.levels.el": "Enseignement primaire",
|
||||
"profile.education.levels.none": "Sans diplôme",
|
||||
"profile.education.levels.o": "Autre niveau d'étude",
|
||||
"profile.editbutton.edit": "Modifier",
|
||||
"profile.formcontrols.who.can.see": "Qui peut voir ça :",
|
||||
"profile.formcontrols.button.cancel": "Annuler",
|
||||
"profile.formcontrols.button.save": "Enregistrer",
|
||||
"profile.formcontrols.button.saving": "Enregistrement",
|
||||
"profile.formcontrols.button.saved": "Enregistré",
|
||||
"profile.visibility.who.just.me": "Juste moi",
|
||||
"profile.visibility.who.everyone": "Tout le monde sur {siteName}",
|
||||
"profile.learningGoal.learningGoal": "Learning Goal",
|
||||
"profile.learningGoal.options.start_career": "I want to start my career",
|
||||
"profile.learningGoal.options.advance_career": "I want to advance my career",
|
||||
"profile.learningGoal.options.learn_something_new": "I want to learn something new",
|
||||
"profile.learningGoal.options.something_else": "Something else",
|
||||
"profile.name.full.name": "Nom complet",
|
||||
"profile.name.details": "C'est le nom qui apparaît dans votre compte et sur vos certificats.",
|
||||
"profile.name.empty": "Ajouter un nom",
|
||||
"profile.preferredlanguage.empty": "Ajouter une langue",
|
||||
"profile.preferredlanguage.label": "Langue principale parlée",
|
||||
"profile.profileavatar.upload-button": "Envoyer la photo",
|
||||
"profile.profileavatar.remove.button": "Supprimer",
|
||||
"profile.image.alt.attribute": "Profil avatar",
|
||||
"profile.profileavatar.change-button": "Modifier",
|
||||
"profile.sociallinks.add": "Ajouter {network}",
|
||||
"profile.sociallinks.social.links": "Liens vers les réseaux sociaux",
|
||||
"profile.notfound.message": "La page que vous recherchez n'est pas disponible ou il y a une erreur dans l'URL. Veuillez vérifier l'URL et réessayer.",
|
||||
"profile.viewMyRecords": "Voir mes succès",
|
||||
"profile.loading": "Chargement du profil....",
|
||||
"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}."
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
{
|
||||
"profile.page.title": "Profil | {siteName}",
|
||||
"profile.age.details": "Pour partager votre profil avec d'autres apprenants {siteName}, vous devez confirmer que vous avez plus de 13 ans.",
|
||||
"profile.age.set.date": "Entrez votre date de naissance",
|
||||
"profile.datejoined.member.since": "Membre depuis {year}",
|
||||
"profile.bio.empty": "Ajouter une courte biographie",
|
||||
"profile.bio.about.me": "À propos de moi",
|
||||
"profile.certificate.organization.label": "De",
|
||||
"profile.certificate.completion.date.label": "Terminé le {date}",
|
||||
"profile.no.certificates": "Vous n'avez pas encore d'attestation.",
|
||||
"profile.certificates.my.certificates": "Mes Attestations",
|
||||
"profile.certificates.view.certificate": "Voir votre attestation",
|
||||
"profile.certificates.types.verified": "Attestation vérifiée",
|
||||
"profile.certificates.types.professional": "Attestation professionnelle",
|
||||
"profile.certificates.types.unknown": "Attestation",
|
||||
"profile.country.label": "Adresse",
|
||||
"profile.country.empty": "Ajouter un emplacement",
|
||||
"profile.education.empty": "Ajouter formation",
|
||||
"profile.education.education": "Formation",
|
||||
"profile.education.levels.p": "Doctorat",
|
||||
"profile.education.levels.m": "Maîtrise ou diplôme professionnel",
|
||||
"profile.education.levels.b": "Diplôme de baccalauréat",
|
||||
"profile.education.levels.a": "Diplôme d'associé",
|
||||
"profile.education.levels.hs": "Lycée / enseignement secondaire",
|
||||
"profile.education.levels.jhs": "Collège / enseignement secondaire inférieur",
|
||||
"profile.education.levels.el": "Enseignement primaire",
|
||||
"profile.education.levels.none": "Sans formation formelle",
|
||||
"profile.education.levels.o": "Autre niveau de formation",
|
||||
"profile.editbutton.edit": "Éditer",
|
||||
"profile.formcontrols.who.can.see": "Qui peut voir ça :",
|
||||
"profile.formcontrols.button.cancel": "Annuler",
|
||||
"profile.formcontrols.button.save": "Sauvegarder",
|
||||
"profile.formcontrols.button.saving": "Sauvegarde en cours",
|
||||
"profile.formcontrols.button.saved": "Sauvegardé",
|
||||
"profile.visibility.who.just.me": "Juste moi",
|
||||
"profile.visibility.who.everyone": "Tout le monde sur {siteName}",
|
||||
"profile.learningGoal.learningGoal": "Objectif d'apprentissage",
|
||||
"profile.learningGoal.options.start_career": "Je veux commencer ma carrière",
|
||||
"profile.learningGoal.options.advance_career": "Je veux faire progresser ma carrière",
|
||||
"profile.learningGoal.options.learn_something_new": "Je veux apprendre quelque chose de nouveau",
|
||||
"profile.learningGoal.options.something_else": "Autre chose",
|
||||
"profile.name.full.name": "Nom complet",
|
||||
"profile.name.details": "C'est le nom qui apparaît dans votre compte et sur vos attestations.",
|
||||
"profile.name.empty": "Ajouter un nom",
|
||||
"profile.preferredlanguage.empty": "Ajouter une langue",
|
||||
"profile.preferredlanguage.label": "Langue principale parlée",
|
||||
"profile.profileavatar.upload-button": "Téléverser une photo",
|
||||
"profile.profileavatar.remove.button": "Retirer",
|
||||
"profile.image.alt.attribute": "avatar de profil",
|
||||
"profile.profileavatar.change-button": "Modifier",
|
||||
"profile.sociallinks.add": "Ajouter {network}",
|
||||
"profile.sociallinks.social.links": "Liens vers les réseaux sociaux",
|
||||
"profile.notfound.message": "La page que vous recherchez n'est pas disponible ou il y a une erreur dans l'URL. Veuillez vérifier l'URL et réessayer.",
|
||||
"profile.viewMyRecords": "Afficher mes dossiers",
|
||||
"profile.loading": "Chargement du profil...",
|
||||
"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}."
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
{
|
||||
"profile.page.title": "Profile | {siteName}",
|
||||
"profile.age.details": "To share your profile with other {siteName} learners, you must confirm that you are over the age of 13.",
|
||||
"profile.age.set.date": "Set your date of birth",
|
||||
"profile.datejoined.member.since": "Member since {year}",
|
||||
"profile.bio.empty": "Add a short bio",
|
||||
"profile.bio.about.me": "About Me",
|
||||
"profile.certificate.organization.label": "From",
|
||||
"profile.certificate.completion.date.label": "Completed on {date}",
|
||||
"profile.no.certificates": "You don't have any certificates yet.",
|
||||
"profile.certificates.my.certificates": "My Certificates",
|
||||
"profile.certificates.view.certificate": "View Certificate",
|
||||
"profile.certificates.types.verified": "Verified Certificate",
|
||||
"profile.certificates.types.professional": "Professional Certificate",
|
||||
"profile.certificates.types.unknown": "Certificate",
|
||||
"profile.country.label": "Location",
|
||||
"profile.country.empty": "Add location",
|
||||
"profile.education.empty": "Add education",
|
||||
"profile.education.education": "Education",
|
||||
"profile.education.levels.p": "Doctorate",
|
||||
"profile.education.levels.m": "Master's or professional degree",
|
||||
"profile.education.levels.b": "Bachelor's Degree",
|
||||
"profile.education.levels.a": "Associate's degree",
|
||||
"profile.education.levels.hs": "Secondary/high school",
|
||||
"profile.education.levels.jhs": "Junior secondary/junior high/middle school",
|
||||
"profile.education.levels.el": "Elementary/primary school",
|
||||
"profile.education.levels.none": "No formal education",
|
||||
"profile.education.levels.o": "Other education",
|
||||
"profile.editbutton.edit": "Edit",
|
||||
"profile.formcontrols.who.can.see": "Who can see this:",
|
||||
"profile.formcontrols.button.cancel": "Cancel",
|
||||
"profile.formcontrols.button.save": "Save",
|
||||
"profile.formcontrols.button.saving": "Saving",
|
||||
"profile.formcontrols.button.saved": "Saved",
|
||||
"profile.visibility.who.just.me": "Just me",
|
||||
"profile.visibility.who.everyone": "Everyone on {siteName}",
|
||||
"profile.learningGoal.learningGoal": "Learning Goal",
|
||||
"profile.learningGoal.options.start_career": "I want to start my career",
|
||||
"profile.learningGoal.options.advance_career": "I want to advance my career",
|
||||
"profile.learningGoal.options.learn_something_new": "I want to learn something new",
|
||||
"profile.learningGoal.options.something_else": "Something else",
|
||||
"profile.name.full.name": "Full Name",
|
||||
"profile.name.details": "This is the name that appears in your account and on your certificates.",
|
||||
"profile.name.empty": "Add name",
|
||||
"profile.preferredlanguage.empty": "Add language",
|
||||
"profile.preferredlanguage.label": "Primary Language Spoken",
|
||||
"profile.profileavatar.upload-button": "Upload Photo",
|
||||
"profile.profileavatar.remove.button": "Remove",
|
||||
"profile.image.alt.attribute": "profile avatar",
|
||||
"profile.profileavatar.change-button": "Change",
|
||||
"profile.sociallinks.add": "Add {network}",
|
||||
"profile.sociallinks.social.links": "Social Links",
|
||||
"profile.notfound.message": "The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again.",
|
||||
"profile.viewMyRecords": "View My Records",
|
||||
"profile.loading": "Profile loading...",
|
||||
"profile.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}."
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
{
|
||||
"profile.page.title": "Profile | {siteName}",
|
||||
"profile.age.details": "To share your profile with other {siteName} learners, you must confirm that you are over the age of 13.",
|
||||
"profile.age.set.date": "Set your date of birth",
|
||||
"profile.datejoined.member.since": "Member since {year}",
|
||||
"profile.bio.empty": "Add a short bio",
|
||||
"profile.bio.about.me": "About Me",
|
||||
"profile.certificate.organization.label": "From",
|
||||
"profile.certificate.completion.date.label": "Completed on {date}",
|
||||
"profile.no.certificates": "You don't have any certificates yet.",
|
||||
"profile.certificates.my.certificates": "My Certificates",
|
||||
"profile.certificates.view.certificate": "View Certificate",
|
||||
"profile.certificates.types.verified": "Verified Certificate",
|
||||
"profile.certificates.types.professional": "Professional Certificate",
|
||||
"profile.certificates.types.unknown": "Certificate",
|
||||
"profile.country.label": "Location",
|
||||
"profile.country.empty": "Add location",
|
||||
"profile.education.empty": "Add education",
|
||||
"profile.education.education": "Education",
|
||||
"profile.education.levels.p": "Doctorate",
|
||||
"profile.education.levels.m": "Master's or professional degree",
|
||||
"profile.education.levels.b": "Bachelor's Degree",
|
||||
"profile.education.levels.a": "Associate's degree",
|
||||
"profile.education.levels.hs": "Secondary/high school",
|
||||
"profile.education.levels.jhs": "Junior secondary/junior high/middle school",
|
||||
"profile.education.levels.el": "Elementary/primary school",
|
||||
"profile.education.levels.none": "No formal education",
|
||||
"profile.education.levels.o": "Other education",
|
||||
"profile.editbutton.edit": "Edit",
|
||||
"profile.formcontrols.who.can.see": "Who can see this:",
|
||||
"profile.formcontrols.button.cancel": "Cancel",
|
||||
"profile.formcontrols.button.save": "Save",
|
||||
"profile.formcontrols.button.saving": "Saving",
|
||||
"profile.formcontrols.button.saved": "Saved",
|
||||
"profile.visibility.who.just.me": "Just me",
|
||||
"profile.visibility.who.everyone": "Everyone on {siteName}",
|
||||
"profile.learningGoal.learningGoal": "Learning Goal",
|
||||
"profile.learningGoal.options.start_career": "I want to start my career",
|
||||
"profile.learningGoal.options.advance_career": "I want to advance my career",
|
||||
"profile.learningGoal.options.learn_something_new": "I want to learn something new",
|
||||
"profile.learningGoal.options.something_else": "Something else",
|
||||
"profile.name.full.name": "Full Name",
|
||||
"profile.name.details": "This is the name that appears in your account and on your certificates.",
|
||||
"profile.name.empty": "Add name",
|
||||
"profile.preferredlanguage.empty": "Add language",
|
||||
"profile.preferredlanguage.label": "Primary Language Spoken",
|
||||
"profile.profileavatar.upload-button": "Upload Photo",
|
||||
"profile.profileavatar.remove.button": "Remove",
|
||||
"profile.image.alt.attribute": "profile avatar",
|
||||
"profile.profileavatar.change-button": "Change",
|
||||
"profile.sociallinks.add": "Add {network}",
|
||||
"profile.sociallinks.social.links": "Social Links",
|
||||
"profile.notfound.message": "The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again.",
|
||||
"profile.viewMyRecords": "View My Records",
|
||||
"profile.loading": "Profile loading...",
|
||||
"profile.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}."
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
{
|
||||
"profile.page.title": "Profilo | {siteName}",
|
||||
"profile.age.details": "Per condividere il tuo profilo con altri studenti di {siteName}, devi confermare di avere più di 13 anni.",
|
||||
"profile.age.set.date": "Imposta la data di nascita ",
|
||||
"profile.datejoined.member.since": "Membro da {year}",
|
||||
"profile.bio.empty": "Aggiungi una breve biografia ",
|
||||
"profile.bio.about.me": "Su di me",
|
||||
"profile.certificate.organization.label": "Da ",
|
||||
"profile.certificate.completion.date.label": "Completato il {date}",
|
||||
"profile.no.certificates": "Non si dispone ancora di alcun certificato. ",
|
||||
"profile.certificates.my.certificates": "Certificati personali ",
|
||||
"profile.certificates.view.certificate": "Visualizza il certificato",
|
||||
"profile.certificates.types.verified": "Certificato Verificato",
|
||||
"profile.certificates.types.professional": "Certificato professionale ",
|
||||
"profile.certificates.types.unknown": "Certificato ",
|
||||
"profile.country.label": "Posizione",
|
||||
"profile.country.empty": "Aggiungi posizione ",
|
||||
"profile.education.empty": "Aggiungi titolo di studio ",
|
||||
"profile.education.education": "Educazione",
|
||||
"profile.education.levels.p": "Dottorato",
|
||||
"profile.education.levels.m": "Laurea magistrale o titolo accademico professionale",
|
||||
"profile.education.levels.b": "Laurea di primo livello ",
|
||||
"profile.education.levels.a": "Diploma Professionale",
|
||||
"profile.education.levels.hs": "Scuole superiori/liceo",
|
||||
"profile.education.levels.jhs": "Scuole Medie",
|
||||
"profile.education.levels.el": "Scuola Primaria/Elementare",
|
||||
"profile.education.levels.none": "Nessun livello educativo formale",
|
||||
"profile.education.levels.o": "Altro livello educativo",
|
||||
"profile.editbutton.edit": "Modifica",
|
||||
"profile.formcontrols.who.can.see": "Chi può visualizzare: ",
|
||||
"profile.formcontrols.button.cancel": "Annulla",
|
||||
"profile.formcontrols.button.save": "Salva",
|
||||
"profile.formcontrols.button.saving": "Salvataggio in corso",
|
||||
"profile.formcontrols.button.saved": "Salvato",
|
||||
"profile.visibility.who.just.me": "Solo io ",
|
||||
"profile.visibility.who.everyone": "Tutti su {siteName}",
|
||||
"profile.learningGoal.learningGoal": "Obiettivo di apprendimento",
|
||||
"profile.learningGoal.options.start_career": "Voglio iniziare il mio percorso",
|
||||
"profile.learningGoal.options.advance_career": "Voglio avanzare nel mio percorso",
|
||||
"profile.learningGoal.options.learn_something_new": "Voglio imparare qualcosa di nuovo",
|
||||
"profile.learningGoal.options.something_else": "Qualcos'altro",
|
||||
"profile.name.full.name": "Nome e Cognome",
|
||||
"profile.name.details": "Questo è il nome visualizzato nel proprio account e nei propri certificati. ",
|
||||
"profile.name.empty": "Aggiungi nome ",
|
||||
"profile.preferredlanguage.empty": "Aggiungi lingua",
|
||||
"profile.preferredlanguage.label": "Lingua principale ",
|
||||
"profile.profileavatar.upload-button": "Carica foto",
|
||||
"profile.profileavatar.remove.button": "Rimuovi",
|
||||
"profile.image.alt.attribute": "avatar del profilo ",
|
||||
"profile.profileavatar.change-button": "Cambia",
|
||||
"profile.sociallinks.add": "Aggiungi {network}",
|
||||
"profile.sociallinks.social.links": "Link social ",
|
||||
"profile.notfound.message": "La pagina ricercata non è disponibile oppure è presente un errore nell'URL. Controllare l'URL e riprovare. ",
|
||||
"profile.viewMyRecords": "Visualizza record personali ",
|
||||
"profile.loading": "Caricamento del profilo... ",
|
||||
"profile.username.description": "Le informazioni del tuo profilo sono visibili solo a te. Solo il tuo nome utente è visibile agli altri su {siteName}."
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
{
|
||||
"profile.page.title": "Profile | {siteName}",
|
||||
"profile.age.details": "To share your profile with other {siteName} learners, you must confirm that you are over the age of 13.",
|
||||
"profile.age.set.date": "Set your date of birth",
|
||||
"profile.datejoined.member.since": "Member since {year}",
|
||||
"profile.bio.empty": "Add a short bio",
|
||||
"profile.bio.about.me": "About Me",
|
||||
"profile.certificate.organization.label": "From",
|
||||
"profile.certificate.completion.date.label": "Completed on {date}",
|
||||
"profile.no.certificates": "You don't have any certificates yet.",
|
||||
"profile.certificates.my.certificates": "My Certificates",
|
||||
"profile.certificates.view.certificate": "View Certificate",
|
||||
"profile.certificates.types.verified": "Verified Certificate",
|
||||
"profile.certificates.types.professional": "Professional Certificate",
|
||||
"profile.certificates.types.unknown": "Certificate",
|
||||
"profile.country.label": "Location",
|
||||
"profile.country.empty": "Add location",
|
||||
"profile.education.empty": "Add education",
|
||||
"profile.education.education": "Education",
|
||||
"profile.education.levels.p": "Doctorate",
|
||||
"profile.education.levels.m": "Master's or professional degree",
|
||||
"profile.education.levels.b": "Bachelor's Degree",
|
||||
"profile.education.levels.a": "Associate's degree",
|
||||
"profile.education.levels.hs": "Secondary/high school",
|
||||
"profile.education.levels.jhs": "Junior secondary/junior high/middle school",
|
||||
"profile.education.levels.el": "Elementary/primary school",
|
||||
"profile.education.levels.none": "No formal education",
|
||||
"profile.education.levels.o": "Other education",
|
||||
"profile.editbutton.edit": "Edit",
|
||||
"profile.formcontrols.who.can.see": "Who can see this:",
|
||||
"profile.formcontrols.button.cancel": "Cancel",
|
||||
"profile.formcontrols.button.save": "Save",
|
||||
"profile.formcontrols.button.saving": "Saving",
|
||||
"profile.formcontrols.button.saved": "Saved",
|
||||
"profile.visibility.who.just.me": "Just me",
|
||||
"profile.visibility.who.everyone": "Everyone on {siteName}",
|
||||
"profile.learningGoal.learningGoal": "Learning Goal",
|
||||
"profile.learningGoal.options.start_career": "I want to start my career",
|
||||
"profile.learningGoal.options.advance_career": "I want to advance my career",
|
||||
"profile.learningGoal.options.learn_something_new": "I want to learn something new",
|
||||
"profile.learningGoal.options.something_else": "Something else",
|
||||
"profile.name.full.name": "Full Name",
|
||||
"profile.name.details": "This is the name that appears in your account and on your certificates.",
|
||||
"profile.name.empty": "Add name",
|
||||
"profile.preferredlanguage.empty": "Add language",
|
||||
"profile.preferredlanguage.label": "Primary Language Spoken",
|
||||
"profile.profileavatar.upload-button": "Upload Photo",
|
||||
"profile.profileavatar.remove.button": "Remove",
|
||||
"profile.image.alt.attribute": "profile avatar",
|
||||
"profile.profileavatar.change-button": "Change",
|
||||
"profile.sociallinks.add": "Add {network}",
|
||||
"profile.sociallinks.social.links": "Social Links",
|
||||
"profile.notfound.message": "The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again.",
|
||||
"profile.viewMyRecords": "View My Records",
|
||||
"profile.loading": "Profile loading...",
|
||||
"profile.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}."
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
{
|
||||
"profile.page.title": "Perfil | {siteName}",
|
||||
"profile.age.details": "Para partilhar o seu perfil com outros estudantes da plataforma {siteName}, tem de confirmar que tem mais de 13 anos de idade.",
|
||||
"profile.age.set.date": "Indique a sua data de nascimento",
|
||||
"profile.datejoined.member.since": "Utilizador desde {year}",
|
||||
"profile.bio.empty": "Adicionar uma breve biografia",
|
||||
"profile.bio.about.me": "Sobre Mim",
|
||||
"profile.certificate.organization.label": "De",
|
||||
"profile.certificate.completion.date.label": "Concluído a {date}",
|
||||
"profile.no.certificates": "Ainda não tem certificados.",
|
||||
"profile.certificates.my.certificates": "Os Meus Certificados",
|
||||
"profile.certificates.view.certificate": "Ver Certificado",
|
||||
"profile.certificates.types.verified": "Certificado Validado",
|
||||
"profile.certificates.types.professional": "Certificado Profissional",
|
||||
"profile.certificates.types.unknown": "Certificado",
|
||||
"profile.country.label": "Localização",
|
||||
"profile.country.empty": "Adicionar localização",
|
||||
"profile.education.empty": "Adicionar grau de escolaridade",
|
||||
"profile.education.education": "Educação",
|
||||
"profile.education.levels.p": "Doutoramento",
|
||||
"profile.education.levels.m": "Mestrado ou Grau Profissional",
|
||||
"profile.education.levels.b": "Licenciatura",
|
||||
"profile.education.levels.a": "Pós-graduação",
|
||||
"profile.education.levels.hs": "Secundário",
|
||||
"profile.education.levels.jhs": "2ªciclo/3ºciclo",
|
||||
"profile.education.levels.el": "Primária",
|
||||
"profile.education.levels.none": "Sem estudos",
|
||||
"profile.education.levels.o": "Outra formação",
|
||||
"profile.editbutton.edit": "Editar",
|
||||
"profile.formcontrols.who.can.see": "Quem pode ver isto:",
|
||||
"profile.formcontrols.button.cancel": "Cancelar",
|
||||
"profile.formcontrols.button.save": "Guardar",
|
||||
"profile.formcontrols.button.saving": "A Guardar",
|
||||
"profile.formcontrols.button.saved": "Guardado",
|
||||
"profile.visibility.who.just.me": "Apenas eu",
|
||||
"profile.visibility.who.everyone": "Toda a gente em {siteName}",
|
||||
"profile.learningGoal.learningGoal": "Objectivo de aprendizagem",
|
||||
"profile.learningGoal.options.start_career": "Quero começar a minha carreira",
|
||||
"profile.learningGoal.options.advance_career": "Quero progredir na minha carreira",
|
||||
"profile.learningGoal.options.learn_something_new": "Quero aprender algo novo",
|
||||
"profile.learningGoal.options.something_else": "Outra coisa",
|
||||
"profile.name.full.name": "Nome Completo",
|
||||
"profile.name.details": "Este é o nome que aparece na sua conta e nos seus certificados.",
|
||||
"profile.name.empty": "Adicionar nome",
|
||||
"profile.preferredlanguage.empty": "Adicionar idioma",
|
||||
"profile.preferredlanguage.label": "Língua Materna",
|
||||
"profile.profileavatar.upload-button": "Carregar Fotografia",
|
||||
"profile.profileavatar.remove.button": "Eliminar",
|
||||
"profile.image.alt.attribute": "Icon de perfil",
|
||||
"profile.profileavatar.change-button": "Alterar",
|
||||
"profile.sociallinks.add": "Adicionar {network}",
|
||||
"profile.sociallinks.social.links": "Links de Redes Sociais",
|
||||
"profile.notfound.message": "A página que procura não está disponível ou há um erro no URL. Por favor, verifique o URL e tente novamente.",
|
||||
"profile.viewMyRecords": "Ver os Meus Registos",
|
||||
"profile.loading": "A carregar perfil...",
|
||||
"profile.username.description": "As informações do seu perfil só são visíveis para si. Apenas o seu nome de utilizador é visível para outros em {siteName}."
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
{
|
||||
"profile.page.title": "Profile | {siteName}",
|
||||
"profile.age.details": "To share your profile with other {siteName} learners, you must confirm that you are over the age of 13.",
|
||||
"profile.age.set.date": "Set your date of birth",
|
||||
"profile.datejoined.member.since": "Member since {year}",
|
||||
"profile.bio.empty": "Add a short bio",
|
||||
"profile.bio.about.me": "About Me",
|
||||
"profile.certificate.organization.label": "From",
|
||||
"profile.certificate.completion.date.label": "Completed on {date}",
|
||||
"profile.no.certificates": "You don't have any certificates yet.",
|
||||
"profile.certificates.my.certificates": "My Certificates",
|
||||
"profile.certificates.view.certificate": "View Certificate",
|
||||
"profile.certificates.types.verified": "Verified Certificate",
|
||||
"profile.certificates.types.professional": "Professional Certificate",
|
||||
"profile.certificates.types.unknown": "Certificate",
|
||||
"profile.country.label": "Location",
|
||||
"profile.country.empty": "Add location",
|
||||
"profile.education.empty": "Add education",
|
||||
"profile.education.education": "Education",
|
||||
"profile.education.levels.p": "Doctorate",
|
||||
"profile.education.levels.m": "Master's or professional degree",
|
||||
"profile.education.levels.b": "Bachelor's Degree",
|
||||
"profile.education.levels.a": "Associate's degree",
|
||||
"profile.education.levels.hs": "Secondary/high school",
|
||||
"profile.education.levels.jhs": "Junior secondary/junior high/middle school",
|
||||
"profile.education.levels.el": "Elementary/primary school",
|
||||
"profile.education.levels.none": "No formal education",
|
||||
"profile.education.levels.o": "Other education",
|
||||
"profile.editbutton.edit": "Edit",
|
||||
"profile.formcontrols.who.can.see": "Who can see this:",
|
||||
"profile.formcontrols.button.cancel": "Cancel",
|
||||
"profile.formcontrols.button.save": "Save",
|
||||
"profile.formcontrols.button.saving": "Saving",
|
||||
"profile.formcontrols.button.saved": "Saved",
|
||||
"profile.visibility.who.just.me": "Just me",
|
||||
"profile.visibility.who.everyone": "Everyone on {siteName}",
|
||||
"profile.learningGoal.learningGoal": "Learning Goal",
|
||||
"profile.learningGoal.options.start_career": "I want to start my career",
|
||||
"profile.learningGoal.options.advance_career": "I want to advance my career",
|
||||
"profile.learningGoal.options.learn_something_new": "I want to learn something new",
|
||||
"profile.learningGoal.options.something_else": "Something else",
|
||||
"profile.name.full.name": "Full Name",
|
||||
"profile.name.details": "This is the name that appears in your account and on your certificates.",
|
||||
"profile.name.empty": "Add name",
|
||||
"profile.preferredlanguage.empty": "Add language",
|
||||
"profile.preferredlanguage.label": "Primary Language Spoken",
|
||||
"profile.profileavatar.upload-button": "Upload Photo",
|
||||
"profile.profileavatar.remove.button": "Remove",
|
||||
"profile.image.alt.attribute": "profile avatar",
|
||||
"profile.profileavatar.change-button": "Change",
|
||||
"profile.sociallinks.add": "Add {network}",
|
||||
"profile.sociallinks.social.links": "Social Links",
|
||||
"profile.notfound.message": "The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again.",
|
||||
"profile.viewMyRecords": "View My Records",
|
||||
"profile.loading": "Profile loading...",
|
||||
"profile.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}."
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
{
|
||||
"profile.page.title": "Profile | {siteName}",
|
||||
"profile.age.details": "To share your profile with other {siteName} learners, you must confirm that you are over the age of 13.",
|
||||
"profile.age.set.date": "Set your date of birth",
|
||||
"profile.datejoined.member.since": "Member since {year}",
|
||||
"profile.bio.empty": "Add a short bio",
|
||||
"profile.bio.about.me": "About Me",
|
||||
"profile.certificate.organization.label": "From",
|
||||
"profile.certificate.completion.date.label": "Completed on {date}",
|
||||
"profile.no.certificates": "You don't have any certificates yet.",
|
||||
"profile.certificates.my.certificates": "Мої сертифікати",
|
||||
"profile.certificates.view.certificate": "View Certificate",
|
||||
"profile.certificates.types.verified": "Verified Certificate",
|
||||
"profile.certificates.types.professional": "Professional Certificate",
|
||||
"profile.certificates.types.unknown": "Certificate",
|
||||
"profile.country.label": "Location",
|
||||
"profile.country.empty": "Add location",
|
||||
"profile.education.empty": "Add education",
|
||||
"profile.education.education": "Education",
|
||||
"profile.education.levels.p": "Doctorate",
|
||||
"profile.education.levels.m": "Master's or professional degree",
|
||||
"profile.education.levels.b": "Bachelor's Degree",
|
||||
"profile.education.levels.a": "Associate's degree",
|
||||
"profile.education.levels.hs": "Secondary/high school",
|
||||
"profile.education.levels.jhs": "Junior secondary/junior high/middle school",
|
||||
"profile.education.levels.el": "Elementary/primary school",
|
||||
"profile.education.levels.none": "No formal education",
|
||||
"profile.education.levels.o": "Other education",
|
||||
"profile.editbutton.edit": "Edit",
|
||||
"profile.formcontrols.who.can.see": "Who can see this:",
|
||||
"profile.formcontrols.button.cancel": "Cancel",
|
||||
"profile.formcontrols.button.save": "Save",
|
||||
"profile.formcontrols.button.saving": "Saving",
|
||||
"profile.formcontrols.button.saved": "Saved",
|
||||
"profile.visibility.who.just.me": "Just me",
|
||||
"profile.visibility.who.everyone": "Everyone on {siteName}",
|
||||
"profile.learningGoal.learningGoal": "Learning Goal",
|
||||
"profile.learningGoal.options.start_career": "I want to start my career",
|
||||
"profile.learningGoal.options.advance_career": "I want to advance my career",
|
||||
"profile.learningGoal.options.learn_something_new": "I want to learn something new",
|
||||
"profile.learningGoal.options.something_else": "Something else",
|
||||
"profile.name.full.name": "Full Name",
|
||||
"profile.name.details": "This is the name that appears in your account and on your certificates.",
|
||||
"profile.name.empty": "Add name",
|
||||
"profile.preferredlanguage.empty": "Add language",
|
||||
"profile.preferredlanguage.label": "Primary Language Spoken",
|
||||
"profile.profileavatar.upload-button": "Upload Photo",
|
||||
"profile.profileavatar.remove.button": "Remove",
|
||||
"profile.image.alt.attribute": "profile avatar",
|
||||
"profile.profileavatar.change-button": "Change",
|
||||
"profile.sociallinks.add": "Add {network}",
|
||||
"profile.sociallinks.social.links": "Social Links",
|
||||
"profile.notfound.message": "The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again.",
|
||||
"profile.viewMyRecords": "View My Records",
|
||||
"profile.loading": "Profile loading...",
|
||||
"profile.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}."
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
{
|
||||
"profile.page.title": "Profile | {siteName}",
|
||||
"profile.age.details": "To share your profile with other {siteName} learners, you must confirm that you are over the age of 13.",
|
||||
"profile.age.set.date": "Set your date of birth",
|
||||
"profile.datejoined.member.since": "Member since {year}",
|
||||
"profile.bio.empty": "Add a short bio",
|
||||
"profile.bio.about.me": "About Me",
|
||||
"profile.certificate.organization.label": "From",
|
||||
"profile.certificate.completion.date.label": "Completed on {date}",
|
||||
"profile.no.certificates": "You don't have any certificates yet.",
|
||||
"profile.certificates.my.certificates": "My Certificates",
|
||||
"profile.certificates.view.certificate": "View Certificate",
|
||||
"profile.certificates.types.verified": "Verified Certificate",
|
||||
"profile.certificates.types.professional": "Professional Certificate",
|
||||
"profile.certificates.types.unknown": "Certificate",
|
||||
"profile.country.label": "Location",
|
||||
"profile.country.empty": "Add location",
|
||||
"profile.education.empty": "Add education",
|
||||
"profile.education.education": "Education",
|
||||
"profile.education.levels.p": "Doctorate",
|
||||
"profile.education.levels.m": "Master's or professional degree",
|
||||
"profile.education.levels.b": "Bachelor's Degree",
|
||||
"profile.education.levels.a": "Associate's degree",
|
||||
"profile.education.levels.hs": "Secondary/high school",
|
||||
"profile.education.levels.jhs": "Junior secondary/junior high/middle school",
|
||||
"profile.education.levels.el": "Elementary/primary school",
|
||||
"profile.education.levels.none": "No formal education",
|
||||
"profile.education.levels.o": "Other education",
|
||||
"profile.editbutton.edit": "Edit",
|
||||
"profile.formcontrols.who.can.see": "Who can see this:",
|
||||
"profile.formcontrols.button.cancel": "Cancel",
|
||||
"profile.formcontrols.button.save": "Save",
|
||||
"profile.formcontrols.button.saving": "Saving",
|
||||
"profile.formcontrols.button.saved": "Saved",
|
||||
"profile.visibility.who.just.me": "Just me",
|
||||
"profile.visibility.who.everyone": "Everyone on {siteName}",
|
||||
"profile.learningGoal.learningGoal": "Learning Goal",
|
||||
"profile.learningGoal.options.start_career": "I want to start my career",
|
||||
"profile.learningGoal.options.advance_career": "I want to advance my career",
|
||||
"profile.learningGoal.options.learn_something_new": "I want to learn something new",
|
||||
"profile.learningGoal.options.something_else": "Something else",
|
||||
"profile.name.full.name": "Full Name",
|
||||
"profile.name.details": "This is the name that appears in your account and on your certificates.",
|
||||
"profile.name.empty": "Add name",
|
||||
"profile.preferredlanguage.empty": "Add language",
|
||||
"profile.preferredlanguage.label": "Primary Language Spoken",
|
||||
"profile.profileavatar.upload-button": "Upload Photo",
|
||||
"profile.profileavatar.remove.button": "Remove",
|
||||
"profile.image.alt.attribute": "profile avatar",
|
||||
"profile.profileavatar.change-button": "Change",
|
||||
"profile.sociallinks.add": "Add {network}",
|
||||
"profile.sociallinks.social.links": "Social Links",
|
||||
"profile.notfound.message": "The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again.",
|
||||
"profile.viewMyRecords": "View My Records",
|
||||
"profile.loading": "Profile loading...",
|
||||
"profile.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}."
|
||||
}
|
||||
@@ -13,12 +13,12 @@ import {
|
||||
ErrorPage,
|
||||
} from '@edx/frontend-platform/react';
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import React, { StrictMode } from 'react';
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
import Header from '@edx/frontend-component-header';
|
||||
import Footer from '@edx/frontend-component-footer';
|
||||
import { FooterSlot } from '@edx/frontend-component-footer';
|
||||
|
||||
import messages from './i18n';
|
||||
import configureStore from './data/configureStore';
|
||||
@@ -28,32 +28,24 @@ import Head from './head/Head';
|
||||
|
||||
import AppRoutes from './routes/AppRoutes';
|
||||
|
||||
const RenderFooter = () => {
|
||||
const location = useLocation();
|
||||
return location.pathname.includes('/plugin') ? null : <Footer />;
|
||||
};
|
||||
|
||||
const RenderHeader = () => {
|
||||
const location = useLocation();
|
||||
return location.pathname.includes('/plugin') ? null : <Header />;
|
||||
};
|
||||
|
||||
const rootNode = createRoot(document.getElementById('root'));
|
||||
subscribe(APP_READY, () => {
|
||||
ReactDOM.render(
|
||||
<AppProvider store={configureStore()}>
|
||||
<Head />
|
||||
<RenderHeader />
|
||||
<main id="main">
|
||||
<AppRoutes />
|
||||
</main>
|
||||
<RenderFooter />
|
||||
</AppProvider>,
|
||||
document.getElementById('root'),
|
||||
rootNode.render(
|
||||
<StrictMode>
|
||||
<AppProvider store={configureStore()}>
|
||||
<Head />
|
||||
<Header />
|
||||
<main id="main">
|
||||
<AppRoutes />
|
||||
</main>
|
||||
<FooterSlot />
|
||||
</AppProvider>
|
||||
</StrictMode>,
|
||||
);
|
||||
});
|
||||
|
||||
subscribe(APP_INIT_ERROR, (error) => {
|
||||
ReactDOM.render(<ErrorPage message={error.message} />, document.getElementById('root'));
|
||||
rootNode.render(<ErrorPage message={error.message} />);
|
||||
});
|
||||
|
||||
initialize({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@import "~@edx/brand/paragon/fonts";
|
||||
@import "~@edx/brand/paragon/variables";
|
||||
@import "~@edx/paragon/scss/core/core";
|
||||
@import "~@openedx/paragon/scss/core/core";
|
||||
@import "~@edx/brand/paragon/overrides";
|
||||
@import "~@edx/frontend-component-header/dist/index";
|
||||
@import "~@edx/frontend-component-footer/dist/footer";
|
||||
|
||||
97
src/plugin-slots/AdditionalProfileFieldsSlot/README.md
Normal file
97
src/plugin-slots/AdditionalProfileFieldsSlot/README.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# Additional Profile Fields
|
||||
|
||||
### Slot ID: `org.openedx.frontend.profile.additional_profile_fields.v1`
|
||||
|
||||
## Description
|
||||
|
||||
This slot is used to replace/modify/hide the additional profile fields in the profile page.
|
||||
|
||||
## Example
|
||||
The following `env.config.jsx` will extend the default fields with a additional custom fields through a simple example component.
|
||||
|
||||

|
||||
|
||||
### Using the Additional Fields Component
|
||||
Create a file named `env.config.jsx` at the MFE root with this:
|
||||
|
||||
```jsx
|
||||
import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
|
||||
import Example from './src/plugin-slots/AdditionalProfileFieldsSlot/example';
|
||||
|
||||
const config = {
|
||||
pluginSlots: {
|
||||
'org.openedx.frontend.profile.additional_profile_fields.v1': {
|
||||
plugins: [
|
||||
{
|
||||
op: PLUGIN_OPERATIONS.Insert,
|
||||
widget: {
|
||||
id: 'additional_profile_fields',
|
||||
type: DIRECT_PLUGIN,
|
||||
RenderWidget: Example,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
```
|
||||
|
||||
## Plugin Props
|
||||
|
||||
When implementing a plugin for this slot, the following props are available:
|
||||
|
||||
### `updateUserProfile`
|
||||
- **Type**: Function
|
||||
- **Description**: A function for updating the user's profile with new field values. This handles the API call to persist changes to the backend.
|
||||
- **Usage**: Pass an object containing the field updates to be saved to the user's profile. The function automatically handles the persistence and UI updates.
|
||||
|
||||
#### Example
|
||||
```javascript
|
||||
updateUserProfile({ extendedProfile: [{ fieldName: 'favorite_color', fieldValue: value }] });
|
||||
```
|
||||
|
||||
### `profileFieldValues`
|
||||
- **Type**: Array of Objects
|
||||
- **Description**: Contains the current values of all additional profile fields as an array of objects. Each object has a `fieldName` property (string) and a `fieldValue` property (which can be string, boolean, number, or other data types depending on the field type).
|
||||
- **Usage**: Access specific field values by finding the object with the matching `fieldName` and reading its `fieldValue` property. Use array methods like `find()` to locate specific fields.
|
||||
|
||||
#### Example
|
||||
```javascript
|
||||
// Finding a specific field value
|
||||
const nifField = profileFieldValues.find(field => field.fieldName === 'nif');
|
||||
const nifValue = nifField ? nifField.fieldValue : null;
|
||||
|
||||
// Example data structure:
|
||||
[
|
||||
{
|
||||
"fieldName": "favorite_color",
|
||||
"fieldValue": "red"
|
||||
},
|
||||
{
|
||||
"fieldName": "employment_situation",
|
||||
"fieldValue": "Unemployed"
|
||||
},
|
||||
]
|
||||
```
|
||||
|
||||
### `profileFieldErrors`
|
||||
- **Type**: Object
|
||||
- **Description**: Contains validation errors for profile fields. Each key corresponds to a field name, and the value is the error message.
|
||||
- **Usage**: Check for field-specific errors to display validation feedback to users.
|
||||
|
||||
### `formComponents`
|
||||
- **Type**: Object
|
||||
- **Description**: Provides access to reusable form components that are consistent with the rest of the profile page styling and behavior. These components follow the platform's design system and include proper validation and accessibility features.
|
||||
- **Usage**: Use these components in your custom fields implementation to maintain UI consistency. Available components include `SwitchContent` for managing different UI states, `EmptyContent` for empty states, and `EditableItemHeader` for consistent headers.
|
||||
|
||||
### `refreshUserProfile`
|
||||
- **Type**: Function
|
||||
- **Description**: A function that triggers a refresh of the user's profile data. This can be used after updating profile fields to ensure the UI reflects the latest data from the server.
|
||||
- **Usage**: Call this function with the username parameter when you need to manually reload the user profile information. Note that `updateUserProfile` typically handles data refresh automatically.
|
||||
|
||||
#### Example
|
||||
```javascript
|
||||
refreshUserProfile(username);
|
||||
```
|
||||
129
src/plugin-slots/AdditionalProfileFieldsSlot/example/index.jsx
Normal file
129
src/plugin-slots/AdditionalProfileFieldsSlot/example/index.jsx
Normal file
@@ -0,0 +1,129 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button } from '@openedx/paragon';
|
||||
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
|
||||
|
||||
/**
|
||||
* Straightforward example of how you could use the pluginProps provided by
|
||||
* the AdditionalProfileFieldsSlot to create a custom profile field.
|
||||
*
|
||||
* Here you can set a 'favorite_color' field with radio buttons and
|
||||
* save it to the user's profile, especifically to their `meta` in
|
||||
* the user's model. For more information, see the documentation:
|
||||
*
|
||||
* https://github.com/openedx/edx-platform/blob/master/openedx/core/djangoapps/user_api/README.rst#persisting-optional-user-metadata
|
||||
*/
|
||||
const Example = ({
|
||||
updateUserProfile,
|
||||
profileFieldValues,
|
||||
profileFieldErrors,
|
||||
formComponents: { SwitchContent, EditableItemHeader, EmptyContent } = {},
|
||||
}) => {
|
||||
const authenticatedUser = getAuthenticatedUser();
|
||||
const [formMode, setFormMode] = useState('editable');
|
||||
|
||||
// Get current favorite color from profileFieldValues
|
||||
const currentColorField = profileFieldValues?.find(field => field.fieldName === 'favorite_color');
|
||||
const currentColor = currentColorField ? currentColorField.fieldValue : '';
|
||||
|
||||
const [value, setValue] = useState(currentColor);
|
||||
const handleChange = e => setValue(e.target.value);
|
||||
|
||||
// Get any validation errors for the favorite_color field
|
||||
const colorFieldError = profileFieldErrors?.favorite_color;
|
||||
|
||||
useEffect(() => {
|
||||
if (!value) { setFormMode('empty'); }
|
||||
if (colorFieldError) {
|
||||
setFormMode('editing');
|
||||
}
|
||||
}, [colorFieldError, value]);
|
||||
|
||||
const handleSubmit = () => {
|
||||
try {
|
||||
updateUserProfile(authenticatedUser.username, { extendedProfile: [{ fieldName: 'favorite_color', fieldValue: value }] });
|
||||
setFormMode('editable');
|
||||
} catch (error) {
|
||||
setFormMode('editing');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="border border-accent-500 p-3 mt-5">
|
||||
<h3 className="h3">Example Additional Profile Fields Slot</h3>
|
||||
|
||||
<SwitchContent
|
||||
className="pt-40px"
|
||||
expression={formMode}
|
||||
cases={{
|
||||
editing: (
|
||||
<>
|
||||
<label className="edit-section-header" htmlFor="favorite_color">
|
||||
Favorite Color
|
||||
</label>
|
||||
<input
|
||||
className="form-control"
|
||||
id="favorite_color"
|
||||
name="favorite_color"
|
||||
value={value}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<Button type="button" className="mt-2" onClick={handleSubmit}>
|
||||
Save
|
||||
</Button>
|
||||
</>
|
||||
),
|
||||
editable: (
|
||||
<>
|
||||
<div className="row m-0 pb-1.5 align-items-center">
|
||||
<p data-hj-suppress className="h5 font-weight-bold m-0">
|
||||
Favorite Color
|
||||
</p>
|
||||
</div>
|
||||
<EditableItemHeader
|
||||
content={value}
|
||||
showEditButton
|
||||
onClickEdit={() => setFormMode('editing')}
|
||||
showVisibility={false}
|
||||
visibility="private"
|
||||
/>
|
||||
</>
|
||||
),
|
||||
empty: (
|
||||
<>
|
||||
<div className="row m-0 pb-1.5 align-items-center">
|
||||
<p data-hj-suppress className="h5 font-weight-bold m-0">
|
||||
Favorite Color
|
||||
</p>
|
||||
</div>
|
||||
<EmptyContent onClick={() => setFormMode('editing')}>
|
||||
<p className="mb-0">Click to add your favorite color</p>
|
||||
</EmptyContent>
|
||||
</>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Example.propTypes = {
|
||||
updateUserProfile: PropTypes.func.isRequired,
|
||||
profileFieldValues: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
fieldName: PropTypes.string.isRequired,
|
||||
fieldValue: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.bool,
|
||||
PropTypes.number,
|
||||
]).isRequired,
|
||||
}),
|
||||
),
|
||||
profileFieldErrors: PropTypes.objectOf(PropTypes.string),
|
||||
formComponents: PropTypes.shape({
|
||||
SwitchContent: PropTypes.elementType.isRequired,
|
||||
}),
|
||||
};
|
||||
|
||||
export default Example;
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
37
src/plugin-slots/AdditionalProfileFieldsSlot/index.jsx
Normal file
37
src/plugin-slots/AdditionalProfileFieldsSlot/index.jsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { PluginSlot } from '@openedx/frontend-plugin-framework';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { useCallback } from 'react';
|
||||
import { patchProfile } from '../../profile/data/services';
|
||||
import { fetchProfile } from '../../profile/data/actions';
|
||||
|
||||
import SwitchContent from '../../profile/forms/elements/SwitchContent';
|
||||
import EmptyContent from '../../profile/forms/elements/EmptyContent';
|
||||
import EditableItemHeader from '../../profile/forms/elements/EditableItemHeader';
|
||||
|
||||
const AdditionalProfileFieldsSlot = () => {
|
||||
const dispatch = useDispatch();
|
||||
const extendedProfileValues = useSelector((state) => state.profilePage.account.extendedProfile);
|
||||
const errors = useSelector((state) => state.profilePage.errors);
|
||||
|
||||
const pluginProps = {
|
||||
refreshUserProfile: useCallback((username) => dispatch(fetchProfile(username)), [dispatch]),
|
||||
updateUserProfile: patchProfile,
|
||||
profileFieldValues: extendedProfileValues,
|
||||
profileFieldErrors: errors,
|
||||
formComponents: {
|
||||
SwitchContent,
|
||||
EmptyContent,
|
||||
EditableItemHeader,
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<PluginSlot
|
||||
id="org.openedx.frontend.profile.additional_profile_fields.v1"
|
||||
pluginProps={pluginProps}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default AdditionalProfileFieldsSlot;
|
||||
53
src/plugin-slots/FooterSlot/README.md
Normal file
53
src/plugin-slots/FooterSlot/README.md
Normal file
@@ -0,0 +1,53 @@
|
||||
# Footer Slot
|
||||
|
||||
### Slot ID: `org.openedx.frontend.layout.footer.v1`
|
||||
|
||||
### Slot ID Aliases
|
||||
* `footer_slot`
|
||||
|
||||
## Description
|
||||
|
||||
This slot is used to replace/modify/hide the footer.
|
||||
|
||||
The implementation of the `FooterSlot` component lives in [the `frontend-component-footer` repository](https://github.com/openedx/frontend-component-footer/).
|
||||
|
||||
## Example
|
||||
|
||||
The following `env.config.jsx` will replace the default footer.
|
||||
|
||||

|
||||
|
||||
with a simple custom footer
|
||||
|
||||

|
||||
|
||||
```jsx
|
||||
import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
|
||||
|
||||
const config = {
|
||||
pluginSlots: {
|
||||
'org.openedx.frontend.layout.footer.v1': {
|
||||
plugins: [
|
||||
{
|
||||
// Hide the default footer
|
||||
op: PLUGIN_OPERATIONS.Hide,
|
||||
widgetId: 'default_contents',
|
||||
},
|
||||
{
|
||||
// Insert a custom footer
|
||||
op: PLUGIN_OPERATIONS.Insert,
|
||||
widget: {
|
||||
id: 'custom_footer',
|
||||
type: DIRECT_PLUGIN,
|
||||
RenderWidget: () => (
|
||||
<h1 style={{textAlign: 'center'}}>🦶</h1>
|
||||
),
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default config;
|
||||
```
|
||||
BIN
src/plugin-slots/FooterSlot/images/custom_footer.png
Normal file
BIN
src/plugin-slots/FooterSlot/images/custom_footer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
src/plugin-slots/FooterSlot/images/default_footer.png
Normal file
BIN
src/plugin-slots/FooterSlot/images/default_footer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
3
src/plugin-slots/README.md
Normal file
3
src/plugin-slots/README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# `frontend-app-profile` Plugin Slots
|
||||
|
||||
* [`org.openedx.frontend.layout.footer.v1`](./FooterSlot/)
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Alert } from '@edx/paragon';
|
||||
import { Alert } from '@openedx/paragon';
|
||||
import { FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
|
||||
@@ -11,7 +11,11 @@ const AgeMessage = ({ accountSettingsUrl }) => (
|
||||
show
|
||||
>
|
||||
<Alert.Heading id="profile.age.headline">
|
||||
Your profile cannot be shared.
|
||||
<FormattedMessage
|
||||
id="profile.age.cannotShare"
|
||||
defaultMessage="Your profile cannot be shared."
|
||||
description="Error message indicating that the user's profile cannot be shared"
|
||||
/>
|
||||
</Alert.Heading>
|
||||
<FormattedMessage
|
||||
id="profile.age.details"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React from 'react';
|
||||
|
||||
const Banner = () => <div className="profile-page-bg-banner bg-primary d-none d-md-block p-relative" />;
|
||||
const Banner = () => <div className="profile-page-bg-banner bg-primary d-md-block p-relative" />;
|
||||
|
||||
export default Banner;
|
||||
|
||||
@@ -6,6 +6,7 @@ const DateJoined = ({ date }) => {
|
||||
if (date == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<p className="mb-0">
|
||||
<FormattedMessage
|
||||
|
||||
@@ -6,7 +6,7 @@ import { sendTrackingLogEvent } from '@edx/frontend-platform/analytics';
|
||||
import { ensureConfig, getConfig } from '@edx/frontend-platform';
|
||||
import { AppContext } from '@edx/frontend-platform/react';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Alert, Hyperlink } from '@edx/paragon';
|
||||
import { Alert, Hyperlink } from '@openedx/paragon';
|
||||
|
||||
// Actions
|
||||
import {
|
||||
@@ -43,6 +43,8 @@ import messages from './ProfilePage.messages';
|
||||
|
||||
import withParams from '../utils/hoc';
|
||||
|
||||
import AdditionalProfileFieldsSlot from '../plugin-slots/AdditionalProfileFieldsSlot';
|
||||
|
||||
ensureConfig(['CREDENTIALS_BASE_URL', 'LMS_BASE_URL'], 'ProfilePage');
|
||||
|
||||
class ProfilePage extends React.Component {
|
||||
@@ -50,10 +52,9 @@ class ProfilePage extends React.Component {
|
||||
super(props, context);
|
||||
|
||||
const credentialsBaseUrl = context.config.CREDENTIALS_BASE_URL;
|
||||
|
||||
this.state = {
|
||||
viewMyRecordsUrl: credentialsBaseUrl ? `${credentialsBaseUrl}/records` : null,
|
||||
accountSettingsUrl: `${context.config.LMS_BASE_URL}/account/settings`,
|
||||
accountSettingsUrl: context.config.ACCOUNT_SETTINGS_URL,
|
||||
};
|
||||
|
||||
this.handleSaveProfilePhoto = this.handleSaveProfilePhoto.bind(this);
|
||||
@@ -184,12 +185,19 @@ class ProfilePage extends React.Component {
|
||||
visibilityBio,
|
||||
requiresParentalConsent,
|
||||
isLoadingProfile,
|
||||
username,
|
||||
saveState,
|
||||
navigate,
|
||||
} = this.props;
|
||||
|
||||
if (isLoadingProfile) {
|
||||
return <PageLoading srMessage={this.props.intl.formatMessage(messages['profile.loading'])} />;
|
||||
}
|
||||
|
||||
if (!username && saveState === 'error' && navigate) {
|
||||
navigate('/notfound');
|
||||
}
|
||||
|
||||
const commonFormProps = {
|
||||
openHandler: this.handleOpen,
|
||||
closeHandler: this.handleClose,
|
||||
@@ -224,7 +232,6 @@ class ProfilePage extends React.Component {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>PluginPOC</div>
|
||||
<div className="col">
|
||||
<div className="d-md-none">
|
||||
{this.renderHeadingLockup()}
|
||||
@@ -284,6 +291,9 @@ class ProfilePage extends React.Component {
|
||||
{...commonFormProps}
|
||||
/>
|
||||
)}
|
||||
<div className="mb-4">
|
||||
<AdditionalProfileFieldsSlot />
|
||||
</div>
|
||||
</div>
|
||||
<div className="pt-md-3 col-md-8 col-lg-7 offset-lg-1">
|
||||
{!this.isYOBDisabled() && this.renderAgeMessage()}
|
||||
@@ -332,6 +342,7 @@ ProfilePage.propTypes = {
|
||||
// Account data
|
||||
requiresParentalConsent: PropTypes.bool,
|
||||
dateJoined: PropTypes.string,
|
||||
username: PropTypes.string,
|
||||
|
||||
// Bio form data
|
||||
bio: PropTypes.string,
|
||||
@@ -397,6 +408,7 @@ ProfilePage.propTypes = {
|
||||
openForm: PropTypes.func.isRequired,
|
||||
closeForm: PropTypes.func.isRequired,
|
||||
updateDraft: PropTypes.func.isRequired,
|
||||
navigate: PropTypes.func.isRequired,
|
||||
|
||||
// Router
|
||||
params: PropTypes.shape({
|
||||
@@ -409,6 +421,7 @@ ProfilePage.propTypes = {
|
||||
|
||||
ProfilePage.defaultProps = {
|
||||
saveState: null,
|
||||
username: '',
|
||||
savePhotoState: null,
|
||||
photoUploadError: {},
|
||||
profileImage: {},
|
||||
|
||||
@@ -3,13 +3,13 @@ import { getConfig } from '@edx/frontend-platform';
|
||||
import * as analytics from '@edx/frontend-platform/analytics';
|
||||
import { AppContext } from '@edx/frontend-platform/react';
|
||||
import { configure as configureI18n, IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import { mount } from 'enzyme';
|
||||
import { render } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Provider } from 'react-redux';
|
||||
import renderer from 'react-test-renderer';
|
||||
import configureMockStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
import { BrowserRouter, useNavigate } from 'react-router-dom';
|
||||
|
||||
import messages from '../i18n';
|
||||
import ProfilePage from './ProfilePage';
|
||||
@@ -17,6 +17,7 @@ import ProfilePage from './ProfilePage';
|
||||
const mockStore = configureMockStore([thunk]);
|
||||
const storeMocks = {
|
||||
loadingApp: require('./__mocks__/loadingApp.mockStore'),
|
||||
invalidUser: require('./__mocks__/invalidUser.mockStore'),
|
||||
viewOwnProfile: require('./__mocks__/viewOwnProfile.mockStore'),
|
||||
viewOtherProfile: require('./__mocks__/viewOtherProfile.mockStore'),
|
||||
savingEditedBio: require('./__mocks__/savingEditedBio.mockStore'),
|
||||
@@ -66,6 +67,23 @@ beforeEach(() => {
|
||||
analytics.sendTrackingLogEvent.mockReset();
|
||||
});
|
||||
|
||||
const ProfileWrapper = ({ params, requiresParentalConsent }) => {
|
||||
const navigate = useNavigate();
|
||||
return (
|
||||
<ProfilePage
|
||||
{...requiredProfilePageProps}
|
||||
params={params}
|
||||
requiresParentalConsent={requiresParentalConsent}
|
||||
navigate={navigate}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
ProfileWrapper.propTypes = {
|
||||
params: PropTypes.shape({}).isRequired,
|
||||
requiresParentalConsent: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
const ProfilePageWrapper = ({
|
||||
contextValue, store, params, requiresParentalConsent,
|
||||
}) => (
|
||||
@@ -74,7 +92,12 @@ const ProfilePageWrapper = ({
|
||||
>
|
||||
<IntlProvider locale="en">
|
||||
<Provider store={store}>
|
||||
<ProfilePage {...requiredProfilePageProps} params={params} requiresParentalConsent={requiresParentalConsent} />
|
||||
<BrowserRouter>
|
||||
<ProfileWrapper
|
||||
params={params}
|
||||
requiresParentalConsent={requiresParentalConsent}
|
||||
/>
|
||||
</BrowserRouter>
|
||||
</Provider>
|
||||
</IntlProvider>
|
||||
</AppContext.Provider>
|
||||
@@ -100,7 +123,17 @@ describe('<ProfilePage />', () => {
|
||||
config: getConfig(),
|
||||
};
|
||||
const component = <ProfilePageWrapper contextValue={contextValue} store={mockStore(storeMocks.loadingApp)} />;
|
||||
const tree = renderer.create(component).toJSON();
|
||||
const { container: tree } = render(component);
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('successfully redirected to not found page.', () => {
|
||||
const contextValue = {
|
||||
authenticatedUser: { userId: 123, username: 'staff', administrator: true },
|
||||
config: getConfig(),
|
||||
};
|
||||
const component = <ProfilePageWrapper contextValue={contextValue} store={mockStore(storeMocks.invalidUser)} />;
|
||||
const { container: tree } = render(component);
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@@ -110,7 +143,7 @@ describe('<ProfilePage />', () => {
|
||||
config: getConfig(),
|
||||
};
|
||||
const component = <ProfilePageWrapper contextValue={contextValue} store={mockStore(storeMocks.viewOwnProfile)} />;
|
||||
const tree = renderer.create(component).toJSON();
|
||||
const { container: tree } = render(component);
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@@ -144,7 +177,7 @@ describe('<ProfilePage />', () => {
|
||||
match={{ params: { username: 'verified' } }} // Override default match
|
||||
/>
|
||||
);
|
||||
const tree = renderer.create(component).toJSON();
|
||||
const { container: tree } = render(component);
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@@ -159,7 +192,7 @@ describe('<ProfilePage />', () => {
|
||||
store={mockStore(storeMocks.savingEditedBio)}
|
||||
/>
|
||||
);
|
||||
const tree = renderer.create(component).toJSON();
|
||||
const { container: tree } = render(component);
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@@ -176,7 +209,7 @@ describe('<ProfilePage />', () => {
|
||||
store={mockStore(storeData)}
|
||||
/>
|
||||
);
|
||||
const tree = renderer.create(component).toJSON();
|
||||
const { container: tree } = render(component);
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@@ -194,7 +227,7 @@ describe('<ProfilePage />', () => {
|
||||
store={mockStore(storeData)}
|
||||
/>
|
||||
);
|
||||
const tree = renderer.create(component).toJSON();
|
||||
const { container: tree } = render(component);
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@@ -212,7 +245,7 @@ describe('<ProfilePage />', () => {
|
||||
store={mockStore(storeData)}
|
||||
/>
|
||||
);
|
||||
const tree = renderer.create(component).toJSON();
|
||||
const { container: tree } = render(component);
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@@ -230,7 +263,7 @@ describe('<ProfilePage />', () => {
|
||||
store={mockStore(storeData)}
|
||||
/>
|
||||
);
|
||||
const tree = renderer.create(component).toJSON();
|
||||
const { container: tree } = render(component);
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
||||
@@ -248,7 +281,7 @@ describe('<ProfilePage />', () => {
|
||||
store={mockStore(storeMocks.viewOwnProfile)}
|
||||
/>
|
||||
);
|
||||
const tree = renderer.create(component).toJSON();
|
||||
const { container: tree } = render(component);
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
it('test age message alert', () => {
|
||||
@@ -259,17 +292,15 @@ describe('<ProfilePage />', () => {
|
||||
authenticatedUser: { userId: 123, username: 'staff', administrator: true },
|
||||
config: { ...getConfig(), COLLECT_YEAR_OF_BIRTH: true },
|
||||
};
|
||||
const component = (
|
||||
const { container } = render(
|
||||
<ProfilePageWrapper
|
||||
contextValue={contextValue}
|
||||
store={mockStore(storeData)}
|
||||
requiresParentalConsent
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
const wrapper = mount(component);
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find('.alert-info').hasClass('show')).toBe(true);
|
||||
expect(container.querySelector('.alert-info')).toHaveClass('show');
|
||||
});
|
||||
it('test photo error alert', () => {
|
||||
const storeData = JSON.parse(JSON.stringify(storeMocks.viewOwnProfile));
|
||||
@@ -278,11 +309,15 @@ describe('<ProfilePage />', () => {
|
||||
authenticatedUser: { userId: 123, username: 'staff', administrator: true },
|
||||
config: { ...getConfig(), COLLECT_YEAR_OF_BIRTH: true },
|
||||
};
|
||||
const component = <ProfilePageWrapper contextValue={contextValue} store={mockStore(storeData)} />;
|
||||
const wrapper = mount(component);
|
||||
wrapper.update();
|
||||
const { container } = render(
|
||||
<ProfilePageWrapper
|
||||
contextValue={contextValue}
|
||||
store={mockStore(storeData)}
|
||||
requiresParentalConsent
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(wrapper.find('.alert-danger').hasClass('show')).toBe(true);
|
||||
expect(container.querySelector('.alert-danger')).toHaveClass('show');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -292,15 +327,13 @@ describe('<ProfilePage />', () => {
|
||||
authenticatedUser: { userId: 123, username: 'staff', administrator: true },
|
||||
config: getConfig(),
|
||||
};
|
||||
const component = (
|
||||
render(
|
||||
<ProfilePageWrapper
|
||||
contextValue={contextValue}
|
||||
store={mockStore(storeMocks.loadingApp)}
|
||||
params={{ username: 'test-username' }}
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
const wrapper = mount(component);
|
||||
wrapper.update();
|
||||
|
||||
expect(analytics.sendTrackingLogEvent.mock.calls.length).toBe(1);
|
||||
expect(analytics.sendTrackingLogEvent.mock.calls[0][0]).toEqual('edx.profile.viewed');
|
||||
|
||||
@@ -1,219 +0,0 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { ensureConfig } from '@edx/frontend-platform';
|
||||
import { AppContext } from '@edx/frontend-platform/react';
|
||||
import { injectIntl, intlShape, FormattedDate } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faTwitter, faFacebook, faLinkedin } from '@fortawesome/free-brands-svg-icons';
|
||||
import {
|
||||
ActionRow, Avatar, Card, Hyperlink, Icon,
|
||||
} from '@edx/paragon';
|
||||
import { HistoryEdu, VerifiedUser } from '@edx/paragon/icons';
|
||||
|
||||
import get from 'lodash.get';
|
||||
|
||||
import PluginCountry from './forms/PluginCountry';
|
||||
import { Plugin } from '../../plugins';
|
||||
|
||||
// Actions
|
||||
import {
|
||||
fetchProfile,
|
||||
} from './data/actions';
|
||||
|
||||
// Components
|
||||
import PageLoading from './PageLoading';
|
||||
|
||||
// Selectors
|
||||
import { profilePageSelector } from './data/selectors';
|
||||
|
||||
// i18n
|
||||
import messages from './ProfilePage.messages';
|
||||
import eduMessages from './forms/Education.messages';
|
||||
|
||||
import withParams from '../utils/hoc';
|
||||
|
||||
ensureConfig(['CREDENTIALS_BASE_URL', 'LMS_BASE_URL'], 'ProfilePage');
|
||||
|
||||
// eslint-disable-next-line react/function-component-definition
|
||||
function Fallback() {
|
||||
return (
|
||||
<div>this is broken as all get</div>
|
||||
);
|
||||
}
|
||||
|
||||
const platformDisplayInfo = {
|
||||
facebook: {
|
||||
icon: faFacebook,
|
||||
name: '',
|
||||
},
|
||||
twitter: {
|
||||
icon: faTwitter,
|
||||
name: '',
|
||||
},
|
||||
linkedin: {
|
||||
icon: faLinkedin,
|
||||
name: '',
|
||||
},
|
||||
};
|
||||
|
||||
class ProfilePluginPage extends React.Component {
|
||||
componentDidMount() {
|
||||
this.props.fetchProfile(this.props.params.username);
|
||||
}
|
||||
|
||||
renderContent() {
|
||||
const {
|
||||
profileImage,
|
||||
country,
|
||||
levelOfEducation,
|
||||
socialLinks,
|
||||
isLoadingProfile,
|
||||
dateJoined,
|
||||
name,
|
||||
intl,
|
||||
} = this.props;
|
||||
|
||||
if (isLoadingProfile) {
|
||||
return <PageLoading srMessage={this.props.intl.formatMessage(messages['profile.loading'])} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Plugin fallbackComponent={<Fallback />}>
|
||||
<Card className="mb-2">
|
||||
<Card.Header
|
||||
className="pb-5"
|
||||
subtitle={(
|
||||
<Hyperlink destination={`/u/${this.props.params.username}`} target="_parent">
|
||||
View public profile
|
||||
</Hyperlink>
|
||||
)}
|
||||
actions={
|
||||
(
|
||||
<ActionRow className="mt-3">
|
||||
{socialLinks
|
||||
.filter(({ socialLink }) => Boolean(socialLink))
|
||||
.map(({ platform, socialLink }) => (
|
||||
<StaticListItem
|
||||
key={platform}
|
||||
name={platformDisplayInfo[platform].name}
|
||||
url={socialLink}
|
||||
platform={platform}
|
||||
/>
|
||||
))}
|
||||
</ActionRow>
|
||||
)
|
||||
}
|
||||
/>
|
||||
<Card.Section className="text-center" muted>
|
||||
<Avatar
|
||||
size="xl"
|
||||
className="profile-plugin-avatar"
|
||||
src={profileImage.src}
|
||||
alt="Profile image"
|
||||
/>
|
||||
<p className="h2 mb-0 font-weight-bold">{name}</p>
|
||||
<p className="h3 mb-0 font-weight-bold">{this.props.params.username}</p>
|
||||
<PluginCountry
|
||||
country={country}
|
||||
/>
|
||||
</Card.Section>
|
||||
<Card.Footer className="p-0">
|
||||
<Card.Section className="pgn-icons-cell-vertical">
|
||||
<Icon src={VerifiedUser} />
|
||||
<p>
|
||||
since <FormattedDate value={new Date(dateJoined)} year="numeric" />
|
||||
</p>
|
||||
</Card.Section>
|
||||
<Card.Section className="pgn-icons-cell-vertical">
|
||||
<Icon src={HistoryEdu} />
|
||||
<p>
|
||||
{intl.formatMessage(get(
|
||||
eduMessages,
|
||||
`profile.education.levels.${levelOfEducation}`,
|
||||
eduMessages['profile.education.levels.o'],
|
||||
))}
|
||||
</p>
|
||||
</Card.Section>
|
||||
</Card.Footer>
|
||||
</Card>
|
||||
</Plugin>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="profile-page overflow-hidden">
|
||||
{this.renderContent()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const SocialLink = ({ url, name, platform }) => (
|
||||
<a href={url} className="font-weight-bold" target="_parent">
|
||||
<FontAwesomeIcon className="mr-2" icon={platformDisplayInfo[platform].icon} />
|
||||
{name}
|
||||
</a>
|
||||
);
|
||||
|
||||
const StaticListItem = ({ url, name, platform }) => (
|
||||
<ul className="list-inline">
|
||||
<SocialLink name={name} url={url} platform={platform} />
|
||||
</ul>
|
||||
);
|
||||
|
||||
ProfilePluginPage.contextType = AppContext;
|
||||
|
||||
ProfilePluginPage.propTypes = {
|
||||
// Account data
|
||||
dateJoined: PropTypes.string,
|
||||
|
||||
// Country form data
|
||||
country: PropTypes.string,
|
||||
|
||||
// Education form data
|
||||
levelOfEducation: PropTypes.string,
|
||||
|
||||
// Social links form data
|
||||
socialLinks: PropTypes.arrayOf(PropTypes.shape({
|
||||
platform: PropTypes.string,
|
||||
socialLink: PropTypes.string,
|
||||
})),
|
||||
|
||||
// Other data we need
|
||||
profileImage: PropTypes.shape({
|
||||
src: PropTypes.string,
|
||||
isDefault: PropTypes.bool,
|
||||
}),
|
||||
isLoadingProfile: PropTypes.bool.isRequired,
|
||||
|
||||
// Actions
|
||||
fetchProfile: PropTypes.func.isRequired,
|
||||
|
||||
// Router
|
||||
params: PropTypes.shape({
|
||||
username: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
|
||||
// i18n
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
ProfilePluginPage.defaultProps = {
|
||||
profileImage: {},
|
||||
levelOfEducation: null,
|
||||
country: null,
|
||||
socialLinks: [],
|
||||
dateJoined: null,
|
||||
};
|
||||
|
||||
export default connect(
|
||||
profilePageSelector,
|
||||
{
|
||||
fetchProfile,
|
||||
},
|
||||
)(injectIntl(withParams(ProfilePluginPage)));
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
import { VisibilityOff } from '@edx/paragon/icons';
|
||||
import { Icon } from '@edx/paragon';
|
||||
import { VisibilityOff } from '@openedx/paragon/icons';
|
||||
import { Icon } from '@openedx/paragon';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
|
||||
const UsernameDescription = () => (
|
||||
|
||||
41
src/profile/__mocks__/invalidUser.mockStore.js
Normal file
41
src/profile/__mocks__/invalidUser.mockStore.js
Normal file
@@ -0,0 +1,41 @@
|
||||
module.exports = {
|
||||
userAccount: {
|
||||
loading: false,
|
||||
error: null,
|
||||
username: 'staff',
|
||||
email: null,
|
||||
bio: null,
|
||||
name: null,
|
||||
country: null,
|
||||
socialLinks: null,
|
||||
profileImage: {
|
||||
imageUrlMedium: null,
|
||||
imageUrlLarge: null
|
||||
},
|
||||
levelOfEducation: null,
|
||||
learningGoal: null
|
||||
},
|
||||
profilePage: {
|
||||
errors: {},
|
||||
saveState: 'error',
|
||||
savePhotoState: null,
|
||||
currentlyEditingField: null,
|
||||
account: {
|
||||
username: '',
|
||||
socialLinks: []
|
||||
},
|
||||
preferences: {},
|
||||
courseCertificates: [],
|
||||
drafts: {},
|
||||
isLoadingProfile: false,
|
||||
isAuthenticatedUserProfile: true,
|
||||
},
|
||||
router: {
|
||||
location: {
|
||||
pathname: '/u/staffTest',
|
||||
search: '',
|
||||
hash: ''
|
||||
},
|
||||
action: 'POP'
|
||||
}
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -63,12 +63,14 @@ const profilePage = (state = initialState, action = {}) => {
|
||||
return {
|
||||
...state,
|
||||
saveState: 'error',
|
||||
isLoadingProfile: false,
|
||||
errors: { ...state.errors, ...action.payload.errors },
|
||||
};
|
||||
case SAVE_PROFILE.RESET:
|
||||
return {
|
||||
...state,
|
||||
saveState: null,
|
||||
isLoadingProfile: false,
|
||||
errors: {},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { history } from '@edx/frontend-platform';
|
||||
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
|
||||
import pick from 'lodash.pick';
|
||||
import {
|
||||
@@ -95,7 +94,11 @@ export function* handleFetchProfile(action) {
|
||||
yield put(fetchProfileReset());
|
||||
} catch (e) {
|
||||
if (e.response.status === 404) {
|
||||
history.push('/notfound');
|
||||
if (e.processedData && e.processedData.fieldErrors) {
|
||||
yield put(saveProfileFailure(e.processedData.fieldErrors));
|
||||
} else {
|
||||
yield put(saveProfileFailure(e.customAttributes));
|
||||
}
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Form } from '@edx/paragon';
|
||||
import { Form } from '@openedx/paragon';
|
||||
|
||||
import messages from './Bio.messages';
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import {
|
||||
FormattedDate, FormattedMessage, injectIntl, intlShape,
|
||||
} from '@edx/frontend-platform/i18n';
|
||||
import { Hyperlink } from '@edx/paragon';
|
||||
import { Hyperlink } from '@openedx/paragon';
|
||||
import { connect } from 'react-redux';
|
||||
import get from 'lodash.get';
|
||||
|
||||
@@ -68,7 +68,7 @@ class Certificates extends React.Component {
|
||||
})();
|
||||
|
||||
return (
|
||||
<div key={`${modifiedDate}-${courseId}`} className="col col-sm-6 d-flex align-items-stretch">
|
||||
<div key={`${modifiedDate}-${courseId}`} className="col-12 col-sm-6 d-flex align-items-stretch">
|
||||
<div className="card mb-4 certificate flex-grow-1">
|
||||
<div
|
||||
className="certificate-type-illustration"
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Form } from '@edx/paragon';
|
||||
import { Form } from '@openedx/paragon';
|
||||
|
||||
import messages from './Country.messages';
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import get from 'lodash.get';
|
||||
import { Form } from '@edx/paragon';
|
||||
import { Form } from '@openedx/paragon';
|
||||
|
||||
import messages from './Education.messages';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useMemo } from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import renderer from 'react-test-renderer';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import configureMockStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
import { configure as configureI18n, IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
@@ -92,31 +92,25 @@ LearningGoalWrapperWithStore.propTypes = {
|
||||
describe('<LearningGoal />', () => {
|
||||
describe('renders the current learning goal', () => {
|
||||
it('renders "I want to advance my career"', () => {
|
||||
const learningGoalRenderer = renderer.create(
|
||||
render(
|
||||
<LearningGoalWrapper
|
||||
{...requiredLearningGoalProps}
|
||||
formId="learningGoal"
|
||||
/>,
|
||||
);
|
||||
|
||||
const learningGoalInstance = learningGoalRenderer.root;
|
||||
|
||||
expect(learningGoalInstance.findByProps({ className: 'lead' }).children).toEqual(['I want to advance my career']);
|
||||
expect(screen.getByText('I want to advance my career')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('renders "Something else"', () => {
|
||||
requiredLearningGoalProps.learningGoal = 'something_else';
|
||||
|
||||
const learningGoalRenderer = renderer.create(
|
||||
render(
|
||||
<LearningGoalWrapper
|
||||
{...requiredLearningGoalProps}
|
||||
formId="learningGoal"
|
||||
/>,
|
||||
);
|
||||
|
||||
const learningGoalInstance = learningGoalRenderer.root;
|
||||
|
||||
expect(learningGoalInstance.findByProps({ className: 'lead' }).children).toEqual(['Something else']);
|
||||
expect(screen.getByText('Something else')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { injectIntl } from '@edx/frontend-platform/i18n';
|
||||
import { Icon } from '@edx/paragon';
|
||||
import { LocationOn } from '@edx/paragon/icons';
|
||||
|
||||
// Selectors
|
||||
import { countrySelector } from '../data/selectors';
|
||||
|
||||
// eslint-disable-next-line react/prefer-stateless-function
|
||||
class PluginCountry extends React.Component {
|
||||
render() {
|
||||
const {
|
||||
country,
|
||||
countryMessages,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<div className="pgn-icons-cell-horizontal">
|
||||
<Icon src={LocationOn} />
|
||||
<p className="h5 mt-1 ml-1">{countryMessages[country]}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
PluginCountry.propTypes = {
|
||||
country: PropTypes.string,
|
||||
countryMessages: PropTypes.objectOf(PropTypes.string).isRequired,
|
||||
};
|
||||
|
||||
PluginCountry.defaultProps = {
|
||||
country: null,
|
||||
};
|
||||
|
||||
export default connect(
|
||||
countrySelector,
|
||||
{},
|
||||
)(injectIntl(PluginCountry));
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Form } from '@edx/paragon';
|
||||
import { Form } from '@openedx/paragon';
|
||||
|
||||
import messages from './PreferredLanguage.messages';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, Dropdown } from '@edx/paragon';
|
||||
import { Button, Dropdown } from '@openedx/paragon';
|
||||
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { ReactComponent as DefaultAvatar } from '../assets/avatar.svg';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Alert } from '@edx/paragon';
|
||||
import { Alert } from '@openedx/paragon';
|
||||
import { connect } from 'react-redux';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faTwitter, faFacebook, faLinkedin } from '@fortawesome/free-brands-svg-icons';
|
||||
@@ -33,6 +33,108 @@ const platformDisplayInfo = {
|
||||
},
|
||||
};
|
||||
|
||||
const SocialLink = ({ url, name, platform }) => (
|
||||
<a href={url} className="font-weight-bold">
|
||||
<FontAwesomeIcon className="mr-2" icon={platformDisplayInfo[platform].icon} />
|
||||
{name}
|
||||
</a>
|
||||
);
|
||||
|
||||
SocialLink.propTypes = {
|
||||
url: PropTypes.string.isRequired,
|
||||
platform: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
const EditableListItem = ({
|
||||
url, platform, onClickEmptyContent, name,
|
||||
}) => {
|
||||
const linkDisplay = url ? (
|
||||
<SocialLink name={name} url={url} platform={platform} />
|
||||
) : (
|
||||
<EmptyContent onClick={onClickEmptyContent}>Add {name}</EmptyContent>
|
||||
);
|
||||
|
||||
return <li className="form-group">{linkDisplay}</li>;
|
||||
};
|
||||
|
||||
EditableListItem.propTypes = {
|
||||
url: PropTypes.string,
|
||||
platform: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
onClickEmptyContent: PropTypes.func,
|
||||
};
|
||||
EditableListItem.defaultProps = {
|
||||
url: null,
|
||||
onClickEmptyContent: null,
|
||||
};
|
||||
|
||||
const EditingListItem = ({
|
||||
platform, name, value, onChange, error,
|
||||
}) => (
|
||||
<li className="form-group">
|
||||
<label htmlFor={`social-${platform}`}>{name}</label>
|
||||
<input
|
||||
className={classNames('form-control', { 'is-invalid': Boolean(error) })}
|
||||
type="text"
|
||||
id={`social-${platform}`}
|
||||
name={platform}
|
||||
value={value || ''}
|
||||
onChange={onChange}
|
||||
aria-describedby="social-error-feedback"
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
|
||||
EditingListItem.propTypes = {
|
||||
platform: PropTypes.string.isRequired,
|
||||
value: PropTypes.string,
|
||||
name: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
error: PropTypes.string,
|
||||
};
|
||||
|
||||
EditingListItem.defaultProps = {
|
||||
value: null,
|
||||
error: null,
|
||||
};
|
||||
|
||||
const EmptyListItem = ({ onClick, name }) => (
|
||||
<li className="mb-4">
|
||||
<EmptyContent onClick={onClick}>
|
||||
<FormattedMessage
|
||||
id="profile.sociallinks.add"
|
||||
defaultMessage="Add {network}"
|
||||
values={{
|
||||
network: name,
|
||||
}}
|
||||
description="{network} is the name of a social network such as Facebook or Twitter"
|
||||
/>
|
||||
</EmptyContent>
|
||||
</li>
|
||||
);
|
||||
|
||||
EmptyListItem.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const StaticListItem = ({ name, url, platform }) => (
|
||||
<li className="mb-2">
|
||||
<SocialLink name={name} url={url} platform={platform} />
|
||||
</li>
|
||||
);
|
||||
|
||||
StaticListItem.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
url: PropTypes.string,
|
||||
platform: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
StaticListItem.defaultProps = {
|
||||
url: null,
|
||||
};
|
||||
|
||||
class SocialLinks extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@@ -243,105 +345,3 @@ export default connect(
|
||||
editableFormSelector,
|
||||
{},
|
||||
)(injectIntl(SocialLinks));
|
||||
|
||||
const SocialLink = ({ url, name, platform }) => (
|
||||
<a href={url} className="font-weight-bold">
|
||||
<FontAwesomeIcon className="mr-2" icon={platformDisplayInfo[platform].icon} />
|
||||
{name}
|
||||
</a>
|
||||
);
|
||||
|
||||
SocialLink.propTypes = {
|
||||
url: PropTypes.string.isRequired,
|
||||
platform: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
const EditableListItem = ({
|
||||
url, platform, onClickEmptyContent, name,
|
||||
}) => {
|
||||
const linkDisplay = url ? (
|
||||
<SocialLink name={name} url={url} platform={platform} />
|
||||
) : (
|
||||
<EmptyContent onClick={onClickEmptyContent}>Add {name}</EmptyContent>
|
||||
);
|
||||
|
||||
return <li className="form-group">{linkDisplay}</li>;
|
||||
};
|
||||
|
||||
EditableListItem.propTypes = {
|
||||
url: PropTypes.string,
|
||||
platform: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
onClickEmptyContent: PropTypes.func,
|
||||
};
|
||||
EditableListItem.defaultProps = {
|
||||
url: null,
|
||||
onClickEmptyContent: null,
|
||||
};
|
||||
|
||||
const EditingListItem = ({
|
||||
platform, name, value, onChange, error,
|
||||
}) => (
|
||||
<li className="form-group">
|
||||
<label htmlFor={`social-${platform}`}>{name}</label>
|
||||
<input
|
||||
className={classNames('form-control', { 'is-invalid': Boolean(error) })}
|
||||
type="text"
|
||||
id={`social-${platform}`}
|
||||
name={platform}
|
||||
value={value || ''}
|
||||
onChange={onChange}
|
||||
aria-describedby="social-error-feedback"
|
||||
/>
|
||||
</li>
|
||||
);
|
||||
|
||||
EditingListItem.propTypes = {
|
||||
platform: PropTypes.string.isRequired,
|
||||
value: PropTypes.string,
|
||||
name: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
error: PropTypes.string,
|
||||
};
|
||||
|
||||
EditingListItem.defaultProps = {
|
||||
value: null,
|
||||
error: null,
|
||||
};
|
||||
|
||||
const EmptyListItem = ({ onClick, name }) => (
|
||||
<li className="mb-4">
|
||||
<EmptyContent onClick={onClick}>
|
||||
<FormattedMessage
|
||||
id="profile.sociallinks.add"
|
||||
defaultMessage="Add {network}"
|
||||
values={{
|
||||
network: name,
|
||||
}}
|
||||
description="{network} is the name of a social network such as Facebook or Twitter"
|
||||
/>
|
||||
</EmptyContent>
|
||||
</li>
|
||||
);
|
||||
|
||||
EmptyListItem.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const StaticListItem = ({ name, url, platform }) => (
|
||||
<li className="mb-2">
|
||||
<SocialLink name={name} url={url} platform={platform} />
|
||||
</li>
|
||||
);
|
||||
|
||||
StaticListItem.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
url: PropTypes.string,
|
||||
platform: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
StaticListItem.defaultProps = {
|
||||
url: null,
|
||||
};
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { mount } from 'enzyme';
|
||||
import { render, fireEvent, screen } from '@testing-library/react';
|
||||
import PropTypes from 'prop-types';
|
||||
import React, { useMemo } from 'react';
|
||||
import { Provider } from 'react-redux';
|
||||
import renderer from 'react-test-renderer';
|
||||
import configureMockStore from 'redux-mock-store';
|
||||
import thunk from 'redux-thunk';
|
||||
import { configure as configureI18n, IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
@@ -103,7 +102,7 @@ describe('<SocialLinks />', () => {
|
||||
['certificates', 'bio', 'goals', 'socialLinks'].forEach(editMode => (
|
||||
it(`calls social links with edit mode ${editMode}`, () => {
|
||||
const component = <SocialLinksWrapper {...defaultProps} formId={editMode} />;
|
||||
const tree = renderer.create(component).toJSON();
|
||||
const { container: tree } = render(component);
|
||||
expect(tree).toMatchSnapshot();
|
||||
})
|
||||
));
|
||||
@@ -112,47 +111,45 @@ describe('<SocialLinks />', () => {
|
||||
const changeHandler = jest.fn();
|
||||
const submitHandler = jest.fn();
|
||||
const closeHandler = jest.fn();
|
||||
const component = (
|
||||
const { container } = render(
|
||||
<SocialLinksWrapper
|
||||
{...defaultProps}
|
||||
formId="bio"
|
||||
changeHandler={changeHandler}
|
||||
submitHandler={submitHandler}
|
||||
closeHandler={closeHandler}
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
const wrapper = mount(component);
|
||||
const socialLink = wrapper.find(SocialLinks);
|
||||
|
||||
const { platform } = defaultProps.socialLinks[0];
|
||||
const inputField = socialLink.find(`#social-${platform}`);
|
||||
inputField.simulate('change', { target: { value: 'test', name: platform } });
|
||||
const inputField = container.querySelector(`#social-${platform}`);
|
||||
fireEvent.change(inputField, { target: { value: 'test', name: platform } });
|
||||
expect(changeHandler).toHaveBeenCalledTimes(1);
|
||||
|
||||
expect(socialLink.find('#visibilitySocialLinks select').props().value).toBe('private');
|
||||
const event = { target: { value: 'all_users', name: 'visibilitySocialLinks' } };
|
||||
socialLink.find('#visibilitySocialLinks select').simulate('change', event);
|
||||
const selectElement = container.querySelector('#visibilitySocialLinks');
|
||||
expect(selectElement.value).toBe('private');
|
||||
fireEvent.change(selectElement, { target: { value: 'all_users', name: 'visibilitySocialLinks' } });
|
||||
expect(changeHandler).toHaveBeenCalledTimes(2);
|
||||
|
||||
socialLink.find('[aria-labelledby="editing-form"]').simulate('submit');
|
||||
fireEvent.submit(container.querySelector('[aria-labelledby="editing-form"]'));
|
||||
expect(submitHandler).toHaveBeenCalledTimes(1);
|
||||
|
||||
socialLink.find('[aria-labelledby="editing-form"]').find('Button .btn-link').simulate('click');
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Cancel' }));
|
||||
expect(closeHandler).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('calls social links with static', () => {
|
||||
const openHandler = jest.fn();
|
||||
const component = (
|
||||
render(
|
||||
<SocialLinksWrapper
|
||||
{...defaultProps}
|
||||
formId="goals"
|
||||
openHandler={openHandler}
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
const wrapper = mount(component);
|
||||
const socialLink = wrapper.find(SocialLinks);
|
||||
const addFacebookButton = screen.getByRole('button', { name: 'Add Facebook' });
|
||||
fireEvent.click(addFacebookButton);
|
||||
|
||||
socialLink.find('EmptyContent button').first().simulate('click');
|
||||
expect(openHandler).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -160,10 +157,9 @@ describe('<SocialLinks />', () => {
|
||||
const newStore = JSON.parse(JSON.stringify(savingEditedBio));
|
||||
newStore.profilePage.errors.bio = { userMessage: 'error' };
|
||||
|
||||
const component = <SocialLinksWrapperWithStore store={newStore} />;
|
||||
const wrapper = mount(component);
|
||||
const socialLink = wrapper.find(SocialLinks);
|
||||
const { container } = render(<SocialLinksWrapperWithStore store={newStore} />);
|
||||
|
||||
expect(socialLink.find('.alert-danger').exists()).toBe(true);
|
||||
const alertDanger = container.querySelector('.alert-danger');
|
||||
expect(alertDanger).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,586 +1,504 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`<SocialLinks /> calls social links with edit mode bio 1`] = `
|
||||
<div
|
||||
className="pgn-transition-replace-group position-relative mb-5"
|
||||
style={
|
||||
Object {
|
||||
"height": null,
|
||||
}
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"padding": ".1px 0",
|
||||
}
|
||||
}
|
||||
class="pgn-transition-replace-group position-relative mb-5"
|
||||
>
|
||||
<div
|
||||
aria-labelledby="social-links-label"
|
||||
role="dialog"
|
||||
style="padding: .1px 0px;"
|
||||
>
|
||||
<form
|
||||
aria-labelledby="editing-form"
|
||||
onSubmit={[Function]}
|
||||
<div
|
||||
aria-labelledby="social-links-label"
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
className="editable-item-header mb-2"
|
||||
>
|
||||
<h2
|
||||
className="edit-section-header"
|
||||
id="social-links-label"
|
||||
>
|
||||
Social Links
|
||||
</h2>
|
||||
</div>
|
||||
<div
|
||||
id="social-error-feedback"
|
||||
/>
|
||||
<ul
|
||||
className="list-unstyled"
|
||||
>
|
||||
<li
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
htmlFor="social-facebook"
|
||||
>
|
||||
Facebook
|
||||
</label>
|
||||
<input
|
||||
aria-describedby="social-error-feedback"
|
||||
className="form-control"
|
||||
id="social-facebook"
|
||||
name="facebook"
|
||||
onChange={[Function]}
|
||||
type="text"
|
||||
value="https://www.facebook.com/aloha"
|
||||
/>
|
||||
</li>
|
||||
<li
|
||||
className="form-group"
|
||||
>
|
||||
<label
|
||||
htmlFor="social-twitter"
|
||||
>
|
||||
Twitter
|
||||
</label>
|
||||
<input
|
||||
aria-describedby="social-error-feedback"
|
||||
className="form-control"
|
||||
id="social-twitter"
|
||||
name="twitter"
|
||||
onChange={[Function]}
|
||||
type="text"
|
||||
value="https://www.twitter.com/ALOHA"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
className="d-flex flex-row-reverse flex-wrap justify-content-end align-items-center"
|
||||
<form
|
||||
aria-labelledby="editing-form"
|
||||
>
|
||||
<div
|
||||
className="form-group d-flex flex-wrap"
|
||||
class="editable-item-header mb-2"
|
||||
>
|
||||
<label
|
||||
className="col-form-label"
|
||||
htmlFor="visibilitySocialLinks"
|
||||
<h2
|
||||
class="edit-section-header"
|
||||
id="social-links-label"
|
||||
>
|
||||
Who can see this:
|
||||
</label>
|
||||
<span
|
||||
className="d-flex align-items-center"
|
||||
>
|
||||
<span
|
||||
className="d-inline-block ml-1 mr-2"
|
||||
style={
|
||||
Object {
|
||||
"width": "1.5rem",
|
||||
}
|
||||
}
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="svg-inline--fa fa-eye-slash fa-w-20 "
|
||||
data-icon="eye-slash"
|
||||
data-prefix="far"
|
||||
focusable="false"
|
||||
role="img"
|
||||
style={Object {}}
|
||||
viewBox="0 0 640 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M634 471L36 3.51A16 16 0 0 0 13.51 6l-10 12.49A16 16 0 0 0 6 41l598 467.49a16 16 0 0 0 22.49-2.49l10-12.49A16 16 0 0 0 634 471zM296.79 146.47l134.79 105.38C429.36 191.91 380.48 144 320 144a112.26 112.26 0 0 0-23.21 2.47zm46.42 219.07L208.42 260.16C210.65 320.09 259.53 368 320 368a113 113 0 0 0 23.21-2.46zM320 112c98.65 0 189.09 55 237.93 144a285.53 285.53 0 0 1-44 60.2l37.74 29.5a333.7 333.7 0 0 0 52.9-75.11 32.35 32.35 0 0 0 0-29.19C550.29 135.59 442.93 64 320 64c-36.7 0-71.71 7-104.63 18.81l46.41 36.29c18.94-4.3 38.34-7.1 58.22-7.1zm0 288c-98.65 0-189.08-55-237.93-144a285.47 285.47 0 0 1 44.05-60.19l-37.74-29.5a333.6 333.6 0 0 0-52.89 75.1 32.35 32.35 0 0 0 0 29.19C89.72 376.41 197.08 448 320 448c36.7 0 71.71-7.05 104.63-18.81l-46.41-36.28C359.28 397.2 339.89 400 320 400z"
|
||||
fill="currentColor"
|
||||
style={Object {}}
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<select
|
||||
className="d-inline-block form-control"
|
||||
id="visibilitySocialLinks"
|
||||
name="visibilitySocialLinks"
|
||||
onChange={[Function]}
|
||||
type="select"
|
||||
value="private"
|
||||
>
|
||||
<option
|
||||
value="private"
|
||||
>
|
||||
Just me
|
||||
</option>
|
||||
<option
|
||||
value="all_users"
|
||||
>
|
||||
Everyone on localhost
|
||||
</option>
|
||||
</select>
|
||||
</span>
|
||||
Social Links
|
||||
</h2>
|
||||
</div>
|
||||
<div
|
||||
className="form-group flex-shrink-0 flex-grow-1"
|
||||
id="social-error-feedback"
|
||||
/>
|
||||
<ul
|
||||
class="list-unstyled"
|
||||
>
|
||||
<button
|
||||
aria-disabled={false}
|
||||
aria-live="assertive"
|
||||
className="pgn__stateful-btn pgn__stateful-btn-state-pending btn btn-primary"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
type="submit"
|
||||
<li
|
||||
class="form-group"
|
||||
>
|
||||
<label
|
||||
for="social-facebook"
|
||||
>
|
||||
Facebook
|
||||
</label>
|
||||
<input
|
||||
aria-describedby="social-error-feedback"
|
||||
class="form-control"
|
||||
id="social-facebook"
|
||||
name="facebook"
|
||||
type="text"
|
||||
value="https://www.facebook.com/aloha"
|
||||
/>
|
||||
</li>
|
||||
<li
|
||||
class="form-group"
|
||||
>
|
||||
<label
|
||||
for="social-twitter"
|
||||
>
|
||||
Twitter
|
||||
</label>
|
||||
<input
|
||||
aria-describedby="social-error-feedback"
|
||||
class="form-control"
|
||||
id="social-twitter"
|
||||
name="twitter"
|
||||
type="text"
|
||||
value="https://www.twitter.com/ALOHA"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<div
|
||||
class="d-flex flex-row-reverse flex-wrap justify-content-end align-items-center"
|
||||
>
|
||||
<div
|
||||
class="form-group d-flex flex-wrap"
|
||||
>
|
||||
<label
|
||||
class="col-form-label"
|
||||
for="visibilitySocialLinks"
|
||||
>
|
||||
Who can see this:
|
||||
</label>
|
||||
<span
|
||||
className="d-flex align-items-center justify-content-center"
|
||||
class="d-flex align-items-center"
|
||||
>
|
||||
<span
|
||||
className="pgn__stateful-btn-icon"
|
||||
class="d-inline-block ml-1 mr-2"
|
||||
style="width: 1.5rem;"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="svg-inline--fa fa-eye-slash "
|
||||
data-icon="eye-slash"
|
||||
data-prefix="far"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 640 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zm151 118.3C226 97.7 269.5 80 320 80c65.2 0 118.8 29.6 159.9 67.7C518.4 183.5 545 226 558.6 256c-12.6 28-36.6 66.8-70.9 100.9l-53.8-42.2c9.1-17.6 14.2-37.5 14.2-58.7c0-70.7-57.3-128-128-128c-32.2 0-61.7 11.9-84.2 31.5l-46.1-36.1zM394.9 284.2l-81.5-63.9c4.2-8.5 6.6-18.2 6.6-28.3c0-5.5-.7-10.9-2-16c.7 0 1.3 0 2 0c44.2 0 80 35.8 80 80c0 9.9-1.8 19.4-5.1 28.2zm9.4 130.3C378.8 425.4 350.7 432 320 432c-65.2 0-118.8-29.6-159.9-67.7C121.6 328.5 95 286 81.4 256c8.3-18.4 21.5-41.5 39.4-64.8L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5l-41.9-33zM192 256c0 70.7 57.3 128 128 128c13.3 0 26.1-2 38.2-5.8L302 334c-23.5-5.4-43.1-21.2-53.7-42.3l-56.1-44.2c-.2 2.8-.3 5.6-.3 8.5z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
<select
|
||||
class="d-inline-block form-control"
|
||||
id="visibilitySocialLinks"
|
||||
name="visibilitySocialLinks"
|
||||
type="select"
|
||||
>
|
||||
<option
|
||||
value="private"
|
||||
>
|
||||
Just me
|
||||
</option>
|
||||
<option
|
||||
value="all_users"
|
||||
>
|
||||
Everyone on localhost
|
||||
</option>
|
||||
</select>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="form-group flex-shrink-0 flex-grow-1"
|
||||
>
|
||||
<button
|
||||
aria-disabled="false"
|
||||
aria-live="assertive"
|
||||
class="pgn__stateful-btn pgn__stateful-btn-state-pending btn btn-primary"
|
||||
type="submit"
|
||||
>
|
||||
<span
|
||||
class="d-flex align-items-center justify-content-center"
|
||||
>
|
||||
<span
|
||||
className="pgn__icon icon-spin"
|
||||
class="pgn__stateful-btn-icon"
|
||||
>
|
||||
<svg
|
||||
aria-hidden={true}
|
||||
fill="none"
|
||||
focusable={false}
|
||||
height={24}
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
width={24}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
<span
|
||||
class="pgn__icon icon-spin"
|
||||
>
|
||||
<path
|
||||
d="M22 12A10 10 0 1 1 6.122 3.91l1.176 1.618A8 8 0 1 0 20 12h2Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
fill="none"
|
||||
focusable="false"
|
||||
height="24"
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
width="24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M22 12A10 10 0 1 1 6.122 3.91l1.176 1.618A8 8 0 1 0 20 12h2Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
Saving
|
||||
</span>
|
||||
</span>
|
||||
<span>
|
||||
Saving
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-link"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
type="button"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-link"
|
||||
type="button"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<SocialLinks /> calls social links with edit mode certificates 1`] = `
|
||||
<div
|
||||
className="pgn-transition-replace-group position-relative mb-5"
|
||||
style={
|
||||
Object {
|
||||
"height": null,
|
||||
}
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"padding": ".1px 0",
|
||||
}
|
||||
}
|
||||
class="pgn-transition-replace-group position-relative mb-5"
|
||||
>
|
||||
<div
|
||||
className="editable-item-header mb-2"
|
||||
style="padding: .1px 0px;"
|
||||
>
|
||||
<h2
|
||||
className="edit-section-header"
|
||||
id={null}
|
||||
<div
|
||||
class="editable-item-header mb-2"
|
||||
>
|
||||
Social Links
|
||||
<button
|
||||
className="float-right px-0 btn btn-link btn-sm"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"marginTop": "-.35rem",
|
||||
}
|
||||
}
|
||||
type="button"
|
||||
<h2
|
||||
class="edit-section-header"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="svg-inline--fa fa-pencil-alt fa-w-16 mr-1"
|
||||
data-icon="pencil-alt"
|
||||
data-prefix="fas"
|
||||
focusable="false"
|
||||
role="img"
|
||||
style={Object {}}
|
||||
viewBox="0 0 512 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
Social Links
|
||||
<button
|
||||
class="float-right px-0 btn btn-link btn-sm"
|
||||
style="margin-top: -.35rem;"
|
||||
type="button"
|
||||
>
|
||||
<path
|
||||
d="M497.9 142.1l-46.1 46.1c-4.7 4.7-12.3 4.7-17 0l-111-111c-4.7-4.7-4.7-12.3 0-17l46.1-46.1c18.7-18.7 49.1-18.7 67.9 0l60.1 60.1c18.8 18.7 18.8 49.1 0 67.9zM284.2 99.8L21.6 362.4.4 483.9c-2.9 16.4 11.4 30.6 27.8 27.8l121.5-21.3 262.6-262.6c4.7-4.7 4.7-12.3 0-17l-111-111c-4.8-4.7-12.4-4.7-17.1 0zM124.1 339.9c-5.5-5.5-5.5-14.3 0-19.8l154-154c5.5-5.5 14.3-5.5 19.8 0s5.5 14.3 0 19.8l-154 154c-5.5 5.5-14.3 5.5-19.8 0zM88 424h48v36.3l-64.5 11.3-31.1-31.1L51.7 376H88v48z"
|
||||
fill="currentColor"
|
||||
style={Object {}}
|
||||
/>
|
||||
</svg>
|
||||
Edit
|
||||
</button>
|
||||
</h2>
|
||||
<p
|
||||
className="mb-0"
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="svg-inline--fa fa-pencil mr-1"
|
||||
data-icon="pencil"
|
||||
data-prefix="fas"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 512 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M410.3 231l11.3-11.3-33.9-33.9-62.1-62.1L291.7 89.8l-11.3 11.3-22.6 22.6L58.6 322.9c-10.4 10.4-18 23.3-22.2 37.4L1 480.7c-2.5 8.4-.2 17.5 6.1 23.7s15.3 8.5 23.7 6.1l120.3-35.4c14.1-4.2 27-11.8 37.4-22.2L387.7 253.7 410.3 231zM160 399.4l-9.1 22.7c-4 3.1-8.5 5.4-13.3 6.9L59.4 452l23-78.1c1.4-4.9 3.8-9.4 6.9-13.3l22.7-9.1 0 32c0 8.8 7.2 16 16 16l32 0zM362.7 18.7L348.3 33.2 325.7 55.8 314.3 67.1l33.9 33.9 62.1 62.1 33.9 33.9 11.3-11.3 22.6-22.6 14.5-14.5c25-25 25-65.5 0-90.5L453.3 18.7c-25-25-65.5-25-90.5 0zm-47.4 168l-144 144c-6.2 6.2-16.4 6.2-22.6 0s-6.2-16.4 0-22.6l144-144c6.2-6.2 16.4-6.2 22.6 0s6.2 16.4 0 22.6z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
Edit
|
||||
</button>
|
||||
</h2>
|
||||
<p
|
||||
class="mb-0"
|
||||
>
|
||||
<span
|
||||
class="ml-auto small text-muted"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="svg-inline--fa fa-eye-slash "
|
||||
data-icon="eye-slash"
|
||||
data-prefix="far"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 640 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zm151 118.3C226 97.7 269.5 80 320 80c65.2 0 118.8 29.6 159.9 67.7C518.4 183.5 545 226 558.6 256c-12.6 28-36.6 66.8-70.9 100.9l-53.8-42.2c9.1-17.6 14.2-37.5 14.2-58.7c0-70.7-57.3-128-128-128c-32.2 0-61.7 11.9-84.2 31.5l-46.1-36.1zM394.9 284.2l-81.5-63.9c4.2-8.5 6.6-18.2 6.6-28.3c0-5.5-.7-10.9-2-16c.7 0 1.3 0 2 0c44.2 0 80 35.8 80 80c0 9.9-1.8 19.4-5.1 28.2zm9.4 130.3C378.8 425.4 350.7 432 320 432c-65.2 0-118.8-29.6-159.9-67.7C121.6 328.5 95 286 81.4 256c8.3-18.4 21.5-41.5 39.4-64.8L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5l-41.9-33zM192 256c0 70.7 57.3 128 128 128c13.3 0 26.1-2 38.2-5.8L302 334c-23.5-5.4-43.1-21.2-53.7-42.3l-56.1-44.2c-.2 2.8-.3 5.6-.3 8.5z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
Just me
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<ul
|
||||
class="list-unstyled"
|
||||
>
|
||||
<span
|
||||
className="ml-auto small text-muted"
|
||||
<li
|
||||
class="form-group"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="svg-inline--fa fa-eye-slash fa-w-20 "
|
||||
data-icon="eye-slash"
|
||||
data-prefix="far"
|
||||
focusable="false"
|
||||
role="img"
|
||||
style={Object {}}
|
||||
viewBox="0 0 640 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
<a
|
||||
class="font-weight-bold"
|
||||
href="https://www.facebook.com/aloha"
|
||||
>
|
||||
<path
|
||||
d="M634 471L36 3.51A16 16 0 0 0 13.51 6l-10 12.49A16 16 0 0 0 6 41l598 467.49a16 16 0 0 0 22.49-2.49l10-12.49A16 16 0 0 0 634 471zM296.79 146.47l134.79 105.38C429.36 191.91 380.48 144 320 144a112.26 112.26 0 0 0-23.21 2.47zm46.42 219.07L208.42 260.16C210.65 320.09 259.53 368 320 368a113 113 0 0 0 23.21-2.46zM320 112c98.65 0 189.09 55 237.93 144a285.53 285.53 0 0 1-44 60.2l37.74 29.5a333.7 333.7 0 0 0 52.9-75.11 32.35 32.35 0 0 0 0-29.19C550.29 135.59 442.93 64 320 64c-36.7 0-71.71 7-104.63 18.81l46.41 36.29c18.94-4.3 38.34-7.1 58.22-7.1zm0 288c-98.65 0-189.08-55-237.93-144a285.47 285.47 0 0 1 44.05-60.19l-37.74-29.5a333.6 333.6 0 0 0-52.89 75.1 32.35 32.35 0 0 0 0 29.19C89.72 376.41 197.08 448 320 448c36.7 0 71.71-7.05 104.63-18.81l-46.41-36.28C359.28 397.2 339.89 400 320 400z"
|
||||
fill="currentColor"
|
||||
style={Object {}}
|
||||
/>
|
||||
</svg>
|
||||
|
||||
Just me
|
||||
</span>
|
||||
</p>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="svg-inline--fa fa-facebook mr-2"
|
||||
data-icon="facebook"
|
||||
data-prefix="fab"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 512 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M512 256C512 114.6 397.4 0 256 0S0 114.6 0 256C0 376 82.7 476.8 194.2 504.5V334.2H141.4V256h52.8V222.3c0-87.1 39.4-127.5 125-127.5c16.2 0 44.2 3.2 55.7 6.4V172c-6-.6-16.5-1-29.6-1c-42 0-58.2 15.9-58.2 57.2V256h83.6l-14.4 78.2H287V510.1C413.8 494.8 512 386.9 512 256h0z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
Facebook
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
class="form-group"
|
||||
>
|
||||
<a
|
||||
class="font-weight-bold"
|
||||
href="https://www.twitter.com/ALOHA"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="svg-inline--fa fa-twitter mr-2"
|
||||
data-icon="twitter"
|
||||
data-prefix="fab"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 512 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
Twitter
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<ul
|
||||
className="list-unstyled"
|
||||
>
|
||||
<li
|
||||
className="form-group"
|
||||
>
|
||||
<a
|
||||
className="font-weight-bold"
|
||||
href="https://www.facebook.com/aloha"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="svg-inline--fa fa-facebook fa-w-16 mr-2"
|
||||
data-icon="facebook"
|
||||
data-prefix="fab"
|
||||
focusable="false"
|
||||
role="img"
|
||||
style={Object {}}
|
||||
viewBox="0 0 512 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M504 256C504 119 393 8 256 8S8 119 8 256c0 123.78 90.69 226.38 209.25 245V327.69h-63V256h63v-54.64c0-62.15 37-96.48 93.67-96.48 27.14 0 55.52 4.84 55.52 4.84v61h-31.28c-30.8 0-40.41 19.12-40.41 38.73V256h68.78l-11 71.69h-57.78V501C413.31 482.38 504 379.78 504 256z"
|
||||
fill="currentColor"
|
||||
style={Object {}}
|
||||
/>
|
||||
</svg>
|
||||
Facebook
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
className="form-group"
|
||||
>
|
||||
<a
|
||||
className="font-weight-bold"
|
||||
href="https://www.twitter.com/ALOHA"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="svg-inline--fa fa-twitter fa-w-16 mr-2"
|
||||
data-icon="twitter"
|
||||
data-prefix="fab"
|
||||
focusable="false"
|
||||
role="img"
|
||||
style={Object {}}
|
||||
viewBox="0 0 512 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"
|
||||
fill="currentColor"
|
||||
style={Object {}}
|
||||
/>
|
||||
</svg>
|
||||
Twitter
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<SocialLinks /> calls social links with edit mode goals 1`] = `
|
||||
<div
|
||||
className="pgn-transition-replace-group position-relative mb-5"
|
||||
style={
|
||||
Object {
|
||||
"height": null,
|
||||
}
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"padding": ".1px 0",
|
||||
}
|
||||
}
|
||||
class="pgn-transition-replace-group position-relative mb-5"
|
||||
>
|
||||
<div
|
||||
className="editable-item-header mb-2"
|
||||
style="padding: .1px 0px;"
|
||||
>
|
||||
<h2
|
||||
className="edit-section-header"
|
||||
id={null}
|
||||
<div
|
||||
class="editable-item-header mb-2"
|
||||
>
|
||||
Social Links
|
||||
</h2>
|
||||
<h2
|
||||
class="edit-section-header"
|
||||
>
|
||||
Social Links
|
||||
</h2>
|
||||
</div>
|
||||
<ul
|
||||
class="list-unstyled"
|
||||
>
|
||||
<li
|
||||
class="mb-4"
|
||||
>
|
||||
<div>
|
||||
<button
|
||||
class="pl-0 text-left btn btn-link"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="svg-inline--fa fa-plus fa-xs mr-2"
|
||||
data-icon="plus"
|
||||
data-prefix="fas"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 448 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M256 80c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 144L48 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l144 0 0 144c0 17.7 14.3 32 32 32s32-14.3 32-32l0-144 144 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-144 0 0-144z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
Add Facebook
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
class="mb-4"
|
||||
>
|
||||
<div>
|
||||
<button
|
||||
class="pl-0 text-left btn btn-link"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="svg-inline--fa fa-plus fa-xs mr-2"
|
||||
data-icon="plus"
|
||||
data-prefix="fas"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 448 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M256 80c0-17.7-14.3-32-32-32s-32 14.3-32 32l0 144L48 224c-17.7 0-32 14.3-32 32s14.3 32 32 32l144 0 0 144c0 17.7 14.3 32 32 32s32-14.3 32-32l0-144 144 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-144 0 0-144z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
Add Twitter
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<ul
|
||||
className="list-unstyled"
|
||||
>
|
||||
<li
|
||||
className="mb-4"
|
||||
>
|
||||
<div>
|
||||
<button
|
||||
className="pl-0 text-left btn btn-link"
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
tabIndex={0}
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="svg-inline--fa fa-plus fa-w-14 fa-xs mr-2"
|
||||
data-icon="plus"
|
||||
data-prefix="fas"
|
||||
focusable="false"
|
||||
role="img"
|
||||
style={Object {}}
|
||||
viewBox="0 0 448 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M416 208H272V64c0-17.67-14.33-32-32-32h-32c-17.67 0-32 14.33-32 32v144H32c-17.67 0-32 14.33-32 32v32c0 17.67 14.33 32 32 32h144v144c0 17.67 14.33 32 32 32h32c17.67 0 32-14.33 32-32V304h144c17.67 0 32-14.33 32-32v-32c0-17.67-14.33-32-32-32z"
|
||||
fill="currentColor"
|
||||
style={Object {}}
|
||||
/>
|
||||
</svg>
|
||||
Add Facebook
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
<li
|
||||
className="mb-4"
|
||||
>
|
||||
<div>
|
||||
<button
|
||||
className="pl-0 text-left btn btn-link"
|
||||
onClick={[Function]}
|
||||
onKeyDown={[Function]}
|
||||
tabIndex={0}
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="svg-inline--fa fa-plus fa-w-14 fa-xs mr-2"
|
||||
data-icon="plus"
|
||||
data-prefix="fas"
|
||||
focusable="false"
|
||||
role="img"
|
||||
style={Object {}}
|
||||
viewBox="0 0 448 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M416 208H272V64c0-17.67-14.33-32-32-32h-32c-17.67 0-32 14.33-32 32v144H32c-17.67 0-32 14.33-32 32v32c0 17.67 14.33 32 32 32h144v144c0 17.67 14.33 32 32 32h32c17.67 0 32-14.33 32-32V304h144c17.67 0 32-14.33 32-32v-32c0-17.67-14.33-32-32-32z"
|
||||
fill="currentColor"
|
||||
style={Object {}}
|
||||
/>
|
||||
</svg>
|
||||
Add Twitter
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<SocialLinks /> calls social links with edit mode socialLinks 1`] = `
|
||||
<div
|
||||
className="pgn-transition-replace-group position-relative mb-5"
|
||||
style={
|
||||
Object {
|
||||
"height": null,
|
||||
}
|
||||
}
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
style={
|
||||
Object {
|
||||
"padding": ".1px 0",
|
||||
}
|
||||
}
|
||||
class="pgn-transition-replace-group position-relative mb-5"
|
||||
>
|
||||
<div
|
||||
className="editable-item-header mb-2"
|
||||
style="padding: .1px 0px;"
|
||||
>
|
||||
<h2
|
||||
className="edit-section-header"
|
||||
id={null}
|
||||
<div
|
||||
class="editable-item-header mb-2"
|
||||
>
|
||||
Social Links
|
||||
<button
|
||||
className="float-right px-0 btn btn-link btn-sm"
|
||||
disabled={false}
|
||||
onClick={[Function]}
|
||||
style={
|
||||
Object {
|
||||
"marginTop": "-.35rem",
|
||||
}
|
||||
}
|
||||
type="button"
|
||||
<h2
|
||||
class="edit-section-header"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="svg-inline--fa fa-pencil-alt fa-w-16 mr-1"
|
||||
data-icon="pencil-alt"
|
||||
data-prefix="fas"
|
||||
focusable="false"
|
||||
role="img"
|
||||
style={Object {}}
|
||||
viewBox="0 0 512 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
Social Links
|
||||
<button
|
||||
class="float-right px-0 btn btn-link btn-sm"
|
||||
style="margin-top: -.35rem;"
|
||||
type="button"
|
||||
>
|
||||
<path
|
||||
d="M497.9 142.1l-46.1 46.1c-4.7 4.7-12.3 4.7-17 0l-111-111c-4.7-4.7-4.7-12.3 0-17l46.1-46.1c18.7-18.7 49.1-18.7 67.9 0l60.1 60.1c18.8 18.7 18.8 49.1 0 67.9zM284.2 99.8L21.6 362.4.4 483.9c-2.9 16.4 11.4 30.6 27.8 27.8l121.5-21.3 262.6-262.6c4.7-4.7 4.7-12.3 0-17l-111-111c-4.8-4.7-12.4-4.7-17.1 0zM124.1 339.9c-5.5-5.5-5.5-14.3 0-19.8l154-154c5.5-5.5 14.3-5.5 19.8 0s5.5 14.3 0 19.8l-154 154c-5.5 5.5-14.3 5.5-19.8 0zM88 424h48v36.3l-64.5 11.3-31.1-31.1L51.7 376H88v48z"
|
||||
fill="currentColor"
|
||||
style={Object {}}
|
||||
/>
|
||||
</svg>
|
||||
Edit
|
||||
</button>
|
||||
</h2>
|
||||
<p
|
||||
className="mb-0"
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="svg-inline--fa fa-pencil mr-1"
|
||||
data-icon="pencil"
|
||||
data-prefix="fas"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 512 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M410.3 231l11.3-11.3-33.9-33.9-62.1-62.1L291.7 89.8l-11.3 11.3-22.6 22.6L58.6 322.9c-10.4 10.4-18 23.3-22.2 37.4L1 480.7c-2.5 8.4-.2 17.5 6.1 23.7s15.3 8.5 23.7 6.1l120.3-35.4c14.1-4.2 27-11.8 37.4-22.2L387.7 253.7 410.3 231zM160 399.4l-9.1 22.7c-4 3.1-8.5 5.4-13.3 6.9L59.4 452l23-78.1c1.4-4.9 3.8-9.4 6.9-13.3l22.7-9.1 0 32c0 8.8 7.2 16 16 16l32 0zM362.7 18.7L348.3 33.2 325.7 55.8 314.3 67.1l33.9 33.9 62.1 62.1 33.9 33.9 11.3-11.3 22.6-22.6 14.5-14.5c25-25 25-65.5 0-90.5L453.3 18.7c-25-25-65.5-25-90.5 0zm-47.4 168l-144 144c-6.2 6.2-16.4 6.2-22.6 0s-6.2-16.4 0-22.6l144-144c6.2-6.2 16.4-6.2 22.6 0s6.2 16.4 0 22.6z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
Edit
|
||||
</button>
|
||||
</h2>
|
||||
<p
|
||||
class="mb-0"
|
||||
>
|
||||
<span
|
||||
class="ml-auto small text-muted"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="svg-inline--fa fa-eye-slash "
|
||||
data-icon="eye-slash"
|
||||
data-prefix="far"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 640 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zm151 118.3C226 97.7 269.5 80 320 80c65.2 0 118.8 29.6 159.9 67.7C518.4 183.5 545 226 558.6 256c-12.6 28-36.6 66.8-70.9 100.9l-53.8-42.2c9.1-17.6 14.2-37.5 14.2-58.7c0-70.7-57.3-128-128-128c-32.2 0-61.7 11.9-84.2 31.5l-46.1-36.1zM394.9 284.2l-81.5-63.9c4.2-8.5 6.6-18.2 6.6-28.3c0-5.5-.7-10.9-2-16c.7 0 1.3 0 2 0c44.2 0 80 35.8 80 80c0 9.9-1.8 19.4-5.1 28.2zm9.4 130.3C378.8 425.4 350.7 432 320 432c-65.2 0-118.8-29.6-159.9-67.7C121.6 328.5 95 286 81.4 256c8.3-18.4 21.5-41.5 39.4-64.8L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5l-41.9-33zM192 256c0 70.7 57.3 128 128 128c13.3 0 26.1-2 38.2-5.8L302 334c-23.5-5.4-43.1-21.2-53.7-42.3l-56.1-44.2c-.2 2.8-.3 5.6-.3 8.5z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
Just me
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<ul
|
||||
class="list-unstyled"
|
||||
>
|
||||
<span
|
||||
className="ml-auto small text-muted"
|
||||
<li
|
||||
class="form-group"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="svg-inline--fa fa-eye-slash fa-w-20 "
|
||||
data-icon="eye-slash"
|
||||
data-prefix="far"
|
||||
focusable="false"
|
||||
role="img"
|
||||
style={Object {}}
|
||||
viewBox="0 0 640 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
<a
|
||||
class="font-weight-bold"
|
||||
href="https://www.facebook.com/aloha"
|
||||
>
|
||||
<path
|
||||
d="M634 471L36 3.51A16 16 0 0 0 13.51 6l-10 12.49A16 16 0 0 0 6 41l598 467.49a16 16 0 0 0 22.49-2.49l10-12.49A16 16 0 0 0 634 471zM296.79 146.47l134.79 105.38C429.36 191.91 380.48 144 320 144a112.26 112.26 0 0 0-23.21 2.47zm46.42 219.07L208.42 260.16C210.65 320.09 259.53 368 320 368a113 113 0 0 0 23.21-2.46zM320 112c98.65 0 189.09 55 237.93 144a285.53 285.53 0 0 1-44 60.2l37.74 29.5a333.7 333.7 0 0 0 52.9-75.11 32.35 32.35 0 0 0 0-29.19C550.29 135.59 442.93 64 320 64c-36.7 0-71.71 7-104.63 18.81l46.41 36.29c18.94-4.3 38.34-7.1 58.22-7.1zm0 288c-98.65 0-189.08-55-237.93-144a285.47 285.47 0 0 1 44.05-60.19l-37.74-29.5a333.6 333.6 0 0 0-52.89 75.1 32.35 32.35 0 0 0 0 29.19C89.72 376.41 197.08 448 320 448c36.7 0 71.71-7.05 104.63-18.81l-46.41-36.28C359.28 397.2 339.89 400 320 400z"
|
||||
fill="currentColor"
|
||||
style={Object {}}
|
||||
/>
|
||||
</svg>
|
||||
|
||||
Just me
|
||||
</span>
|
||||
</p>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="svg-inline--fa fa-facebook mr-2"
|
||||
data-icon="facebook"
|
||||
data-prefix="fab"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 512 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M512 256C512 114.6 397.4 0 256 0S0 114.6 0 256C0 376 82.7 476.8 194.2 504.5V334.2H141.4V256h52.8V222.3c0-87.1 39.4-127.5 125-127.5c16.2 0 44.2 3.2 55.7 6.4V172c-6-.6-16.5-1-29.6-1c-42 0-58.2 15.9-58.2 57.2V256h83.6l-14.4 78.2H287V510.1C413.8 494.8 512 386.9 512 256h0z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
Facebook
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
class="form-group"
|
||||
>
|
||||
<a
|
||||
class="font-weight-bold"
|
||||
href="https://www.twitter.com/ALOHA"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="svg-inline--fa fa-twitter mr-2"
|
||||
data-icon="twitter"
|
||||
data-prefix="fab"
|
||||
focusable="false"
|
||||
role="img"
|
||||
viewBox="0 0 512 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
Twitter
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<ul
|
||||
className="list-unstyled"
|
||||
>
|
||||
<li
|
||||
className="form-group"
|
||||
>
|
||||
<a
|
||||
className="font-weight-bold"
|
||||
href="https://www.facebook.com/aloha"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="svg-inline--fa fa-facebook fa-w-16 mr-2"
|
||||
data-icon="facebook"
|
||||
data-prefix="fab"
|
||||
focusable="false"
|
||||
role="img"
|
||||
style={Object {}}
|
||||
viewBox="0 0 512 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M504 256C504 119 393 8 256 8S8 119 8 256c0 123.78 90.69 226.38 209.25 245V327.69h-63V256h63v-54.64c0-62.15 37-96.48 93.67-96.48 27.14 0 55.52 4.84 55.52 4.84v61h-31.28c-30.8 0-40.41 19.12-40.41 38.73V256h68.78l-11 71.69h-57.78V501C413.31 482.38 504 379.78 504 256z"
|
||||
fill="currentColor"
|
||||
style={Object {}}
|
||||
/>
|
||||
</svg>
|
||||
Facebook
|
||||
</a>
|
||||
</li>
|
||||
<li
|
||||
className="form-group"
|
||||
>
|
||||
<a
|
||||
className="font-weight-bold"
|
||||
href="https://www.twitter.com/ALOHA"
|
||||
>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
className="svg-inline--fa fa-twitter fa-w-16 mr-2"
|
||||
data-icon="twitter"
|
||||
data-prefix="fab"
|
||||
focusable="false"
|
||||
role="img"
|
||||
style={Object {}}
|
||||
viewBox="0 0 512 512"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"
|
||||
fill="currentColor"
|
||||
style={Object {}}
|
||||
/>
|
||||
</svg>
|
||||
Twitter
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faPencilAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Button } from '@edx/paragon';
|
||||
import { Button } from '@openedx/paragon';
|
||||
|
||||
import messages from './EditButton.messages';
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button, StatefulButton } from '@edx/paragon';
|
||||
import { Button, StatefulButton } from '@openedx/paragon';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import messages from './FormControls.messages';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { TransitionReplace } from '@edx/paragon';
|
||||
import { TransitionReplace } from '@openedx/paragon';
|
||||
|
||||
const onChildExit = (htmlNode) => {
|
||||
// If the leaving child has focus, take control and redirect it
|
||||
|
||||
@@ -3,4 +3,3 @@ export { default as saga } from './data/sagas';
|
||||
export { default as ProfilePage } from './ProfilePage';
|
||||
export { default as NotFoundPage } from './NotFoundPage';
|
||||
export { default as messages } from './ProfilePage.messages';
|
||||
export { default as ProfilePluginPage } from './ProfilePluginPage';
|
||||
|
||||
@@ -162,28 +162,4 @@
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.pgn-icons-cell-vertical {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
margin: 1px;
|
||||
}
|
||||
.pgn-icons-cell-horizontal {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
margin: 1px;
|
||||
}
|
||||
|
||||
.profile-plugin-avatar {
|
||||
@include media-breakpoint-up(xs) {
|
||||
max-width: 12rem;
|
||||
margin-right: 0;
|
||||
margin-top: -4rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,16 +3,19 @@ import {
|
||||
AuthenticatedPageRoute,
|
||||
PageWrap,
|
||||
} from '@edx/frontend-platform/react';
|
||||
import { Routes, Route } from 'react-router-dom';
|
||||
import { ProfilePage, NotFoundPage, ProfilePluginPage } from '../profile';
|
||||
import { Routes, Route, useNavigate } from 'react-router-dom';
|
||||
import { ProfilePage, NotFoundPage } from '../profile';
|
||||
|
||||
const AppRoutes = () => (
|
||||
<Routes>
|
||||
<Route path="/u/:username" element={<AuthenticatedPageRoute><ProfilePage /></AuthenticatedPageRoute>} />
|
||||
<Route path="/u/:username/plugin" element={<AuthenticatedPageRoute><ProfilePluginPage /></AuthenticatedPageRoute>} />
|
||||
<Route path="/notfound" element={<PageWrap><NotFoundPage /></PageWrap>} />
|
||||
<Route path="*" element={<PageWrap><NotFoundPage /></PageWrap>} />
|
||||
</Routes>
|
||||
);
|
||||
const AppRoutes = () => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<Routes>
|
||||
<Route path="/u/:username" element={<AuthenticatedPageRoute><ProfilePage navigate={navigate} /></AuthenticatedPageRoute>} />
|
||||
<Route path="/notfound" element={<PageWrap><NotFoundPage /></PageWrap>} />
|
||||
<Route path="*" element={<PageWrap><NotFoundPage /></PageWrap>} />
|
||||
</Routes>
|
||||
);
|
||||
};
|
||||
|
||||
export default AppRoutes;
|
||||
|
||||
@@ -15,7 +15,6 @@ jest.mock('@edx/frontend-platform/auth', () => ({
|
||||
jest.mock('../profile', () => ({
|
||||
ProfilePage: () => (<div>Profile page</div>),
|
||||
NotFoundPage: () => (<div>Not found page</div>),
|
||||
ProfilePluginPage: () => (<div>Plugin page</div>),
|
||||
}));
|
||||
|
||||
const RoutesWithProvider = (context, path) => (
|
||||
@@ -55,22 +54,6 @@ describe('routes', () => {
|
||||
expect(screen.getByText('Profile page')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('Profile Plugin page should be accessible for authenticated users', () => {
|
||||
render(
|
||||
RoutesWithProvider(
|
||||
{
|
||||
authenticatedUser: {
|
||||
username: 'edx',
|
||||
email: 'edx@example.com',
|
||||
},
|
||||
config: getConfig(),
|
||||
},
|
||||
'/u/edx/plugin',
|
||||
),
|
||||
);
|
||||
expect(screen.getByText('Plugin page')).toBeTruthy();
|
||||
});
|
||||
|
||||
test('should show NotFound page for a bad route', () => {
|
||||
render(
|
||||
RoutesWithProvider(unauthenticatedUser, '/nonMatchingRoute'),
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import 'core-js/stable';
|
||||
import 'regenerator-runtime/runtime';
|
||||
|
||||
import Enzyme from 'enzyme';
|
||||
import Adapter from '@wojtekmaj/enzyme-adapter-react-17';
|
||||
|
||||
Enzyme.configure({ adapter: new Adapter() });
|
||||
import '@testing-library/jest-dom';
|
||||
|
||||
Reference in New Issue
Block a user