Compare commits

..

14 Commits

Author SHA1 Message Date
Jason Wesson
2904168ed4 update: profile POC from master (#926)
* Update dependencies and visibility in Profile page

---------

Co-authored-by: jsnwesson <jsnwesson@users.noreply.github.com>
2023-11-27 09:23:50 -08:00
Maxwell Frank
ad80493869 Merge pull request #920 from openedx/mfrank/remove-scrollbars
fix: remove scrollbars
2023-11-22 13:00:35 -05:00
Maxwell Frank
a8b803d344 fix: linting errors 2023-11-22 16:17:06 +00:00
Maxwell Frank
0340b94add fix: remove scrollbars 2023-11-22 16:01:47 +00:00
Jason Wesson
b665d3897b Jwesson/fix target link (#914)
* fix: add _parent to social media links

---------

Co-authored-by: Jason Wesson <jwesson@2u.com>
2023-11-14 08:38:07 -08:00
Jason Wesson
542fa3d5ce fix: "view public profile" link sends parent frame to URL (#913)
Co-authored-by: Jason Wesson <jwesson@2u.com>
2023-11-14 07:08:09 -08:00
Jason Wesson
d5949f55c2 Merge master into PluggablePOCFeature (#908)
---------

Co-authored-by: jsnwesson <jsnwesson@users.noreply.github.com>
2023-11-07 20:03:44 -08:00
Jason Wesson
cd28310937 fix: hyperlink in plugin page should redirect to user profile (#897)
* add user's full name to plugn page

Co-authored-by: Jason Wesson <jwesson@2u.com>
2023-10-24 13:04:55 -07:00
Jason Wesson
b75347ad06 Merge master into PluginPOCFeature (#896) 2023-10-24 09:51:12 -07:00
Chris Deery
bd931338d8 Merge pull request #885 from openedx/PluginPOC/addTag
feat: add little tag to identify POC
2023-10-19 12:08:44 -04:00
Chris Deery
f03d5afa0d fix: update snapshot 2023-10-19 15:58:39 +00:00
Chris Deery
910e17f75d feat: add little tag to identify POC 2023-10-19 15:34:04 +00:00
Jason Wesson
2fa5cadf22 fix: rebase pluginPOCFeature with master branch (#884)
* fix(deps): update dependency @edx/paragon to v20.46.3

* chore(deps): update commitlint monorepo to v17.8.0

* fix(deps): update dependency @edx/frontend-component-footer to v12.4.0

* chore: bump frontend-platform (#869)

* fix: Add ID attribute to the main content (#845)

* chore: update browserslist DB (#871)

* fix(deps): update dependency @edx/frontend-platform to v5.6.1 (#875)

* build(deps): bump @babel/traverse from 7.22.5 to 7.23.2 (#879)

* bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.22.5 to 7.23.2.

* fix(deps): update dependency core-js to v3.33.0 (#876)
2023-10-18 11:58:21 -07:00
Jason Wesson
bd8221997e Move changes from ProfilePluginPOC to aperture/PluginPOCFeature (#883)
* build: create profile plugin page
* build: add plugins folder to Profile
* build: wrap Profile Plugin Page with Plugin

Co-authored-by: Jason Wesson <jwesson@2u.com>
2023-10-18 11:43:43 -07:00
30 changed files with 1648 additions and 843 deletions

View File

@@ -28,4 +28,3 @@ APP_ID=''
MFE_CONFIG_API_URL=''
SEARCH_CATALOG_URL='http://localhost:18000/courses'
ENABLE_SKILLS_BUILDER_PROFILE=''
ENABLE_PACT_STUBS=true

View File

@@ -7,7 +7,7 @@ transifex_utils = ./node_modules/.bin/transifex-utils.js
i18n = ./src/i18n
transifex_input = $(i18n)/transifex_input.json
# This directory must match .babelrc .
transifex_temp = ./temp/babel-plugin-react-intl
transifex_temp = ./temp/babel-plugin-formatjs
NPM_TESTS=build i18n_extract lint test

View File

@@ -1,5 +1,6 @@
#####################
frontend-app-profile
##########################
#####################
|license-badge| |status-badge| |ci-badge| |codecov-badge|
@@ -17,8 +18,9 @@ frontend-app-profile
:target: https://codecov.io/github/openedx/frontend-app-profile?branch=main
:alt: Codecov
********
Purpose
=======
********
This is a micro-frontend application responsible for the display and updating of user profiles.
@@ -26,11 +28,12 @@ When a user views their own profile, they're given fields to edit their full nam
When a user views someone else's profile, they see all those fields that that user set as public.
***************
Getting Started
===============
***************
Devstack Installation
---------------------
Installation
============
Follow these steps to provision, run, and enable an instance of the
Profile MFE for local development via the `devstack`_.
@@ -46,13 +49,30 @@ Profile MFE for local development via the `devstack`_.
.. code-block::
npm install
npm start # The server will run on port 1995
1. Clone your new repo:
``git clone https://github.com/openedx/frontend-app-profile.git``
2. Use node v18.x.
The current version of the micro-frontend build scripts support node 18.
Using other major versions of node *may* work, but this is unsupported. For
convenience, this repository includes an .nvmrc file to help in setting the
correct node version via `nvm <https://github.com/nvm-sh/nvm>`_.
3. Install npm dependencies:
``cd frontend-app-profile && npm ci``
4. Start the dev server:
``npm start``
The server will run on port 1995
Once the dev server is up, visit http://localhost:1995/u/staff.
Configuration
-------------
=============
This MFE is configured via node environment variables supplied at build time. See the .env file for the list of required environment variables. Example build syntax with a single environment variable:

881
package-lock.json generated
View File

@@ -9,9 +9,9 @@
"version": "1.0.0-semantically-released",
"license": "AGPL-3.0",
"dependencies": {
"@edx/brand": "npm:@edx/brand-openedx@1.2.0",
"@edx/frontend-component-footer": "12.4.0",
"@edx/frontend-component-header": "4.7.1",
"@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",
@@ -21,7 +21,7 @@
"@fortawesome/react-fontawesome": "0.2.0",
"@pact-foundation/pact": "^11.0.2",
"classnames": "2.3.2",
"core-js": "3.33.0",
"core-js": "3.33.3",
"history": "5.3.0",
"lodash.camelcase": "4.3.0",
"lodash.get": "4.4.2",
@@ -30,6 +30,7 @@
"prop-types": "15.8.1",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-error-boundary": "^4.0.11",
"react-helmet": "6.1.0",
"react-redux": "7.2.9",
"react-router": "6.16.0",
@@ -44,14 +45,13 @@
"universal-cookie": "4.0.4"
},
"devDependencies": {
"@commitlint/cli": "17.8.0",
"@commitlint/config-angular": "17.8.0",
"@commitlint/cli": "17.8.1",
"@commitlint/config-angular": "17.8.1",
"@edx/browserslist-config": "^1.1.1",
"@edx/frontend-build": "12.9.17",
"@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",
"codecov": "3.8.3",
"enzyme": "3.11.0",
"glob": "10.3.10",
"react-test-renderer": "17.0.2",
@@ -2109,16 +2109,16 @@
}
},
"node_modules/@commitlint/cli": {
"version": "17.8.0",
"resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-17.8.0.tgz",
"integrity": "sha512-D3LdyZYbiRyAChfJMNlAd9f2P9vNQ7GWbI9gN2o7L5hF07QJDqj4z/pcJF3PjDbJWOaUUXla287RdDmmKqH2WQ==",
"version": "17.8.1",
"resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-17.8.1.tgz",
"integrity": "sha512-ay+WbzQesE0Rv4EQKfNbSMiJJ12KdKTDzIt0tcK4k11FdsWmtwP0Kp1NWMOUswfIWo6Eb7p7Ln721Nx9FLNBjg==",
"dev": true,
"dependencies": {
"@commitlint/format": "^17.4.4",
"@commitlint/lint": "^17.8.0",
"@commitlint/load": "^17.8.0",
"@commitlint/read": "^17.5.1",
"@commitlint/types": "^17.4.4",
"@commitlint/format": "^17.8.1",
"@commitlint/lint": "^17.8.1",
"@commitlint/load": "^17.8.1",
"@commitlint/read": "^17.8.1",
"@commitlint/types": "^17.8.1",
"execa": "^5.0.0",
"lodash.isfunction": "^3.0.9",
"resolve-from": "5.0.0",
@@ -2133,33 +2133,33 @@
}
},
"node_modules/@commitlint/config-angular": {
"version": "17.8.0",
"resolved": "https://registry.npmjs.org/@commitlint/config-angular/-/config-angular-17.8.0.tgz",
"integrity": "sha512-3Jygi+j6AqbVbzZujQZqQhk1WPF4ipeh8MHcXlHa1humQQ2ROqfWUqdFlAxn9iIE93lbJz/ANGthJ9+k9sG7Zg==",
"version": "17.8.1",
"resolved": "https://registry.npmjs.org/@commitlint/config-angular/-/config-angular-17.8.1.tgz",
"integrity": "sha512-M3gfC2YttKOEDHrtOtCHfoIdF74vUc+ffJHTyU6j+xqlXrc34iOS665r1rRaTgX6CEeUAnNdpWn0zKYZTj27lA==",
"dev": true,
"dependencies": {
"@commitlint/config-angular-type-enum": "^17.4.0"
"@commitlint/config-angular-type-enum": "^17.8.1"
},
"engines": {
"node": ">=v14"
}
},
"node_modules/@commitlint/config-angular-type-enum": {
"version": "17.4.0",
"resolved": "https://registry.npmjs.org/@commitlint/config-angular-type-enum/-/config-angular-type-enum-17.4.0.tgz",
"integrity": "sha512-qbmfOfVqQHMKfc6CxS0A9b7+EFsOyEBoh4+i8Qa05uk8YhT/zY1CeIXK5V3wwemMDcHUegyL/ZnwCvWD7g8GxA==",
"version": "17.8.1",
"resolved": "https://registry.npmjs.org/@commitlint/config-angular-type-enum/-/config-angular-type-enum-17.8.1.tgz",
"integrity": "sha512-vuIQ9rZckMPRM6I3mWzfWb7TfvE/yHJXNKaR2weuhf+lQEVSZSGQP3LwdXpMcT2QGKE9VAhU/jw0qIE0Z9EbHA==",
"dev": true,
"engines": {
"node": ">=v14"
}
},
"node_modules/@commitlint/config-validator": {
"version": "17.6.7",
"resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.6.7.tgz",
"integrity": "sha512-vJSncmnzwMvpr3lIcm0I8YVVDJTzyjy7NZAeXbTXy+MPUdAr9pKyyg7Tx/ebOQ9kqzE6O9WT6jg2164br5UdsQ==",
"version": "17.8.1",
"resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.8.1.tgz",
"integrity": "sha512-UUgUC+sNiiMwkyiuIFR7JG2cfd9t/7MV8VB4TZ+q02ZFkHoduUS4tJGsCBWvBOGD9Btev6IecPMvlWUfJorkEA==",
"dev": true,
"dependencies": {
"@commitlint/types": "^17.4.4",
"@commitlint/types": "^17.8.1",
"ajv": "^8.11.0"
},
"engines": {
@@ -2167,12 +2167,12 @@
}
},
"node_modules/@commitlint/ensure": {
"version": "17.6.7",
"resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-17.6.7.tgz",
"integrity": "sha512-mfDJOd1/O/eIb/h4qwXzUxkmskXDL9vNPnZ4AKYKiZALz4vHzwMxBSYtyL2mUIDeU9DRSpEUins8SeKtFkYHSw==",
"version": "17.8.1",
"resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-17.8.1.tgz",
"integrity": "sha512-xjafwKxid8s1K23NFpL8JNo6JnY/ysetKo8kegVM7c8vs+kWLP8VrQq+NbhgVlmCojhEDbzQKp4eRXSjVOGsow==",
"dev": true,
"dependencies": {
"@commitlint/types": "^17.4.4",
"@commitlint/types": "^17.8.1",
"lodash.camelcase": "^4.3.0",
"lodash.kebabcase": "^4.1.1",
"lodash.snakecase": "^4.1.1",
@@ -2184,21 +2184,21 @@
}
},
"node_modules/@commitlint/execute-rule": {
"version": "17.4.0",
"resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-17.4.0.tgz",
"integrity": "sha512-LIgYXuCSO5Gvtc0t9bebAMSwd68ewzmqLypqI2Kke1rqOqqDbMpYcYfoPfFlv9eyLIh4jocHWwCK5FS7z9icUA==",
"version": "17.8.1",
"resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-17.8.1.tgz",
"integrity": "sha512-JHVupQeSdNI6xzA9SqMF+p/JjrHTcrJdI02PwesQIDCIGUrv04hicJgCcws5nzaoZbROapPs0s6zeVHoxpMwFQ==",
"dev": true,
"engines": {
"node": ">=v14"
}
},
"node_modules/@commitlint/format": {
"version": "17.4.4",
"resolved": "https://registry.npmjs.org/@commitlint/format/-/format-17.4.4.tgz",
"integrity": "sha512-+IS7vpC4Gd/x+uyQPTAt3hXs5NxnkqAZ3aqrHd5Bx/R9skyCAWusNlNbw3InDbAK6j166D9asQM8fnmYIa+CXQ==",
"version": "17.8.1",
"resolved": "https://registry.npmjs.org/@commitlint/format/-/format-17.8.1.tgz",
"integrity": "sha512-f3oMTyZ84M9ht7fb93wbCKmWxO5/kKSbwuYvS867duVomoOsgrgljkGGIztmT/srZnaiGbaK8+Wf8Ik2tSr5eg==",
"dev": true,
"dependencies": {
"@commitlint/types": "^17.4.4",
"@commitlint/types": "^17.8.1",
"chalk": "^4.1.0"
},
"engines": {
@@ -2206,12 +2206,12 @@
}
},
"node_modules/@commitlint/is-ignored": {
"version": "17.8.0",
"resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.8.0.tgz",
"integrity": "sha512-8bR6rxNcWaNprPBdE4ePIOwbxutTQGOsRPYWssX+zjGxnEljzaZSGzFUOMxapYILlf8Tts/O1wPQgG549Rdvdg==",
"version": "17.8.1",
"resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.8.1.tgz",
"integrity": "sha512-UshMi4Ltb4ZlNn4F7WtSEugFDZmctzFpmbqvpyxD3la510J+PLcnyhf9chs7EryaRFJMdAKwsEKfNK0jL/QM4g==",
"dev": true,
"dependencies": {
"@commitlint/types": "^17.4.4",
"@commitlint/types": "^17.8.1",
"semver": "7.5.4"
},
"engines": {
@@ -2219,30 +2219,30 @@
}
},
"node_modules/@commitlint/lint": {
"version": "17.8.0",
"resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-17.8.0.tgz",
"integrity": "sha512-4ihwnqOY4TcJN6iz5Jv1LeYavvBllONwFyGxOIWmCT5s4PNMb43cws2TUdbXTZL1Vq59etGKd5LWYDFPVbs5EA==",
"version": "17.8.1",
"resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-17.8.1.tgz",
"integrity": "sha512-aQUlwIR1/VMv2D4GXSk7PfL5hIaFSfy6hSHV94O8Y27T5q+DlDEgd/cZ4KmVI+MWKzFfCTiTuWqjfRSfdRllCA==",
"dev": true,
"dependencies": {
"@commitlint/is-ignored": "^17.8.0",
"@commitlint/parse": "^17.7.0",
"@commitlint/rules": "^17.7.0",
"@commitlint/types": "^17.4.4"
"@commitlint/is-ignored": "^17.8.1",
"@commitlint/parse": "^17.8.1",
"@commitlint/rules": "^17.8.1",
"@commitlint/types": "^17.8.1"
},
"engines": {
"node": ">=v14"
}
},
"node_modules/@commitlint/load": {
"version": "17.8.0",
"resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.8.0.tgz",
"integrity": "sha512-9VnGXYJCP4tXmR4YrwP8n5oX6T5ZsHfPQq6WuUQOvAI+QsDQMaTGgTRXr7us+xsjz+b+mMBSagogqfUx2aixyw==",
"version": "17.8.1",
"resolved": "https://registry.npmjs.org/@commitlint/load/-/load-17.8.1.tgz",
"integrity": "sha512-iF4CL7KDFstP1kpVUkT8K2Wl17h2yx9VaR1ztTc8vzByWWcbO/WaKwxsnCOqow9tVAlzPfo1ywk9m2oJ9ucMqA==",
"dev": true,
"dependencies": {
"@commitlint/config-validator": "^17.6.7",
"@commitlint/execute-rule": "^17.4.0",
"@commitlint/resolve-extends": "^17.6.7",
"@commitlint/types": "^17.4.4",
"@commitlint/config-validator": "^17.8.1",
"@commitlint/execute-rule": "^17.8.1",
"@commitlint/resolve-extends": "^17.8.1",
"@commitlint/types": "^17.8.1",
"@types/node": "20.5.1",
"chalk": "^4.1.0",
"cosmiconfig": "^8.0.0",
@@ -2252,28 +2252,28 @@
"lodash.uniq": "^4.5.0",
"resolve-from": "^5.0.0",
"ts-node": "^10.8.1",
"typescript": "^4.6.4 || ^5.0.0"
"typescript": "^4.6.4 || ^5.2.2"
},
"engines": {
"node": ">=v14"
}
},
"node_modules/@commitlint/message": {
"version": "17.4.2",
"resolved": "https://registry.npmjs.org/@commitlint/message/-/message-17.4.2.tgz",
"integrity": "sha512-3XMNbzB+3bhKA1hSAWPCQA3lNxR4zaeQAQcHj0Hx5sVdO6ryXtgUBGGv+1ZCLMgAPRixuc6en+iNAzZ4NzAa8Q==",
"version": "17.8.1",
"resolved": "https://registry.npmjs.org/@commitlint/message/-/message-17.8.1.tgz",
"integrity": "sha512-6bYL1GUQsD6bLhTH3QQty8pVFoETfFQlMn2Nzmz3AOLqRVfNNtXBaSY0dhZ0dM6A2MEq4+2d7L/2LP8TjqGRkA==",
"dev": true,
"engines": {
"node": ">=v14"
}
},
"node_modules/@commitlint/parse": {
"version": "17.7.0",
"resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-17.7.0.tgz",
"integrity": "sha512-dIvFNUMCUHqq5Abv80mIEjLVfw8QNuA4DS7OWip4pcK/3h5wggmjVnlwGCDvDChkw2TjK1K6O+tAEV78oxjxag==",
"version": "17.8.1",
"resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-17.8.1.tgz",
"integrity": "sha512-/wLUickTo0rNpQgWwLPavTm7WbwkZoBy3X8PpkUmlSmQJyWQTj0m6bDjiykMaDt41qcUbfeFfaCvXfiR4EGnfw==",
"dev": true,
"dependencies": {
"@commitlint/types": "^17.4.4",
"@commitlint/types": "^17.8.1",
"conventional-changelog-angular": "^6.0.0",
"conventional-commits-parser": "^4.0.0"
},
@@ -2282,13 +2282,13 @@
}
},
"node_modules/@commitlint/read": {
"version": "17.5.1",
"resolved": "https://registry.npmjs.org/@commitlint/read/-/read-17.5.1.tgz",
"integrity": "sha512-7IhfvEvB//p9aYW09YVclHbdf1u7g7QhxeYW9ZHSO8Huzp8Rz7m05aCO1mFG7G8M+7yfFnXB5xOmG18brqQIBg==",
"version": "17.8.1",
"resolved": "https://registry.npmjs.org/@commitlint/read/-/read-17.8.1.tgz",
"integrity": "sha512-Fd55Oaz9irzBESPCdMd8vWWgxsW3OWR99wOntBDHgf9h7Y6OOHjWEdS9Xzen1GFndqgyoaFplQS5y7KZe0kO2w==",
"dev": true,
"dependencies": {
"@commitlint/top-level": "^17.4.0",
"@commitlint/types": "^17.4.4",
"@commitlint/top-level": "^17.8.1",
"@commitlint/types": "^17.8.1",
"fs-extra": "^11.0.0",
"git-raw-commits": "^2.0.11",
"minimist": "^1.2.6"
@@ -2298,13 +2298,13 @@
}
},
"node_modules/@commitlint/resolve-extends": {
"version": "17.6.7",
"resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-17.6.7.tgz",
"integrity": "sha512-PfeoAwLHtbOaC9bGn/FADN156CqkFz6ZKiVDMjuC2N5N0740Ke56rKU7Wxdwya8R8xzLK9vZzHgNbuGhaOVKIg==",
"version": "17.8.1",
"resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-17.8.1.tgz",
"integrity": "sha512-W/ryRoQ0TSVXqJrx5SGkaYuAaE/BUontL1j1HsKckvM6e5ZaG0M9126zcwL6peKSuIetJi7E87PRQF8O86EW0Q==",
"dev": true,
"dependencies": {
"@commitlint/config-validator": "^17.6.7",
"@commitlint/types": "^17.4.4",
"@commitlint/config-validator": "^17.8.1",
"@commitlint/types": "^17.8.1",
"import-fresh": "^3.0.0",
"lodash.mergewith": "^4.6.2",
"resolve-from": "^5.0.0",
@@ -2315,15 +2315,15 @@
}
},
"node_modules/@commitlint/rules": {
"version": "17.7.0",
"resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-17.7.0.tgz",
"integrity": "sha512-J3qTh0+ilUE5folSaoK91ByOb8XeQjiGcdIdiB/8UT1/Rd1itKo0ju/eQVGyFzgTMYt8HrDJnGTmNWwcMR1rmA==",
"version": "17.8.1",
"resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-17.8.1.tgz",
"integrity": "sha512-2b7OdVbN7MTAt9U0vKOYKCDsOvESVXxQmrvuVUZ0rGFMCrCPJWWP1GJ7f0lAypbDAhaGb8zqtdOr47192LBrIA==",
"dev": true,
"dependencies": {
"@commitlint/ensure": "^17.6.7",
"@commitlint/message": "^17.4.2",
"@commitlint/to-lines": "^17.4.0",
"@commitlint/types": "^17.4.4",
"@commitlint/ensure": "^17.8.1",
"@commitlint/message": "^17.8.1",
"@commitlint/to-lines": "^17.8.1",
"@commitlint/types": "^17.8.1",
"execa": "^5.0.0"
},
"engines": {
@@ -2331,18 +2331,18 @@
}
},
"node_modules/@commitlint/to-lines": {
"version": "17.4.0",
"resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-17.4.0.tgz",
"integrity": "sha512-LcIy/6ZZolsfwDUWfN1mJ+co09soSuNASfKEU5sCmgFCvX5iHwRYLiIuoqXzOVDYOy7E7IcHilr/KS0e5T+0Hg==",
"version": "17.8.1",
"resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-17.8.1.tgz",
"integrity": "sha512-LE0jb8CuR/mj6xJyrIk8VLz03OEzXFgLdivBytoooKO5xLt5yalc8Ma5guTWobw998sbR3ogDd+2jed03CFmJA==",
"dev": true,
"engines": {
"node": ">=v14"
}
},
"node_modules/@commitlint/top-level": {
"version": "17.4.0",
"resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-17.4.0.tgz",
"integrity": "sha512-/1loE/g+dTTQgHnjoCy0AexKAEFyHsR2zRB4NWrZ6lZSMIxAhBJnmCqwao7b4H8888PsfoTBCLBYIw8vGnej8g==",
"version": "17.8.1",
"resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-17.8.1.tgz",
"integrity": "sha512-l6+Z6rrNf5p333SHfEte6r+WkOxGlWK4bLuZKbtf/2TXRN+qhrvn1XE63VhD8Oe9oIHQ7F7W1nG2k/TJFhx2yA==",
"dev": true,
"dependencies": {
"find-up": "^5.0.0"
@@ -2352,9 +2352,9 @@
}
},
"node_modules/@commitlint/types": {
"version": "17.4.4",
"resolved": "https://registry.npmjs.org/@commitlint/types/-/types-17.4.4.tgz",
"integrity": "sha512-amRN8tRLYOsxRr6mTnGGGvB5EmW/4DDjLMgiwK3CCVEmN6Sr/6xePGEpWaspKkckILuUORCwe6VfDBw6uj4axQ==",
"version": "17.8.1",
"resolved": "https://registry.npmjs.org/@commitlint/types/-/types-17.8.1.tgz",
"integrity": "sha512-PXDQXkAmiMEG162Bqdh9ChML/GJZo6vU+7F03ALKDK8zYc6SuAr47LjG7hGYRqUOz+WK0dU7bQ0xzuqFMdxzeQ==",
"dev": true,
"dependencies": {
"chalk": "^4.1.0"
@@ -2394,9 +2394,9 @@
}
},
"node_modules/@csstools/cascade-layer-name-parser": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-1.0.4.tgz",
"integrity": "sha512-zXMGsJetbLoXe+gjEES07MEGjL0Uy3hMxmnGtVBrRpVKr5KV9OgCB09zr/vLrsEtoVQTgJFewxaU8IYSAE4tjg==",
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/@csstools/cascade-layer-name-parser/-/cascade-layer-name-parser-1.0.5.tgz",
"integrity": "sha512-v/5ODKNBMfBl0us/WQjlfsvSlYxfZLhNMVIsuCPib2ulTwGKYbKJbwqw671+qH9Y4wvWVnu7LBChvml/wBKjFg==",
"funding": [
{
"type": "github",
@@ -2411,8 +2411,8 @@
"node": "^14 || ^16 || >=18"
},
"peerDependencies": {
"@csstools/css-parser-algorithms": "^2.3.1",
"@csstools/css-tokenizer": "^2.2.0"
"@csstools/css-parser-algorithms": "^2.3.2",
"@csstools/css-tokenizer": "^2.2.1"
}
},
"node_modules/@csstools/css-parser-algorithms": {
@@ -2485,10 +2485,10 @@
}
},
"node_modules/@edx/brand": {
"name": "@edx/brand-openedx",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@edx/brand-openedx/-/brand-openedx-1.2.0.tgz",
"integrity": "sha512-r4PDN3rCgDsLovW44ayxoNNHgG5I4Rvss6MG5CrQEX4oW8YhQVEod+jJtwR5vi0mFLN2GIaMlDpd7iIy03VqXg=="
"name": "@openedx/brand-openedx",
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@openedx/brand-openedx/-/brand-openedx-1.2.3.tgz",
"integrity": "sha512-Dn9CtpC8fovh++Xi4NF5NJoeR9yU2yXZnV9IujxIyGd/dn0Phq5t6dzJVfupwq09mpDnzJv7egA8Znz/3ljO+w=="
},
"node_modules/@edx/browserslist-config": {
"version": "1.2.0",
@@ -2510,9 +2510,9 @@
}
},
"node_modules/@edx/frontend-build": {
"version": "12.9.17",
"resolved": "https://registry.npmjs.org/@edx/frontend-build/-/frontend-build-12.9.17.tgz",
"integrity": "sha512-P4w/jp456o5EQ5l0WVl4JIRHnC5xbG+M1Ce7xX1JKex4YDN3lmoBuiaguPg3n6CBDsNKEiBr48n4u+ug+5UTCw==",
"version": "13.0.8",
"resolved": "https://registry.npmjs.org/@edx/frontend-build/-/frontend-build-13.0.8.tgz",
"integrity": "sha512-TXUoF7BTIHe4h+W4Dt9+Gf8PXnfVJRwLKitEkpYUH5r4kDEJfyKKGYSbizkciGI+9QScbhm2u8Alv2agDXV1TA==",
"dependencies": {
"@babel/cli": "7.22.5",
"@babel/core": "7.22.5",
@@ -2524,13 +2524,14 @@
"@babel/preset-react": "7.22.5",
"@edx/eslint-config": "3.2.0",
"@edx/new-relic-source-map-webpack-plugin": "2.1.0",
"@formatjs/cli": "^6.0.3",
"@fullhuman/postcss-purgecss": "5.0.0",
"@pmmmwh/react-refresh-webpack-plugin": "0.5.11",
"@svgr/webpack": "8.1.0",
"autoprefixer": "10.4.16",
"babel-jest": "26.6.3",
"babel-loader": "9.1.3",
"babel-plugin-react-intl": "7.9.4",
"babel-plugin-formatjs": "^10.4.0",
"babel-plugin-transform-imports": "2.0.0",
"babel-polyfill": "6.26.0",
"chalk": "4.1.2",
@@ -2552,20 +2553,20 @@
"image-minimizer-webpack-plugin": "3.8.3",
"jest": "26.6.3",
"mini-css-extract-plugin": "1.6.2",
"postcss": "8.4.30",
"postcss-custom-media": "10.0.1",
"postcss": "8.4.31",
"postcss-custom-media": "10.0.2",
"postcss-loader": "7.3.3",
"postcss-rtlcss": "4.0.8",
"postcss-rtlcss": "4.0.9",
"react-dev-utils": "12.0.1",
"react-refresh": "0.14.0",
"resolve-url-loader": "5.0.0",
"sass": "1.65.1",
"sass": "1.69.5",
"sass-loader": "13.3.2",
"sharp": "0.32.6",
"source-map-loader": "4.0.1",
"style-loader": "3.3.3",
"url-loader": "4.1.1",
"webpack": "5.88.2",
"webpack": "5.89.0",
"webpack-bundle-analyzer": "4.9.1",
"webpack-cli": "5.1.4",
"webpack-dev-server": "4.15.1",
@@ -2579,9 +2580,9 @@
}
},
"node_modules/@edx/frontend-component-footer": {
"version": "12.4.0",
"resolved": "https://registry.npmjs.org/@edx/frontend-component-footer/-/frontend-component-footer-12.4.0.tgz",
"integrity": "sha512-DmAC8eTB4ARYlzBewzxWB3Afn9v8j9nSudqDd6LiXHUYDZ8Z7QPrMfmrmfDHllflVd22NvljmP2hp+WdtjkgMA==",
"version": "12.6.0",
"resolved": "https://registry.npmjs.org/@edx/frontend-component-footer/-/frontend-component-footer-12.6.0.tgz",
"integrity": "sha512-s+YwcThkETBcyRji2Zl7CTYebrsqevP0rmTYyycEL952HkpbQ7EY8Ym0wYH2sp/ZnZ9bTyEeWsiQS5l7eMyY1A==",
"dependencies": {
"@edx/paragon": "^21.3.1",
"@fortawesome/fontawesome-svg-core": "6.4.2",
@@ -2592,16 +2593,16 @@
"lodash": "^4.17.21"
},
"peerDependencies": {
"@edx/frontend-platform": "^4.0.0 || ^5.0.0",
"@edx/frontend-platform": "^4.0.0 || ^5.0.0 || ^6.0.0",
"prop-types": "^15.5.10",
"react": "^16.9.0 || ^17.0.0",
"react-dom": "^16.9.0 || ^17.0.0"
}
},
"node_modules/@edx/frontend-component-footer/node_modules/@edx/paragon": {
"version": "21.5.3",
"resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-21.5.3.tgz",
"integrity": "sha512-Xyq7gWLb4n8qWMsNBIPV3tZnOK0bBdHyqj8UVOrFbpBCSPlOES06qpcpT4IrtpfCh0LmUCBiIk5j9XMz3p6zaw==",
"version": "21.5.6",
"resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-21.5.6.tgz",
"integrity": "sha512-CWR9mFBQAnZ29GeP8igPk3dBLgIQmZJ6tZQiou6855TjHIXcvgmbIvtchKw9SgzhW+D5B0hQJet94zsm+GG/Rg==",
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.1.1",
"@fortawesome/react-fontawesome": "^0.1.18",
@@ -2748,32 +2749,32 @@
}
},
"node_modules/@edx/frontend-component-header": {
"version": "4.7.1",
"resolved": "https://registry.npmjs.org/@edx/frontend-component-header/-/frontend-component-header-4.7.1.tgz",
"integrity": "sha512-Fs4KXXgLBLjI6fH+AxpCyS2ItoGAtbWQ6J1GqK9MeoUsEFHBzL4//fBTbU2HwCJSpn7W8p63CYtk2EOy3eY1Og==",
"version": "4.10.1",
"resolved": "https://registry.npmjs.org/@edx/frontend-component-header/-/frontend-component-header-4.10.1.tgz",
"integrity": "sha512-bcQ+ebdy/lM2TfLVB+WhdWNsuc51cFVgt5UQuJcV5nw6ACYUkBVTLbGDT7J8797bsQe6Lywg7Cj9ykB+Dy78Kw==",
"dependencies": {
"@edx/paragon": "21.3.1",
"@edx/paragon": "21.5.6",
"@fortawesome/fontawesome-svg-core": "6.4.2",
"@fortawesome/free-brands-svg-icons": "6.4.2",
"@fortawesome/free-regular-svg-icons": "6.4.2",
"@fortawesome/free-solid-svg-icons": "6.4.2",
"@fortawesome/react-fontawesome": "^0.2.0",
"axios-mock-adapter": "1.21.5",
"axios-mock-adapter": "1.22.0",
"babel-polyfill": "6.26.0",
"react-responsive": "8.2.0",
"react-transition-group": "4.4.5"
},
"peerDependencies": {
"@edx/frontend-platform": "^4.0.0 || ^5.0.0",
"@edx/frontend-platform": "^4.0.0 || ^5.0.0 || ^6.0.0",
"prop-types": "^15.5.10",
"react": "^16.9.0 || ^17.0.0",
"react-dom": "^16.9.0 || ^17.0.0"
}
},
"node_modules/@edx/frontend-component-header/node_modules/@edx/paragon": {
"version": "21.3.1",
"resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-21.3.1.tgz",
"integrity": "sha512-bXTUaOEmT8XLnDQzYS8QLMvWK5K2BN4jHlx25lO8N0XWRQeDiQTdbx8OrEbv8QOPTlrv0an5MZc+qjlleJFObg==",
"version": "21.5.6",
"resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-21.5.6.tgz",
"integrity": "sha512-CWR9mFBQAnZ29GeP8igPk3dBLgIQmZJ6tZQiou6855TjHIXcvgmbIvtchKw9SgzhW+D5B0hQJet94zsm+GG/Rg==",
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.1.1",
"@fortawesome/react-fontawesome": "^0.1.18",
@@ -3261,6 +3262,25 @@
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
}
},
"node_modules/@formatjs/cli": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/@formatjs/cli/-/cli-6.2.0.tgz",
"integrity": "sha512-sP04UpocRHYwSovUnunAZHYvCTVbNcaLtWKnr1lETGRUnRRQqnXy/3d2Ce271ELXmNUSde2eHRdu4rv2XaVaiQ==",
"bin": {
"formatjs": "bin/formatjs"
},
"engines": {
"node": ">= 16"
},
"peerDependencies": {
"vue": "^3.3.4"
},
"peerDependenciesMeta": {
"vue": {
"optional": true
}
}
},
"node_modules/@formatjs/ecma402-abstract": {
"version": "1.11.4",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz",
@@ -3347,23 +3367,6 @@
"tslib": "^2.1.0"
}
},
"node_modules/@formatjs/intl-numberformat": {
"version": "5.7.6",
"resolved": "https://registry.npmjs.org/@formatjs/intl-numberformat/-/intl-numberformat-5.7.6.tgz",
"integrity": "sha512-ZlZfYtvbVHYZY5OG3RXizoCwxKxEKOrzEe2YOw9wbzoxF3PmFn0SAgojCFGLyNXkkR6xVxlylhbuOPf1dkIVNg==",
"dependencies": {
"@formatjs/ecma402-abstract": "1.4.0",
"tslib": "^2.0.1"
}
},
"node_modules/@formatjs/intl-numberformat/node_modules/@formatjs/ecma402-abstract": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.4.0.tgz",
"integrity": "sha512-Mv027hcLFjE45K8UJ8PjRpdDGfR0aManEFj1KzoN8zXNveHGEygpZGfFf/FTTMl+QEVSrPAUlyxaCApvmv47AQ==",
"dependencies": {
"tslib": "^2.0.1"
}
},
"node_modules/@formatjs/intl-pluralrules": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/@formatjs/intl-pluralrules/-/intl-pluralrules-4.3.3.tgz",
@@ -3384,42 +3387,6 @@
"tslib": "^2.1.0"
}
},
"node_modules/@formatjs/ts-transformer": {
"version": "2.13.0",
"resolved": "https://registry.npmjs.org/@formatjs/ts-transformer/-/ts-transformer-2.13.0.tgz",
"integrity": "sha512-mu7sHXZk1NWZrQ3eUqugpSYo8x5/tXkrI4uIbFqCEC0eNgQaIcoKgVeDFgDAcgG+cEme2atAUYSFF+DFWC4org==",
"dependencies": {
"intl-messageformat-parser": "6.1.2",
"tslib": "^2.0.1",
"typescript": "^4.0"
},
"peerDependencies": {
"ts-jest": "^26.4.0"
},
"peerDependenciesMeta": {
"ts-jest": {
"optional": true
}
}
},
"node_modules/@formatjs/ts-transformer/node_modules/@formatjs/ecma402-abstract": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.5.0.tgz",
"integrity": "sha512-wXv36yo+mfWllweN0Fq7sUs7PUiNopn7I0JpLTe3hGu6ZMR4CV7LqK1llhB18pndwpKoafQKb1et2DCJAOW20Q==",
"dependencies": {
"tslib": "^2.0.1"
}
},
"node_modules/@formatjs/ts-transformer/node_modules/intl-messageformat-parser": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-6.1.2.tgz",
"integrity": "sha512-4GQDEPhl/ZMNDKwMsLqyw1LG2IAWjmLJXdmnRcHKeLQzpgtNYZI6lVw1279pqIkRk2MfKb9aDsVFzm565azK5A==",
"deprecated": "We've written a new parser that's 6x faster and is backwards compatible. Please use @formatjs/icu-messageformat-parser",
"dependencies": {
"@formatjs/ecma402-abstract": "1.5.0",
"tslib": "^2.0.1"
}
},
"node_modules/@fortawesome/fontawesome-common-types": {
"version": "0.2.36",
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.36.tgz",
@@ -5071,11 +5038,11 @@
}
},
"node_modules/@restart/hooks": {
"version": "0.4.9",
"resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.9.tgz",
"integrity": "sha512-3BekqcwB6Umeya+16XPooARn4qEPW6vNvwYnlofIYe6h9qG1/VeD7UvShCWx11eFz5ELYmwIEshz+MkPX3wjcQ==",
"version": "0.4.11",
"resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.11.tgz",
"integrity": "sha512-Ft/ncTULZN6ldGHiF/k5qt72O8JyRMOeg0tApvCni8LkoiEahO+z3TNxfXIVGy890YtWVDvJAl662dVJSJXvMw==",
"dependencies": {
"dequal": "^2.0.2"
"dequal": "^2.0.3"
},
"peerDependencies": {
"react": ">=16.8.0"
@@ -5443,6 +5410,14 @@
"@babel/types": "^7.0.0"
}
},
"node_modules/@types/babel__helper-plugin-utils": {
"version": "7.10.1",
"resolved": "https://registry.npmjs.org/@types/babel__helper-plugin-utils/-/babel__helper-plugin-utils-7.10.1.tgz",
"integrity": "sha512-6RaT7i6r2rT6ouIDZ2Cd6dPkq4wn1F8pLyDO+7wPVsL1dodvORiZORImaD6j9FBcHjPGuERE0hhtwkuPNXsO0A==",
"dependencies": {
"@types/babel__core": "*"
}
},
"node_modules/@types/babel__template": {
"version": "7.4.1",
"resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz",
@@ -5544,14 +5519,6 @@
"@types/send": "*"
}
},
"node_modules/@types/fs-extra": {
"version": "9.0.13",
"resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz",
"integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==",
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/glob": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
@@ -5622,6 +5589,11 @@
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz",
"integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA=="
},
"node_modules/@types/json-stable-stringify": {
"version": "1.0.34",
"resolved": "https://registry.npmjs.org/@types/json-stable-stringify/-/json-stable-stringify-1.0.34.tgz",
"integrity": "sha512-s2cfwagOQAS8o06TcwKfr9Wx11dNGbH2E9vJz1cqV+a/LOyhWNLUNd6JSRYNzvB4d29UuJX2M0Dj9vE1T8fRXw=="
},
"node_modules/@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
@@ -5709,9 +5681,9 @@
}
},
"node_modules/@types/react-transition-group": {
"version": "4.4.6",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz",
"integrity": "sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==",
"version": "4.4.7",
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.7.tgz",
"integrity": "sha512-ICCyBl5mvyqYp8Qeq9B5G/fyBSRC0zx3XM3sCC6KkcMsNeAHqXBKkmat4GqdJET5jtYUpZXrxI5flve5qhi2Eg==",
"dependencies": {
"@types/react": "*"
}
@@ -5726,15 +5698,6 @@
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
"integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ=="
},
"node_modules/@types/schema-utils": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/@types/schema-utils/-/schema-utils-2.4.0.tgz",
"integrity": "sha512-454hrj5gz/FXcUE20ygfEiN4DxZ1sprUo0V1gqIqkNZ/CzoEzAZEll2uxMsuyz6BYjiQan4Aa65xbTemfzW9hQ==",
"deprecated": "This is a stub types definition. schema-utils provides its own type definitions, so you do not need this installed.",
"dependencies": {
"schema-utils": "*"
}
},
"node_modules/@types/send": {
"version": "0.17.1",
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.1.tgz",
@@ -5775,9 +5738,9 @@
"integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw=="
},
"node_modules/@types/warning": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.0.tgz",
"integrity": "sha512-t/Tvs5qR47OLOr+4E9ckN8AmP2Tf16gWq+/qA4iUGS/OOyHVO8wv2vjJuX8SNOUTJyWb+2t7wJm6cXILFnOROA=="
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.1.tgz",
"integrity": "sha512-ywJmriP+nvjBKNBEMaNZgj2irZHoxcKeYcyMLbqhYKbDVn8yCIULy2Ol/tvIb37O3IBeZj3RU4tXqQTtGwoAMg=="
},
"node_modules/@types/ws": {
"version": "8.5.5",
@@ -6243,16 +6206,6 @@
"sprintf-js": "~1.0.2"
}
},
"node_modules/argv": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/argv/-/argv-0.0.2.tgz",
"integrity": "sha512-dEamhpPEwRUBpLNHeuCm/v+g0anFByHahxodVO/BbAarHVBBg2MccCwf9K+o1Pof+2btdnkJelYVUWjW/VrATw==",
"deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
"dev": true,
"engines": {
"node": ">=0.6.10"
}
},
"node_modules/aria-hidden": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.3.tgz",
@@ -6576,9 +6529,9 @@
}
},
"node_modules/axios-mock-adapter": {
"version": "1.21.5",
"resolved": "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-1.21.5.tgz",
"integrity": "sha512-5NI1V/VK+8+JeTF8niqOowuysA4b8mGzdlMN/QnTnoXbYh4HZSNiopsDclN2g/m85+G++IrEtUdZaQ3GnaMsSA==",
"version": "1.22.0",
"resolved": "https://registry.npmjs.org/axios-mock-adapter/-/axios-mock-adapter-1.22.0.tgz",
"integrity": "sha512-dmI0KbkyAhntUR05YY96qg2H6gg0XMl2+qTW0xmYg6Up+BFBAJYRLROMXRdDEL06/Wqwa0TJThAYvFtSFdRCZw==",
"dependencies": {
"fast-deep-equal": "^3.1.3",
"is-buffer": "^2.0.5"
@@ -6708,6 +6661,87 @@
"url": "https://opencollective.com/webpack"
}
},
"node_modules/babel-plugin-formatjs": {
"version": "10.5.6",
"resolved": "https://registry.npmjs.org/babel-plugin-formatjs/-/babel-plugin-formatjs-10.5.6.tgz",
"integrity": "sha512-XlE8WHF/ZstS5K3ZCWb5nQ6e9u6KpNquTpHpjteGaMSguSjvbfNb7CsF4YHq1fTPBdHWNspA3qfAqMGgHBO4mw==",
"dependencies": {
"@babel/core": "^7.10.4",
"@babel/helper-plugin-utils": "^7.10.4",
"@babel/plugin-syntax-jsx": "7",
"@babel/traverse": "7",
"@babel/types": "^7.12.11",
"@formatjs/icu-messageformat-parser": "2.6.2",
"@formatjs/ts-transformer": "3.13.5",
"@types/babel__core": "^7.1.7",
"@types/babel__helper-plugin-utils": "^7.10.0",
"@types/babel__traverse": "^7.1.7",
"tslib": "^2.4.0"
}
},
"node_modules/babel-plugin-formatjs/node_modules/@formatjs/ecma402-abstract": {
"version": "1.17.2",
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.17.2.tgz",
"integrity": "sha512-k2mTh0m+IV1HRdU0xXM617tSQTi53tVR2muvYOsBeYcUgEAyxV1FOC7Qj279th3fBVQ+Dj6muvNJZcHSPNdbKg==",
"dependencies": {
"@formatjs/intl-localematcher": "0.4.2",
"tslib": "^2.4.0"
}
},
"node_modules/babel-plugin-formatjs/node_modules/@formatjs/icu-messageformat-parser": {
"version": "2.6.2",
"resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.6.2.tgz",
"integrity": "sha512-nF/Iww7sc5h+1MBCDRm68qpHTCG4xvGzYs/x9HFcDETSGScaJ1Fcadk5U/NXjXeCtzD+DhN4BAwKFVclHfKMdA==",
"dependencies": {
"@formatjs/ecma402-abstract": "1.17.2",
"@formatjs/icu-skeleton-parser": "1.6.2",
"tslib": "^2.4.0"
}
},
"node_modules/babel-plugin-formatjs/node_modules/@formatjs/icu-skeleton-parser": {
"version": "1.6.2",
"resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.6.2.tgz",
"integrity": "sha512-VtB9Slo4ZL6QgtDFJ8Injvscf0xiDd4bIV93SOJTBjUF4xe2nAWOoSjLEtqIG+hlIs1sNrVKAaFo3nuTI4r5ZA==",
"dependencies": {
"@formatjs/ecma402-abstract": "1.17.2",
"tslib": "^2.4.0"
}
},
"node_modules/babel-plugin-formatjs/node_modules/@formatjs/intl-localematcher": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.4.2.tgz",
"integrity": "sha512-BGdtJFmaNJy5An/Zan4OId/yR9Ih1OojFjcduX/xOvq798OgWSyDtd6Qd5jqJXwJs1ipe4Fxu9+cshic5Ox2tA==",
"dependencies": {
"tslib": "^2.4.0"
}
},
"node_modules/babel-plugin-formatjs/node_modules/@formatjs/ts-transformer": {
"version": "3.13.5",
"resolved": "https://registry.npmjs.org/@formatjs/ts-transformer/-/ts-transformer-3.13.5.tgz",
"integrity": "sha512-dh2mmZqkId0UeM+FQtmwugpMGvyzTBmXj5LjwD4M5OeSm62tcgkScjqeO/1EetaNS/JkTUBbsFBnHzaDzh3yOw==",
"dependencies": {
"@formatjs/icu-messageformat-parser": "2.6.2",
"@types/json-stable-stringify": "^1.0.32",
"@types/node": "14 || 16 || 17",
"chalk": "^4.0.0",
"json-stable-stringify": "^1.0.1",
"tslib": "^2.4.0",
"typescript": "^4.7 || 5"
},
"peerDependencies": {
"ts-jest": ">=27"
},
"peerDependenciesMeta": {
"ts-jest": {
"optional": true
}
}
},
"node_modules/babel-plugin-formatjs/node_modules/@types/node": {
"version": "17.0.45",
"resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz",
"integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw=="
},
"node_modules/babel-plugin-istanbul": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz",
@@ -6781,83 +6815,6 @@
"@babel/core": "^7.0.0-0"
}
},
"node_modules/babel-plugin-react-intl": {
"version": "7.9.4",
"resolved": "https://registry.npmjs.org/babel-plugin-react-intl/-/babel-plugin-react-intl-7.9.4.tgz",
"integrity": "sha512-cMKrHEXrw43yT4M89Wbgq8A8N8lffSquj1Piwov/HVukR7jwOw8gf9btXNsQhT27ccyqEwy+M286JQYy0jby2g==",
"deprecated": "this package has been renamed to babel-plugin-formatjs",
"dependencies": {
"@babel/core": "^7.9.0",
"@babel/helper-plugin-utils": "^7.8.3",
"@babel/types": "^7.9.5",
"@formatjs/ts-transformer": "^2.6.0",
"@types/babel__core": "^7.1.7",
"@types/fs-extra": "^9.0.1",
"@types/schema-utils": "^2.4.0",
"fs-extra": "^9.0.0",
"intl-messageformat-parser": "^5.3.7",
"schema-utils": "^2.6.6"
}
},
"node_modules/babel-plugin-react-intl/node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"dependencies": {
"fast-deep-equal": "^3.1.1",
"fast-json-stable-stringify": "^2.0.0",
"json-schema-traverse": "^0.4.1",
"uri-js": "^4.2.2"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/epoberezkin"
}
},
"node_modules/babel-plugin-react-intl/node_modules/ajv-keywords": {
"version": "3.5.2",
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
"peerDependencies": {
"ajv": "^6.9.1"
}
},
"node_modules/babel-plugin-react-intl/node_modules/fs-extra": {
"version": "9.1.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
"dependencies": {
"at-least-node": "^1.0.0",
"graceful-fs": "^4.2.0",
"jsonfile": "^6.0.1",
"universalify": "^2.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/babel-plugin-react-intl/node_modules/json-schema-traverse": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
},
"node_modules/babel-plugin-react-intl/node_modules/schema-utils": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz",
"integrity": "sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==",
"dependencies": {
"@types/json-schema": "^7.0.5",
"ajv": "^6.12.4",
"ajv-keywords": "^3.5.2"
},
"engines": {
"node": ">= 8.9.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
}
},
"node_modules/babel-plugin-transform-imports": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/babel-plugin-transform-imports/-/babel-plugin-transform-imports-2.0.0.tgz",
@@ -7344,9 +7301,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001549",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001549.tgz",
"integrity": "sha512-qRp48dPYSCYaP+KurZLhDYdVE+yEyht/3NlmcJgVQ2VMGt6JL36ndQ/7rgspdZsJuxDPFIo/OzBT2+GmIJ53BA==",
"version": "1.0.30001563",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001563.tgz",
"integrity": "sha512-na2WUmOxnwIZtwnFI2CZ/3er0wdNzU7hN+cPYz/z2ajHThnkWjNBOpEPP4n+4r2WPM847JaMotaJE3bnfzjyKw==",
"funding": [
{
"type": "opencollective",
@@ -7718,26 +7675,6 @@
"node": ">= 0.12.0"
}
},
"node_modules/codecov": {
"version": "3.8.3",
"resolved": "https://registry.npmjs.org/codecov/-/codecov-3.8.3.tgz",
"integrity": "sha512-Y8Hw+V3HgR7V71xWH2vQ9lyS358CbGCldWlJFR0JirqoGtOoas3R3/OclRTvgUYFK29mmJICDPauVKmpqbwhOA==",
"deprecated": "https://about.codecov.io/blog/codecov-uploader-deprecation-plan/",
"dev": true,
"dependencies": {
"argv": "0.0.2",
"ignore-walk": "3.0.4",
"js-yaml": "3.14.1",
"teeny-request": "7.1.1",
"urlgrey": "1.0.0"
},
"bin": {
"codecov": "bin/codecov"
},
"engines": {
"node": ">=4.0"
}
},
"node_modules/collect-v8-coverage": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz",
@@ -7986,9 +7923,9 @@
}
},
"node_modules/core-js": {
"version": "3.33.0",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.33.0.tgz",
"integrity": "sha512-HoZr92+ZjFEKar5HS6MC776gYslNOKHt75mEBKWKnPeFDpZ6nH5OeF3S6HFT1mUAUZKrzkez05VboaX8myjSuw==",
"version": "3.33.3",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.33.3.tgz",
"integrity": "sha512-lo0kOocUlLKmm6kv/FswQL8zbkH7mVsLJ/FULClOhv8WRVmKLVcs6XPNQAzstfeJTCHMyButEwG+z1kHxHoDZw==",
"hasInstallScript": true,
"funding": {
"type": "opencollective",
@@ -10220,21 +10157,6 @@
"resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz",
"integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA=="
},
"node_modules/fast-url-parser": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/fast-url-parser/-/fast-url-parser-1.1.3.tgz",
"integrity": "sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==",
"dev": true,
"dependencies": {
"punycode": "^1.3.2"
}
},
"node_modules/fast-url-parser/node_modules/punycode": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
"integrity": "sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==",
"dev": true
},
"node_modules/fastest-levenshtein": {
"version": "1.0.16",
"resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz",
@@ -10357,6 +10279,14 @@
"node": ">=8"
}
},
"node_modules/filter-obj": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
"integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/finalhandler": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
@@ -11857,15 +11787,6 @@
"node": ">= 4"
}
},
"node_modules/ignore-walk": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.4.tgz",
"integrity": "sha512-PY6Ii8o1jMRA1z4F2hRkH/xN59ox43DavKvD3oDpfurRlOJyAHpifIwpbdv1n4jt4ov0jSpw3kQ4GhJnpBL6WQ==",
"dev": true,
"dependencies": {
"minimatch": "^3.0.4"
}
},
"node_modules/image-minimizer-webpack-plugin": {
"version": "3.8.3",
"resolved": "https://registry.npmjs.org/image-minimizer-webpack-plugin/-/image-minimizer-webpack-plugin-3.8.3.tgz",
@@ -12083,15 +12004,6 @@
"tslib": "^2.1.0"
}
},
"node_modules/intl-messageformat-parser": {
"version": "5.5.1",
"resolved": "https://registry.npmjs.org/intl-messageformat-parser/-/intl-messageformat-parser-5.5.1.tgz",
"integrity": "sha512-TvB3LqF2VtP6yI6HXlRT5TxX98HKha6hCcrg9dwlPwNaedVNuQA9KgBdtWKgiyakyCTYHQ+KJeFEstNKfZr64w==",
"deprecated": "We've written a new parser that's 6x faster and is backwards compatible. Please use @formatjs/icu-messageformat-parser",
"dependencies": {
"@formatjs/intl-numberformat": "^5.5.2"
}
},
"node_modules/invariant": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
@@ -14628,9 +14540,9 @@
}
},
"node_modules/jquery": {
"version": "3.7.0",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.0.tgz",
"integrity": "sha512-umpJ0/k8X0MvD1ds0P9SfowREz2LenHsQaxSohMZ5OMNEU2r0tf8pdeEFTHMFxWVxKNyU9rTtK3CWzUCTKJUeQ==",
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz",
"integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==",
"peer": true
},
"node_modules/js-base64": {
@@ -14739,6 +14651,17 @@
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
},
"node_modules/json-stable-stringify": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.2.tgz",
"integrity": "sha512-eunSSaEnxV12z+Z73y/j5N37/In40GK4GmsSy+tEHJMxknvqnA7/djeYtAgW0GsWHUfg+847WJjKaEylk2y09g==",
"dependencies": {
"jsonify": "^0.0.1"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/json-stable-stringify-without-jsonify": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
@@ -14766,6 +14689,14 @@
"graceful-fs": "^4.1.6"
}
},
"node_modules/jsonify": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz",
"integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==",
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/jsonparse": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz",
@@ -15149,39 +15080,6 @@
"node": ">= 12"
}
},
"node_modules/mailto-link/node_modules/filter-obj": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz",
"integrity": "sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/mailto-link/node_modules/query-string": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-7.0.1.tgz",
"integrity": "sha512-uIw3iRvHnk9to1blJCG3BTc+Ro56CBowJXKmNNAm3RulvPBzWLRqKSiiDk+IplJhsydwtuNMHi8UGQFcCLVfkA==",
"dependencies": {
"decode-uri-component": "^0.2.0",
"filter-obj": "^1.1.0",
"split-on-first": "^1.0.0",
"strict-uri-encode": "^2.0.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/mailto-link/node_modules/split-on-first": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
"integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==",
"engines": {
"node": ">=6"
}
},
"node_modules/make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
@@ -15706,48 +15604,6 @@
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz",
"integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA=="
},
"node_modules/node-fetch": {
"version": "2.6.11",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.11.tgz",
"integrity": "sha512-4I6pdBY1EthSqDmJkiNk3JIT8cswwR9nfeW/cPdUagJYEQG7R95WRH74wpz7ma8Gh/9dI9FP+OU+0E4FvtA55w==",
"dev": true,
"dependencies": {
"whatwg-url": "^5.0.0"
},
"engines": {
"node": "4.x || >=6.0.0"
},
"peerDependencies": {
"encoding": "^0.1.0"
},
"peerDependenciesMeta": {
"encoding": {
"optional": true
}
}
},
"node_modules/node-fetch/node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
"dev": true
},
"node_modules/node-fetch/node_modules/webidl-conversions": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
"dev": true
},
"node_modules/node-fetch/node_modules/whatwg-url": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
"integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
"dev": true,
"dependencies": {
"tr46": "~0.0.3",
"webidl-conversions": "^3.0.0"
}
},
"node_modules/node-forge": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
@@ -15856,9 +15712,9 @@
}
},
"node_modules/object-code": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/object-code/-/object-code-1.2.4.tgz",
"integrity": "sha512-uGq4ETUuWe+GA586NXEriiaozNuff+YNFXlpD8cVrM1GoiuTZpCABP+bZCWDrvQDoCiSTyiWAFHD/HF/iwhb2w=="
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/object-code/-/object-code-1.3.0.tgz",
"integrity": "sha512-PLplgvzuFhSPBuTX/mtaXEnU3c6g7qKflVVQbV9VWEnV/34iKeAX1jeDNCKq1OgGlsnkA/NjldCzTbHxa7Wj4A=="
},
"node_modules/object-copy": {
"version": "0.1.0",
@@ -16775,9 +16631,9 @@
}
},
"node_modules/postcss": {
"version": "8.4.30",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.30.tgz",
"integrity": "sha512-7ZEao1g4kd68l97aWG/etQKPKq07us0ieSZ2TnFDk11i0ZfDW2AwKHYU8qv4MZKqN2fdBfg+7q0ES06UA73C1g==",
"version": "8.4.31",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
"integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
"funding": [
{
"type": "opencollective",
@@ -16849,9 +16705,9 @@
}
},
"node_modules/postcss-custom-media": {
"version": "10.0.1",
"resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-10.0.1.tgz",
"integrity": "sha512-fil7cosvzlIAYmZJPtNFcTH0Er7a3GveEK4q5Y/L24eWQHmiw8Fv/E5DMkVpdbNjkGzJxrvowOSt/Il9HZ06VQ==",
"version": "10.0.2",
"resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-10.0.2.tgz",
"integrity": "sha512-zcEFNRmDm2fZvTPdI1pIW3W//UruMcLosmMiCdpQnrCsTRzWlKQPYMa1ud9auL0BmrryKK1+JjIGn19K0UjO/w==",
"funding": [
{
"type": "github",
@@ -16863,10 +16719,10 @@
}
],
"dependencies": {
"@csstools/cascade-layer-name-parser": "^1.0.4",
"@csstools/css-parser-algorithms": "^2.3.1",
"@csstools/css-tokenizer": "^2.2.0",
"@csstools/media-query-list-parser": "^2.1.4"
"@csstools/cascade-layer-name-parser": "^1.0.5",
"@csstools/css-parser-algorithms": "^2.3.2",
"@csstools/css-tokenizer": "^2.2.1",
"@csstools/media-query-list-parser": "^2.1.5"
},
"engines": {
"node": "^14 || ^16 || >=18"
@@ -17256,14 +17112,14 @@
}
},
"node_modules/postcss-rtlcss": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/postcss-rtlcss/-/postcss-rtlcss-4.0.8.tgz",
"integrity": "sha512-CR2sY889PHnX6K8rjW9FG4Qvm9UJsIekDakMtEYGH3zgFp9XADMeaKcA0hPOmkClNh0jWbkaPBm0jZ6fHmqkJQ==",
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/postcss-rtlcss/-/postcss-rtlcss-4.0.9.tgz",
"integrity": "sha512-dCNKEf+FgTv+EA3XI8ysg2RnpS5s3/iZmU+9qpCNFxHU/BhK+4hz7jyCsCAfo0CLnDrMPtaQENhwb+EGm1wh7Q==",
"dependencies": {
"rtlcss": "4.1.0"
"rtlcss": "4.1.1"
},
"engines": {
"node": ">=12.0.0"
"node": ">=18.0.0"
},
"peerDependencies": {
"postcss": "^8.4.21"
@@ -17573,6 +17429,23 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/query-string": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-7.0.1.tgz",
"integrity": "sha512-uIw3iRvHnk9to1blJCG3BTc+Ro56CBowJXKmNNAm3RulvPBzWLRqKSiiDk+IplJhsydwtuNMHi8UGQFcCLVfkA==",
"dependencies": {
"decode-uri-component": "^0.2.0",
"filter-obj": "^1.1.0",
"split-on-first": "^1.0.0",
"strict-uri-encode": "^2.0.0"
},
"engines": {
"node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/querystringify": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
@@ -17890,6 +17763,17 @@
"react": ">= 16.8 || 18.0.0"
}
},
"node_modules/react-error-boundary": {
"version": "4.0.11",
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.11.tgz",
"integrity": "sha512-U13ul67aP5DOSPNSCWQ/eO0AQEYzEFkVljULQIjMV0KlffTAhxuDoBKdO0pb/JZ8mDhMKFZ9NZi0BmLGUiNphw==",
"dependencies": {
"@babel/runtime": "^7.12.5"
},
"peerDependencies": {
"react": ">=16.13.1"
}
},
"node_modules/react-error-overlay": {
"version": "6.0.11",
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
@@ -17901,9 +17785,9 @@
"integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
},
"node_modules/react-focus-lock": {
"version": "2.9.4",
"resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.9.4.tgz",
"integrity": "sha512-7pEdXyMseqm3kVjhdVH18sovparAzLg5h6WvIx7/Ck3ekjhrrDMEegHSa3swwC8wgfdd7DIdUVRGeiHT9/7Sgg==",
"version": "2.9.5",
"resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-2.9.5.tgz",
"integrity": "sha512-h6vrdgUbsH2HeD5I7I3Cx1PPrmwGuKYICS+kB9m+32X/9xHRrAbxgvaBpG7BFBN9h3tO+C3qX1QAVESmi4CiIA==",
"dependencies": {
"@babel/runtime": "^7.0.0",
"focus-lock": "^0.11.6",
@@ -17923,12 +17807,12 @@
}
},
"node_modules/react-focus-on": {
"version": "3.8.1",
"resolved": "https://registry.npmjs.org/react-focus-on/-/react-focus-on-3.8.1.tgz",
"integrity": "sha512-fQcBx+SZMgXoRL+69r5+ic4bdVgqaCeKeoFPra8yhcSuL/3unWavfdirEFBGgH71K+RiocMTS6DETHcX0SlOZg==",
"version": "3.9.1",
"resolved": "https://registry.npmjs.org/react-focus-on/-/react-focus-on-3.9.1.tgz",
"integrity": "sha512-IYo2j4mgNpZEJNv+/XzZs3S3xhJbR+AFop092h4OMW7sbFpIMVWxp/Z61V/gfpsgOi7VnoSFXP2bfOWWkjjtOw==",
"dependencies": {
"aria-hidden": "^1.2.2",
"react-focus-lock": "^2.9.2",
"react-focus-lock": "^2.9.4",
"react-remove-scroll": "^2.5.6",
"react-style-singleton": "^2.2.0",
"tslib": "^2.3.1",
@@ -18914,9 +18798,9 @@
}
},
"node_modules/rtlcss": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.1.0.tgz",
"integrity": "sha512-W+N4hh0nVqVrrn3mRkHakxpB+c9cQ4CRT67O39kgA+1DjyhrdsqyCqIuHXyvWaXn4/835n+oX3fYJCi4+G/06A==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/rtlcss/-/rtlcss-4.1.1.tgz",
"integrity": "sha512-/oVHgBtnPNcggP2aVXQjSy6N1mMAfHg4GSag0QtZBlD5bdDgAHwr4pydqJGd+SUCu9260+Pjqbjwtvu7EMH1KQ==",
"dependencies": {
"escalade": "^3.1.1",
"picocolors": "^1.0.0",
@@ -19294,9 +19178,9 @@
}
},
"node_modules/sass": {
"version": "1.65.1",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.65.1.tgz",
"integrity": "sha512-9DINwtHmA41SEd36eVPQ9BJKpn7eKDQmUHmpI0y5Zv2Rcorrh0zS+cFrt050hdNbmmCNKTW3hV5mWfuegNRsEA==",
"version": "1.69.5",
"resolved": "https://registry.npmjs.org/sass/-/sass-1.69.5.tgz",
"integrity": "sha512-qg2+UCJibLr2LCVOt3OlPhr/dqVHWOa9XtZf2OjbLs/T4VPSJ00udtgJxH3neXZm+QqX8B+3cU7RaLqp1iVfcQ==",
"dependencies": {
"chokidar": ">=3.0.0 <4.0.0",
"immutable": "^4.0.0",
@@ -20181,6 +20065,14 @@
"wbuf": "^1.7.3"
}
},
"node_modules/split-on-first": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/split-on-first/-/split-on-first-1.1.0.tgz",
"integrity": "sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==",
"engines": {
"node": ">=6"
}
},
"node_modules/split-string": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz",
@@ -20326,15 +20218,6 @@
"node": ">= 0.8"
}
},
"node_modules/stream-events": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/stream-events/-/stream-events-1.0.5.tgz",
"integrity": "sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==",
"dev": true,
"dependencies": {
"stubs": "^3.0.0"
}
},
"node_modules/streamx": {
"version": "2.15.0",
"resolved": "https://registry.npmjs.org/streamx/-/streamx-2.15.0.tgz",
@@ -20542,12 +20425,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/stubs": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/stubs/-/stubs-3.0.0.tgz",
"integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==",
"dev": true
},
"node_modules/style-loader": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.3.tgz",
@@ -20766,31 +20643,6 @@
"node": ">=6"
}
},
"node_modules/teeny-request": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/teeny-request/-/teeny-request-7.1.1.tgz",
"integrity": "sha512-iwY6rkW5DDGq8hE2YgNQlKbptYpY5Nn2xecjQiNjOXWbKzPGUfmeUBCSQbbr306d7Z7U2N0TPl+/SwYRfua1Dg==",
"dev": true,
"dependencies": {
"http-proxy-agent": "^4.0.0",
"https-proxy-agent": "^5.0.0",
"node-fetch": "^2.6.1",
"stream-events": "^1.0.5",
"uuid": "^8.0.0"
},
"engines": {
"node": ">=10"
}
},
"node_modules/teeny-request/node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"dev": true,
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/terminal-link": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz",
@@ -21551,15 +21403,6 @@
"requires-port": "^1.0.0"
}
},
"node_modules/urlgrey": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/urlgrey/-/urlgrey-1.0.0.tgz",
"integrity": "sha512-hJfIzMPJmI9IlLkby8QrsCykQ+SXDeO2W5Q9QTW3QpqZVTx4a/K7p8/5q+/isD8vsbVaFgql/gvAoQCRQ2Cb5w==",
"dev": true,
"dependencies": {
"fast-url-parser": "^1.1.3"
}
},
"node_modules/use": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz",
@@ -21628,9 +21471,13 @@
}
},
"node_modules/uuid": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
"version": "9.0.1",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"bin": {
"uuid": "dist/bin/uuid"
}
@@ -21749,9 +21596,9 @@
}
},
"node_modules/webpack": {
"version": "5.88.2",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.88.2.tgz",
"integrity": "sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ==",
"version": "5.89.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.89.0.tgz",
"integrity": "sha512-qyfIC10pOr70V+jkmud8tMfajraGCZMBWJtrmuBymQKCrLTRejBI8STDp1MCyZu/QTdZSeacCQYpYNQVOzX5kw==",
"dependencies": {
"@types/eslint-scope": "^3.7.3",
"@types/estree": "^1.0.0",

View File

@@ -10,12 +10,12 @@
},
"scripts": {
"build": "fedx-scripts webpack",
"i18n_extract": "BABEL_ENV=i18n fedx-scripts babel src --quiet > /dev/null",
"i18n_extract": "fedx-scripts formatjs extract",
"lint": "fedx-scripts eslint --ext .js --ext .jsx .",
"snapshot": "fedx-scripts jest --updateSnapshot",
"start": "fedx-scripts webpack-dev-server --progress",
"test": "TZ=UTC fedx-scripts jest --coverage --passWithNoTests",
"stubs": "pact-stub-service ./src/pacts/frontend-app-profile-edx-platform.json --port 18000 --cors"
"stubs": "pact-stub-service ./src/pacts/frontend-app-profile-edx-platform.json --port 18000"
},
"bugs": {
"url": "https://github.com/openedx/frontend-app-profile/issues"
@@ -28,9 +28,9 @@
"extends @edx/browserslist-config"
],
"dependencies": {
"@edx/brand": "npm:@edx/brand-openedx@1.2.0",
"@edx/frontend-component-footer": "12.4.0",
"@edx/frontend-component-header": "4.7.1",
"@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",
@@ -40,7 +40,7 @@
"@fortawesome/react-fontawesome": "0.2.0",
"@pact-foundation/pact": "^11.0.2",
"classnames": "2.3.2",
"core-js": "3.33.0",
"core-js": "3.33.3",
"history": "5.3.0",
"lodash.camelcase": "4.3.0",
"lodash.get": "4.4.2",
@@ -49,6 +49,7 @@
"prop-types": "15.8.1",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-error-boundary": "^4.0.11",
"react-helmet": "6.1.0",
"react-redux": "7.2.9",
"react-router": "6.16.0",
@@ -63,14 +64,13 @@
"universal-cookie": "4.0.4"
},
"devDependencies": {
"@commitlint/cli": "17.8.0",
"@commitlint/config-angular": "17.8.0",
"@commitlint/cli": "17.8.1",
"@commitlint/config-angular": "17.8.1",
"@edx/browserslist-config": "^1.1.1",
"@edx/frontend-build": "12.9.17",
"@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",
"codecov": "3.8.3",
"enzyme": "3.11.0",
"glob": "10.3.10",
"react-test-renderer": "17.0.2",

93
plugins/Plugin.jsx Normal file
View File

@@ -0,0 +1,93 @@
'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,
};

View File

@@ -0,0 +1,42 @@
'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,
};

View File

@@ -0,0 +1,99 @@
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,
};

View File

@@ -0,0 +1,45 @@
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,
};

75
plugins/PluginSlot.jsx Normal file
View File

@@ -0,0 +1,75 @@
/* 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: {},
};

View File

@@ -0,0 +1,8 @@
// 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';

96
plugins/data/hooks.js Normal file
View File

@@ -0,0 +1,96 @@
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],
);
}

10
plugins/data/shapes.js Normal file
View File

@@ -0,0 +1,10 @@
/* 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
});

18
plugins/index.js Normal file
View File

@@ -0,0 +1,18 @@
// 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';

View File

@@ -15,6 +15,7 @@ import {
import React from 'react';
import ReactDOM from 'react-dom';
import { useLocation } from 'react-router-dom';
import Header from '@edx/frontend-component-header';
import Footer from '@edx/frontend-component-footer';
@@ -27,15 +28,25 @@ 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 />;
};
subscribe(APP_READY, () => {
ReactDOM.render(
<AppProvider store={configureStore()}>
<Head />
<Header />
<RenderHeader />
<main id="main">
<AppRoutes />
</main>
<Footer />
<RenderFooter />
</AppProvider>,
document.getElementById('root'),
);
@@ -48,7 +59,6 @@ subscribe(APP_INIT_ERROR, (error) => {
initialize({
messages,
hydrateAuthenticatedUser: true,
isPactStubEnabled: process.env.ENABLE_PACT_STUBS,
handlers: {
config: () => {
mergeConfig({

View File

@@ -57,208 +57,6 @@
},
"status": 200
}
},
{
"description": "A request for user's basic information",
"providerStates": [
{
"name": "I have a user's basic information"
}
],
"request": {
"method": "GET",
"path": "/api/user/v1/accounts/edx"
},
"response": {
"body": {
"bio": "This is my bio",
"country": "ME",
"dateJoined": "2017-06-07T00:44:23Z",
"email": "edx@example.com",
"isActive": true,
"name": "Lemon Seltzer",
"username": "edx",
"yearOfBirth": 1901,
"language_proficiencies": [{"code": "en"}]
},
"headers": {
"Content-Type": "application/json"
},
"matchingRules": {
"body": {
"$": {
"combine": "AND",
"matchers": [
{
"match": "type"
}
]
}
}
},
"status": 200
}
},
{
"description": "A request for user's basic information",
"providerStates": [
{
"name": "I have a user's basic information"
}
],
"request": {
"method": "POST",
"path": "/login_refresh"
},
"response": {
"body": {
"login": "This is my login_refresh"
},
"headers": {
"Content-Type": "application/json"
},
"matchingRules": {
"body": {
"$": {
"combine": "AND",
"matchers": [
{
"match": "type"
}
]
}
}
},
"status": 200
}
},
{
"description": "A request for user's basic information",
"providerStates": [
{
"name": "I have a user's basic information"
}
],
"request": {
"method": "GET",
"path": "/csrf/api/v1/token"
},
"response": {
"body": {
"csrfToken": "UYVGuFndy1sJPtAFtXPNQTnkZxZPKSYbvd3KKoKTyk6Vq9k01lg4vSsgjidWbnpt"
},
"headers": {
"Content-Type": "application/json"
},
"matchingRules": {
"body": {
"$": {
"combine": "AND",
"matchers": [
{
"match": "type"
}
]
}
}
},
"status": 200
}
},
{
"description": "A request for user's basic information",
"providerStates": [
{
"name": "I have a user's basic information"
}
],
"request": {
"method": "GET",
"path": "/api/user/v1/preferences/edx"
},
"response": {
"body": {
"pref-lang": "en"
},
"headers": {
"Content-Type": "application/json"
},
"matchingRules": {
"body": {
"$": {
"combine": "AND",
"matchers": [
{
"match": "type"
}
]
}
}
},
"status": 200
}
},
{
"description": "A request for user's basic information",
"providerStates": [
{
"name": "I have a user's basic information"
}
],
"request": {
"method": "GET",
"path": "/api/certificates/v0/certificates/edx/"
},
"response": {
"body": [],
"headers": {
"Content-Type": "application/json"
},
"matchingRules": {
"body": {
"$": {
"combine": "AND",
"matchers": [
{
"match": "type"
}
]
}
}
},
"status": 200
}
},
{
"description": "A request for user's basic information",
"providerStates": [
{
"name": "I have a user's basic information"
}
],
"request": {
"method": "POST",
"path": "/event"
},
"response": {
"body": [],
"headers": {
"Content-Type": "application/json"
},
"matchingRules": {
"body": {
"$": {
"combine": "AND",
"matchers": [
{
"match": "type"
}
]
}
}
},
"status": 200
}
}
],
"metadata": {

View File

@@ -6,7 +6,6 @@ const DateJoined = ({ date }) => {
if (date == null) {
return null;
}
return (
<p className="mb-0">
<FormattedMessage

View File

@@ -126,7 +126,7 @@ class ProfilePage extends React.Component {
return (
<span data-hj-suppress>
<h1 className="h2 mb-0 font-weight-bold">{this.props.params.username}</h1>
<h1 className="h2 mb-0 font-weight-bold text-truncate">{this.props.params.username}</h1>
<DateJoined date={dateJoined} />
{this.isYOBDisabled() && <UsernameDescription />}
<hr className="d-none d-md-block" />
@@ -178,6 +178,7 @@ class ProfilePage extends React.Component {
visibilityLearningGoal,
languageProficiencies,
visibilityLanguageProficiencies,
courseCertificates,
visibilityCourseCertificates,
bio,
visibilityBio,
@@ -196,6 +197,17 @@ class ProfilePage extends React.Component {
changeHandler: this.handleChange,
};
const isBlockVisible = (blockInfo) => this.isAuthenticatedUserProfile()
|| (!this.isAuthenticatedUserProfile() && Boolean(blockInfo));
const isLanguageBlockVisible = isBlockVisible(languageProficiencies.length);
const isEducationBlockVisible = isBlockVisible(levelOfEducation);
const isSocialLinksBLockVisible = isBlockVisible(socialLinks.some((link) => link.socialLink !== null));
const isBioBlockVisible = isBlockVisible(bio);
const isCertificatesBlockVisible = isBlockVisible(courseCertificates.length);
const isNameBlockVisible = isBlockVisible(name);
const isLocationBlockVisible = isBlockVisible(country);
return (
<div className="container-fluid">
<div className="row align-items-center pt-4 mb-4 pt-md-0 mb-md-0">
@@ -212,7 +224,8 @@ class ProfilePage extends React.Component {
/>
</div>
</div>
<div className="col pl-0">
<div>PluginPOC</div>
<div className="col">
<div className="d-md-none">
{this.renderHeadingLockup()}
</div>
@@ -230,46 +243,58 @@ class ProfilePage extends React.Component {
<div className="d-md-none mb-4">
{this.renderViewMyRecordsButton()}
</div>
<Name
name={name}
visibilityName={visibilityName}
formId="name"
{...commonFormProps}
/>
<Country
country={country}
visibilityCountry={visibilityCountry}
formId="country"
{...commonFormProps}
/>
<PreferredLanguage
languageProficiencies={languageProficiencies}
visibilityLanguageProficiencies={visibilityLanguageProficiencies}
formId="languageProficiencies"
{...commonFormProps}
/>
<Education
levelOfEducation={levelOfEducation}
visibilityLevelOfEducation={visibilityLevelOfEducation}
formId="levelOfEducation"
{...commonFormProps}
/>
<SocialLinks
socialLinks={socialLinks}
draftSocialLinksByPlatform={draftSocialLinksByPlatform}
visibilitySocialLinks={visibilitySocialLinks}
formId="socialLinks"
{...commonFormProps}
/>
{isNameBlockVisible && (
<Name
name={name}
visibilityName={visibilityName}
formId="name"
{...commonFormProps}
/>
)}
{isLocationBlockVisible && (
<Country
country={country}
visibilityCountry={visibilityCountry}
formId="country"
{...commonFormProps}
/>
)}
{isLanguageBlockVisible && (
<PreferredLanguage
languageProficiencies={languageProficiencies}
visibilityLanguageProficiencies={visibilityLanguageProficiencies}
formId="languageProficiencies"
{...commonFormProps}
/>
)}
{isEducationBlockVisible && (
<Education
levelOfEducation={levelOfEducation}
visibilityLevelOfEducation={visibilityLevelOfEducation}
formId="levelOfEducation"
{...commonFormProps}
/>
)}
{isSocialLinksBLockVisible && (
<SocialLinks
socialLinks={socialLinks}
draftSocialLinksByPlatform={draftSocialLinksByPlatform}
visibilitySocialLinks={visibilitySocialLinks}
formId="socialLinks"
{...commonFormProps}
/>
)}
</div>
<div className="pt-md-3 col-md-8 col-lg-7 offset-lg-1">
{!this.isYOBDisabled() && this.renderAgeMessage()}
<Bio
bio={bio}
visibilityBio={visibilityBio}
formId="bio"
{...commonFormProps}
/>
{isBioBlockVisible && (
<Bio
bio={bio}
visibilityBio={visibilityBio}
formId="bio"
{...commonFormProps}
/>
)}
{getConfig().ENABLE_SKILLS_BUILDER_PROFILE && (
<LearningGoal
learningGoal={learningGoal}
@@ -278,11 +303,13 @@ class ProfilePage extends React.Component {
{...commonFormProps}
/>
)}
<Certificates
visibilityCourseCertificates={visibilityCourseCertificates}
formId="certificates"
{...commonFormProps}
/>
{isCertificatesBlockVisible && (
<Certificates
visibilityCourseCertificates={visibilityCourseCertificates}
formId="certificates"
{...commonFormProps}
/>
)}
</div>
</div>
</div>

View File

@@ -114,16 +114,34 @@ describe('<ProfilePage />', () => {
expect(tree).toMatchSnapshot();
});
it('viewing other profile', () => {
it('viewing other profile with all fields', () => {
const contextValue = {
authenticatedUser: { userId: 123, username: 'staff', administrator: true },
config: getConfig(),
};
const component = (
<ProfilePageWrapper
contextValue={contextValue}
store={mockStore(storeMocks.viewOtherProfile)}
params={{ username: 'verified' }} // Override default params
store={mockStore({
...storeMocks.viewOtherProfile,
profilePage: {
...storeMocks.viewOtherProfile.profilePage,
account: {
...storeMocks.viewOtherProfile.profilePage.account,
name: 'user',
country: 'EN',
bio: 'bio',
courseCertificates: ['course certificates'],
levelOfEducation: 'some level',
languageProficiencies: ['some lang'],
socialLinks: ['twitter'],
timeZone: 'time zone',
accountPrivacy: 'all_users',
},
},
})}
match={{ params: { username: 'verified' } }} // Override default match
/>
);
const tree = renderer.create(component).toJSON();

View File

@@ -0,0 +1,219 @@
/* 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)));

View File

@@ -103,8 +103,11 @@ exports[`<ProfilePage /> Renders correctly in various states test country edit w
</div>
</div>
</div>
<div>
PluginPOC
</div>
<div
className="col pl-0"
className="col"
>
<div
className="d-md-none"
@@ -113,7 +116,7 @@ exports[`<ProfilePage /> Renders correctly in various states test country edit w
data-hj-suppress={true}
>
<h1
className="h2 mb-0 font-weight-bold"
className="h2 mb-0 font-weight-bold text-truncate"
>
staff
</h1>
@@ -191,7 +194,7 @@ exports[`<ProfilePage /> Renders correctly in various states test country edit w
data-hj-suppress={true}
>
<h1
className="h2 mb-0 font-weight-bold"
className="h2 mb-0 font-weight-bold text-truncate"
>
staff
</h1>
@@ -1696,7 +1699,7 @@ exports[`<ProfilePage /> Renders correctly in various states test country edit w
</svg>
</span>
<select
className="d-inline-block w-auto form-control"
className="d-inline-block form-control"
id="visibilityCountry"
name="visibilityCountry"
onChange={[Function]}
@@ -2486,8 +2489,11 @@ exports[`<ProfilePage /> Renders correctly in various states test education edit
</div>
</div>
</div>
<div>
PluginPOC
</div>
<div
className="col pl-0"
className="col"
>
<div
className="d-md-none"
@@ -2496,7 +2502,7 @@ exports[`<ProfilePage /> Renders correctly in various states test education edit
data-hj-suppress={true}
>
<h1
className="h2 mb-0 font-weight-bold"
className="h2 mb-0 font-weight-bold text-truncate"
>
staff
</h1>
@@ -2574,7 +2580,7 @@ exports[`<ProfilePage /> Renders correctly in various states test education edit
data-hj-suppress={true}
>
<h1
className="h2 mb-0 font-weight-bold"
className="h2 mb-0 font-weight-bold text-truncate"
>
staff
</h1>
@@ -3053,7 +3059,7 @@ exports[`<ProfilePage /> Renders correctly in various states test education edit
</svg>
</span>
<select
className="d-inline-block w-auto form-control"
className="d-inline-block form-control"
id="visibilityLevelOfEducation"
name="visibilityLevelOfEducation"
onChange={[Function]}
@@ -3663,8 +3669,11 @@ exports[`<ProfilePage /> Renders correctly in various states test preferreded la
</div>
</div>
</div>
<div>
PluginPOC
</div>
<div
className="col pl-0"
className="col"
>
<div
className="d-md-none"
@@ -3673,7 +3682,7 @@ exports[`<ProfilePage /> Renders correctly in various states test preferreded la
data-hj-suppress={true}
>
<h1
className="h2 mb-0 font-weight-bold"
className="h2 mb-0 font-weight-bold text-truncate"
>
staff
</h1>
@@ -3751,7 +3760,7 @@ exports[`<ProfilePage /> Renders correctly in various states test preferreded la
data-hj-suppress={true}
>
<h1
className="h2 mb-0 font-weight-bold"
className="h2 mb-0 font-weight-bold text-truncate"
>
staff
</h1>
@@ -5015,7 +5024,7 @@ exports[`<ProfilePage /> Renders correctly in various states test preferreded la
</svg>
</span>
<select
className="d-inline-block w-auto form-control"
className="d-inline-block form-control"
id="visibilityLanguageProficiencies"
name="visibilityLanguageProficiencies"
onChange={[Function]}
@@ -5643,7 +5652,7 @@ exports[`<ProfilePage /> Renders correctly in various states test preferreded la
</div>
`;
exports[`<ProfilePage /> Renders correctly in various states viewing other profile 1`] = `
exports[`<ProfilePage /> Renders correctly in various states viewing other profile with all fields 1`] = `
<div
className="profile-page"
>
@@ -5692,8 +5701,11 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
</div>
</div>
</div>
<div>
PluginPOC
</div>
<div
className="col pl-0"
className="col"
>
<div
className="d-md-none"
@@ -5702,9 +5714,9 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
data-hj-suppress={true}
>
<h1
className="h2 mb-0 font-weight-bold"
className="h2 mb-0 font-weight-bold text-truncate"
>
verified
staff
</h1>
<p
className="mb-0"
@@ -5747,7 +5759,52 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
</div>
<div
className="d-none d-md-block float-right"
/>
>
<a
className="pgn__hyperlink default-link standalone-link btn btn-primary"
href="http://localhost:18150/records"
onClick={[Function]}
rel="noopener noreferrer"
target="_blank"
>
View My Records
<span
className="pgn__hyperlink__external-icon"
title="Opens in a new tab"
>
<span
className="pgn__icon"
style={
Object {
"height": "1em",
"width": "1em",
}
}
>
<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="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
fill="currentColor"
/>
</svg>
<span
className="sr-only"
>
in a new tab
</span>
</span>
</span>
</a>
</div>
</div>
</div>
<div
@@ -5763,9 +5820,9 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
data-hj-suppress={true}
>
<h1
className="h2 mb-0 font-weight-bold"
className="h2 mb-0 font-weight-bold text-truncate"
>
verified
staff
</h1>
<p
className="mb-0"
@@ -5808,7 +5865,52 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
</div>
<div
className="d-md-none mb-4"
/>
>
<a
className="pgn__hyperlink default-link standalone-link btn btn-primary"
href="http://localhost:18150/records"
onClick={[Function]}
rel="noopener noreferrer"
target="_blank"
>
View My Records
<span
className="pgn__hyperlink__external-icon"
title="Opens in a new tab"
>
<span
className="pgn__icon"
style={
Object {
"height": "1em",
"width": "1em",
}
}
>
<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="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
fill="currentColor"
/>
</svg>
<span
className="sr-only"
>
in a new tab
</span>
</span>
</span>
</a>
</div>
<div
className="pgn-transition-replace-group position-relative mb-5"
style={
@@ -5816,7 +5918,32 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
"height": null,
}
}
/>
>
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
className="editable-item-header mb-2"
>
<h2
className="edit-section-header"
id={null}
>
Full Name
</h2>
</div>
<p
className="h5"
data-hj-suppress={true}
>
user
</p>
</div>
</div>
<div
className="pgn-transition-replace-group position-relative mb-5"
style={
@@ -5824,7 +5951,30 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
"height": null,
}
}
/>
>
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
className="editable-item-header mb-2"
>
<h2
className="edit-section-header"
id={null}
>
Location
</h2>
</div>
<p
className="h5"
data-hj-suppress={true}
/>
</div>
</div>
<div
className="pgn-transition-replace-group position-relative mb-5"
style={
@@ -5832,7 +5982,30 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
"height": null,
}
}
/>
>
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
className="editable-item-header mb-2"
>
<h2
className="edit-section-header"
id={null}
>
Primary Language Spoken
</h2>
</div>
<p
className="h5"
data-hj-suppress={true}
/>
</div>
</div>
<div
className="pgn-transition-replace-group position-relative mb-5"
style={
@@ -5840,7 +6013,32 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
"height": null,
}
}
/>
>
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
className="editable-item-header mb-2"
>
<h2
className="edit-section-header"
id={null}
>
Education
</h2>
</div>
<p
className="h5"
data-hj-suppress={true}
>
Other education
</p>
</div>
</div>
<div
className="pgn-transition-replace-group position-relative mb-5"
style={
@@ -5848,7 +6046,29 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
"height": null,
}
}
/>
>
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
className="editable-item-header mb-2"
>
<h2
className="edit-section-header"
id={null}
>
Social Links
</h2>
</div>
<ul
className="list-unstyled"
/>
</div>
</div>
</div>
<div
className="pt-md-3 col-md-8 col-lg-7 offset-lg-1"
@@ -5860,7 +6080,32 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
"height": null,
}
}
/>
>
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
className="editable-item-header mb-2"
>
<h2
className="edit-section-header"
id={null}
>
About Me
</h2>
</div>
<p
className="lead"
data-hj-suppress={true}
>
bio
</p>
</div>
</div>
<div
className="pgn-transition-replace-group position-relative mb-4"
style={
@@ -5868,7 +6113,27 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
"height": null,
}
}
/>
>
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
className="editable-item-header mb-2"
>
<h2
className="edit-section-header"
id={null}
>
My Certificates
</h2>
</div>
You don't have any certificates yet.
</div>
</div>
</div>
</div>
</div>
@@ -5947,8 +6212,11 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
</div>
</div>
</div>
<div>
PluginPOC
</div>
<div
className="col pl-0"
className="col"
>
<div
className="d-md-none"
@@ -5957,7 +6225,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
data-hj-suppress={true}
>
<h1
className="h2 mb-0 font-weight-bold"
className="h2 mb-0 font-weight-bold text-truncate"
>
staff
</h1>
@@ -6035,7 +6303,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
data-hj-suppress={true}
>
<h1
className="h2 mb-0 font-weight-bold"
className="h2 mb-0 font-weight-bold text-truncate"
>
staff
</h1>
@@ -6997,8 +7265,11 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
</div>
</div>
</div>
<div>
PluginPOC
</div>
<div
className="col pl-0"
className="col"
>
<div
className="d-md-none"
@@ -7007,7 +7278,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
data-hj-suppress={true}
>
<h1
className="h2 mb-0 font-weight-bold"
className="h2 mb-0 font-weight-bold text-truncate"
>
staff
</h1>
@@ -7085,7 +7356,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
data-hj-suppress={true}
>
<h1
className="h2 mb-0 font-weight-bold"
className="h2 mb-0 font-weight-bold text-truncate"
>
staff
</h1>
@@ -7773,7 +8044,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
</svg>
</span>
<select
className="d-inline-block w-auto form-control"
className="d-inline-block form-control"
id="visibilityBio"
name="visibilityBio"
onChange={[Function]}
@@ -8114,8 +8385,11 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
</div>
</div>
</div>
<div>
PluginPOC
</div>
<div
className="col pl-0"
className="col"
>
<div
className="d-md-none"
@@ -8124,7 +8398,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
data-hj-suppress={true}
>
<h1
className="h2 mb-0 font-weight-bold"
className="h2 mb-0 font-weight-bold text-truncate"
>
staff
</h1>
@@ -8202,7 +8476,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
data-hj-suppress={true}
>
<h1
className="h2 mb-0 font-weight-bold"
className="h2 mb-0 font-weight-bold text-truncate"
>
staff
</h1>
@@ -8898,7 +9172,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
</svg>
</span>
<select
className="d-inline-block w-auto form-control"
className="d-inline-block form-control"
id="visibilityBio"
name="visibilityBio"
onChange={[Function]}
@@ -9239,8 +9513,11 @@ exports[`<ProfilePage /> Renders correctly in various states without credentials
</div>
</div>
</div>
<div>
PluginPOC
</div>
<div
className="col pl-0"
className="col"
>
<div
className="d-md-none"
@@ -9249,7 +9526,7 @@ exports[`<ProfilePage /> Renders correctly in various states without credentials
data-hj-suppress={true}
>
<h1
className="h2 mb-0 font-weight-bold"
className="h2 mb-0 font-weight-bold text-truncate"
>
staff
</h1>
@@ -9282,7 +9559,7 @@ exports[`<ProfilePage /> Renders correctly in various states without credentials
data-hj-suppress={true}
>
<h1
className="h2 mb-0 font-weight-bold"
className="h2 mb-0 font-weight-bold text-truncate"
>
staff
</h1>

View File

@@ -66,6 +66,25 @@ export function* handleFetchProfile(action) {
} else {
[account, courseCertificates] = result;
}
// Set initial visibility values for account
// Set account_privacy as custom is necessary so that when viewing another user's profile,
// their full name is displayed and change visibility forms are worked correctly
if (isAuthenticatedUserProfile && result[0].accountPrivacy === 'all_users') {
yield call(ProfileApiService.patchPreferences, action.payload.username, {
account_privacy: 'custom',
'visibility.name': 'all_users',
'visibility.bio': 'all_users',
'visibility.course_certificates': 'all_users',
'visibility.country': 'all_users',
'visibility.date_joined': 'all_users',
'visibility.level_of_education': 'all_users',
'visibility.language_proficiencies': 'all_users',
'visibility.social_links': 'all_users',
'visibility.time_zone': 'all_users',
});
}
yield put(fetchProfileSuccess(
account,
preferences,

View File

@@ -35,9 +35,12 @@ export const editableFormModeSelector = createSelector(
// or is being hidden from us (for other users' profiles)
let propExists = account[formId] != null && account[formId].length > 0;
propExists = formId === 'certificates' ? certificates.length > 0 : propExists; // overwrite for certificates
// If this isn't the current user's profile or if
// If this isn't the current user's profile
if (!isAuthenticatedUserProfile) {
return 'static';
}
// the current user has no age set / under 13 ...
if (!isAuthenticatedUserProfile || account.requiresParentalConsent) {
if (account.requiresParentalConsent) {
// then there are only two options: static or nothing.
// We use 'null' as a return value because the consumers of
// getMode render nothing at all on a mode of null.
@@ -228,13 +231,13 @@ export const visibilitiesSelector = createSelector(
switch (accountPrivacy) {
case 'custom':
return {
visibilityBio: preferences.visibilityBio || 'private',
visibilityCourseCertificates: preferences.visibilityCourseCertificates || 'private',
visibilityCountry: preferences.visibilityCountry || 'private',
visibilityLevelOfEducation: preferences.visibilityLevelOfEducation || 'private',
visibilityLanguageProficiencies: preferences.visibilityLanguageProficiencies || 'private',
visibilityName: preferences.visibilityName || 'private',
visibilitySocialLinks: preferences.visibilitySocialLinks || 'private',
visibilityBio: preferences.visibilityBio || 'all_users',
visibilityCourseCertificates: preferences.visibilityCourseCertificates || 'all_users',
visibilityCountry: preferences.visibilityCountry || 'all_users',
visibilityLevelOfEducation: preferences.visibilityLevelOfEducation || 'all_users',
visibilityLanguageProficiencies: preferences.visibilityLanguageProficiencies || 'all_users',
visibilityName: preferences.visibilityName || 'all_users',
visibilitySocialLinks: preferences.visibilitySocialLinks || 'all_users',
};
case 'private':
return {

View File

@@ -0,0 +1,40 @@
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));

View File

@@ -119,7 +119,7 @@ exports[`<SocialLinks /> calls social links with edit mode bio 1`] = `
</svg>
</span>
<select
className="d-inline-block w-auto form-control"
className="d-inline-block form-control"
id="visibilitySocialLinks"
name="visibilitySocialLinks"
onChange={[Function]}

View File

@@ -39,7 +39,7 @@ const VisibilitySelect = ({ intl, className, ...props }) => {
<span className="d-inline-block ml-1 mr-2" style={{ width: '1.5rem' }}>
<FontAwesomeIcon icon={icon} />
</span>
<select className="d-inline-block w-auto form-control" {...props}>
<select className="d-inline-block form-control" {...props}>
<option key="private" value="private">
{intl.formatMessage(messages['profile.visibility.who.just.me'])}
</option>

View File

@@ -3,3 +3,4 @@ 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';

View File

@@ -162,4 +162,28 @@
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;
}
}
}

View File

@@ -4,11 +4,12 @@ import {
PageWrap,
} from '@edx/frontend-platform/react';
import { Routes, Route } from 'react-router-dom';
import { ProfilePage, NotFoundPage } from '../profile';
import { ProfilePage, NotFoundPage, ProfilePluginPage } 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>

View File

@@ -15,6 +15,7 @@ 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) => (
@@ -54,6 +55,22 @@ 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'),