Compare commits
28 Commits
open-relea
...
zhancock/r
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
397f6a77fd | ||
|
|
30c6b23766 | ||
|
|
58cbed25d7 | ||
|
|
73de57ddf4 | ||
|
|
7efe8f5cc3 | ||
|
|
263fe6d1a2 | ||
|
|
76f98d5bb2 | ||
|
|
e0386fe40b | ||
|
|
7d99677acd | ||
|
|
29bc2d9e17 | ||
|
|
27f3e79508 | ||
|
|
ed74bee760 | ||
|
|
c7a81fe07a | ||
|
|
d880aac569 | ||
|
|
072d608c64 | ||
|
|
9437142bc8 | ||
|
|
58c8ec5777 | ||
|
|
07357b9f10 | ||
|
|
1264b4245c | ||
|
|
e3ecee18e3 | ||
|
|
c3d96622e8 | ||
|
|
e577efbd27 | ||
|
|
df361236d0 | ||
|
|
e656f5445c | ||
|
|
f124c0d491 | ||
|
|
cc041ba348 | ||
|
|
257c9dcd7f | ||
|
|
1857b86c7e |
64
README.rst
64
README.rst
@@ -1,25 +1,20 @@
|
||||
#####################
|
||||
frontend-app-learning
|
||||
#####################
|
||||
|
||||
|codecov| |license|
|
||||
|
||||
********
|
||||
Purpose
|
||||
********
|
||||
*******
|
||||
|
||||
This is the Learning MFE (micro-frontend application), which renders all
|
||||
learner-facing course pages (like the course outline, the progress page,
|
||||
actual course content, etc).
|
||||
|
||||
Please tag **@edx/engage-squad** on any PRs or issues. Thanks.
|
||||
|
||||
.. |codecov| image:: https://codecov.io/gh/edx/frontend-app-learning/branch/master/graph/badge.svg?token=3z7XvuzTq3
|
||||
:target: https://codecov.io/gh/edx/frontend-app-learning
|
||||
.. |license| image:: https://img.shields.io/badge/license-AGPL-informational
|
||||
:target: https://github.com/openedx/frontend-app-account/blob/master/LICENSE
|
||||
|
||||
***************
|
||||
Getting Started
|
||||
***************
|
||||
|
||||
@@ -46,32 +41,38 @@ To use this application, `devstack <https://github.com/openedx/devstack>`__ must
|
||||
Cloning and Startup
|
||||
===================
|
||||
|
||||
.. code-block::
|
||||
1. Clone your new repo:
|
||||
|
||||
1. Clone your new repo:
|
||||
.. code-block:: bash
|
||||
|
||||
``git clone https://github.com/openedx/frontend-app-learning.git``
|
||||
git clone https://github.com/openedx/frontend-app-learning.git
|
||||
|
||||
2. Use node v18.x.
|
||||
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>`_.
|
||||
The current version of the micro-frontend build scripts supports 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:
|
||||
3. Install npm dependencies:
|
||||
|
||||
``cd frontend-app-learning && npm ci``
|
||||
.. code-block:: bash
|
||||
|
||||
4. Start the dev server:
|
||||
cd frontend-app-learning && npm ci
|
||||
|
||||
``npm start``
|
||||
4. Start the dev server:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
npm start
|
||||
|
||||
Local module development
|
||||
=========================
|
||||
|
||||
To develop locally on modules that are installed into this app, you'll need to create a ``module.config.js``
|
||||
file (which is git-ignored) that defines where to find your local modules, for instance::
|
||||
file (which is git-ignored) that defines where to find your local modules, for instance:
|
||||
|
||||
.. code-block:: js
|
||||
|
||||
module.exports = {
|
||||
/*
|
||||
@@ -100,8 +101,14 @@ The Learning MFE is similar to all the other Open edX MFEs. Read the Open
|
||||
edX Developer Guide's section on
|
||||
`MFE applications <https://edx.readthedocs.io/projects/edx-developer-docs/en/latest/developers_guide/micro_frontends_in_open_edx.html>`_.
|
||||
|
||||
Plugins
|
||||
=======
|
||||
This MFE can be customized using `Frontend Plugin Framework <https://github.com/openedx/frontend-plugin-framework>`_.
|
||||
|
||||
The parts of this MFE that can be customized in that manner are documented `here </src/plugin-slots>`_.
|
||||
|
||||
Environment Variables
|
||||
======================
|
||||
=====================
|
||||
|
||||
This MFE is configured via environment variables supplied at build time.
|
||||
All micro-frontends have a shared set of required environment variables,
|
||||
@@ -127,7 +134,7 @@ SOCIAL_UTM_MILESTONE_CAMPAIGN
|
||||
|
||||
SUPPORT_URL_CALCULATOR_MATH
|
||||
A link that explains how to use the in-course calculator. You can use the
|
||||
one in the example below, if you don't want to have your own branded version.
|
||||
one in the example below if you don't want to have your own branded version.
|
||||
|
||||
Example: https://support.edx.org/hc/en-us/articles/360000038428-Entering-math-expressions-in-assignments-or-the-calculator
|
||||
|
||||
@@ -140,7 +147,7 @@ SUPPORT_URL_ID_VERIFICATION
|
||||
|
||||
SUPPORT_URL_VERIFIED_CERTIFICATE
|
||||
A link that explains what a verified certificate is. You can use the
|
||||
one in the example below, if you don't want to have your own branded version.
|
||||
one in the example below if you don't want to have your own branded version.
|
||||
Optional.
|
||||
|
||||
Example: https://support.edx.org/hc/en-us/articles/206502008-What-is-a-verified-certificate
|
||||
@@ -156,13 +163,13 @@ TWITTER_URL
|
||||
A link to your Twitter account. The Twitter social-share link won't appear
|
||||
unless this is set. Optional.
|
||||
|
||||
Example: https://twitter.com/edXOnline
|
||||
Example: https://twitter.com/openedx
|
||||
|
||||
Getting Help
|
||||
===========
|
||||
============
|
||||
|
||||
If you're having trouble, we have discussion forums at
|
||||
https://discuss.openedx.org where you can connect with others in the community.
|
||||
If you're having trouble, we have `discussion forums`_
|
||||
where you can connect with others in the community.
|
||||
|
||||
Our real-time conversations are on Slack. You can request a `Slack
|
||||
invitation`_, then join our `community Slack workspace`_. Because this is a
|
||||
@@ -180,17 +187,18 @@ For more information about these options, see the `Getting Help`_ page.
|
||||
.. _community Slack workspace: https://openedx.slack.com/
|
||||
.. _#wg-frontend channel: https://openedx.slack.com/archives/C04BM6YC7A6
|
||||
.. _Getting Help: https://openedx.org/community/connect
|
||||
.. _discussion forums: https://discuss.openedx.org
|
||||
|
||||
Contributing
|
||||
============
|
||||
|
||||
Contributions are very welcome. Please read `How To Contribute`_ for details.
|
||||
Contributions are very welcome. Please read `How To Contribute`_ for details.
|
||||
|
||||
.. _How To Contribute: https://openedx.org/r/how-to-contribute
|
||||
|
||||
This project is currently accepting all types of contributions, bug fixes,
|
||||
security fixes, maintenance work, or new features. However, please make sure
|
||||
to have a discussion about your new feature idea with the maintainers prior to
|
||||
to discuss your new feature idea with the maintainers before
|
||||
beginning development to maximize the chances of your change being accepted.
|
||||
You can start a conversation by creating a new issue on this repo summarizing
|
||||
your idea.
|
||||
|
||||
3044
package-lock.json
generated
3044
package-lock.json
generated
@@ -18,19 +18,19 @@
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
|
||||
"@edx/frontend-component-footer": "^13.0.4",
|
||||
"@edx/frontend-component-header": "^5.0.2",
|
||||
"@edx/frontend-lib-learning-assistant": "^2.0.0",
|
||||
"@edx/frontend-lib-special-exams": "^3.0.1",
|
||||
"@edx/frontend-lib-learning-assistant": "^2.2.2",
|
||||
"@edx/frontend-lib-special-exams": "^3.1.3",
|
||||
"@edx/frontend-platform": "^7.1.2",
|
||||
"@edx/openedx-atlas": "^0.6.0",
|
||||
"@edx/react-unit-test-utils": "^2.0.0",
|
||||
"@edx/react-unit-test-utils": "2.0.0",
|
||||
"@fortawesome/fontawesome-svg-core": "1.3.0",
|
||||
"@fortawesome/free-brands-svg-icons": "5.15.4",
|
||||
"@fortawesome/free-regular-svg-icons": "5.15.4",
|
||||
"@fortawesome/free-solid-svg-icons": "5.15.4",
|
||||
"@fortawesome/react-fontawesome": "^0.1.4",
|
||||
"@openedx/frontend-plugin-framework": "^1.0.2",
|
||||
"@openedx/frontend-plugin-framework": "^1.1.2",
|
||||
"@openedx/frontend-slot-footer": "^1.0.2",
|
||||
"@openedx/paragon": "^22.3.0",
|
||||
"@popperjs/core": "2.11.8",
|
||||
"@reduxjs/toolkit": "1.8.1",
|
||||
@@ -2196,19 +2196,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer": {
|
||||
"version": "13.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-component-footer/-/frontend-component-footer-13.0.5.tgz",
|
||||
"integrity": "sha512-9AfXi9lNbDoth8wlsTq/5uBcxYC2Nny4HkudrmINXxVcdVTCG3aHoAzh02CnazdGgfBTHaBSxKp8hs9ShLRHxA==",
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-component-footer/-/frontend-component-footer-14.0.0.tgz",
|
||||
"integrity": "sha512-3Riz6ippBnPz1oq6gZgFBx27bJkNL+rwwKrv0uCuHV/5MscS1aYeKx1ZAMuUsxkKcGX6uhyU6PwM6agvnhKfNQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "6.5.2",
|
||||
"@fortawesome/free-brands-svg-icons": "6.5.2",
|
||||
"@fortawesome/free-regular-svg-icons": "6.5.2",
|
||||
"@fortawesome/free-solid-svg-icons": "6.5.2",
|
||||
"@fortawesome/react-fontawesome": "0.2.0",
|
||||
"lodash": "^4.17.21"
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"lodash": "^4.17.21",
|
||||
"ts-jest": "^29.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@edx/frontend-platform": "^7.0.0",
|
||||
"@edx/frontend-platform": "^7.0.0 || ^8.0.0",
|
||||
"@openedx/paragon": ">= 21.11.3 < 23.0.0",
|
||||
"prop-types": "^15.5.10",
|
||||
"react": "^16.9.0 || ^17.0.0",
|
||||
@@ -2220,6 +2223,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.2.tgz",
|
||||
"integrity": "sha512-gBxPg3aVO6J0kpfHNILc+NMhXnqHumFxOmjYCFfOiLZfwhnnfhtsdA2hfJlDnj+8PjAs6kKQPenOTKj3Rf7zHw==",
|
||||
"hasInstallScript": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
@@ -2229,6 +2233,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.2.tgz",
|
||||
"integrity": "sha512-5CdaCBGl8Rh9ohNdxeeTMxIj8oc3KNBgIeLMvJosBMdslK/UnEB8rzyDRrbKdL1kDweqBPo4GT9wvnakHWucZw==",
|
||||
"hasInstallScript": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-common-types": "6.5.2"
|
||||
},
|
||||
@@ -2241,6 +2246,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.5.2.tgz",
|
||||
"integrity": "sha512-zi5FNYdmKLnEc0jc0uuHH17kz/hfYTg4Uei0wMGzcoCL/4d3WM3u1VMc0iGGa31HuhV5i7ZK8ZlTCQrHqRHSGQ==",
|
||||
"hasInstallScript": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-common-types": "6.5.2"
|
||||
},
|
||||
@@ -2253,6 +2259,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.5.2.tgz",
|
||||
"integrity": "sha512-iabw/f5f8Uy2nTRtJ13XZTS1O5+t+anvlamJ3zJGLEVE2pKsAWhPv2lq01uQlfgCX7VaveT3EVs515cCN9jRbw==",
|
||||
"hasInstallScript": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-common-types": "6.5.2"
|
||||
},
|
||||
@@ -2265,6 +2272,7 @@
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.2.tgz",
|
||||
"integrity": "sha512-QWFZYXFE7O1Gr1dTIp+D6UcFUF0qElOnZptpi7PBUMylJh+vFmIedVe1Ir6RM1t2tEQLLSV1k7bR4o92M+uqlw==",
|
||||
"hasInstallScript": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-common-types": "6.5.2"
|
||||
},
|
||||
@@ -2276,6 +2284,7 @@
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz",
|
||||
"integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
@@ -2284,6 +2293,1076 @@
|
||||
"react": ">=16.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/@jest/core": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz",
|
||||
"integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/console": "^29.7.0",
|
||||
"@jest/reporters": "^29.7.0",
|
||||
"@jest/test-result": "^29.7.0",
|
||||
"@jest/transform": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/node": "*",
|
||||
"ansi-escapes": "^4.2.1",
|
||||
"chalk": "^4.0.0",
|
||||
"ci-info": "^3.2.0",
|
||||
"exit": "^0.1.2",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-changed-files": "^29.7.0",
|
||||
"jest-config": "^29.7.0",
|
||||
"jest-haste-map": "^29.7.0",
|
||||
"jest-message-util": "^29.7.0",
|
||||
"jest-regex-util": "^29.6.3",
|
||||
"jest-resolve": "^29.7.0",
|
||||
"jest-resolve-dependencies": "^29.7.0",
|
||||
"jest-runner": "^29.7.0",
|
||||
"jest-runtime": "^29.7.0",
|
||||
"jest-snapshot": "^29.7.0",
|
||||
"jest-util": "^29.7.0",
|
||||
"jest-validate": "^29.7.0",
|
||||
"jest-watcher": "^29.7.0",
|
||||
"micromatch": "^4.0.4",
|
||||
"pretty-format": "^29.7.0",
|
||||
"slash": "^3.0.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"node-notifier": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/@jest/environment": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz",
|
||||
"integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/fake-timers": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/node": "*",
|
||||
"jest-mock": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/@jest/fake-timers": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz",
|
||||
"integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
"@sinonjs/fake-timers": "^10.0.2",
|
||||
"@types/node": "*",
|
||||
"jest-message-util": "^29.7.0",
|
||||
"jest-mock": "^29.7.0",
|
||||
"jest-util": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/@jest/globals": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz",
|
||||
"integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/environment": "^29.7.0",
|
||||
"@jest/expect": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"jest-mock": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/@jest/source-map": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz",
|
||||
"integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "^0.3.18",
|
||||
"callsites": "^3.0.0",
|
||||
"graceful-fs": "^4.2.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/@jest/test-sequencer": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz",
|
||||
"integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/test-result": "^29.7.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-haste-map": "^29.7.0",
|
||||
"slash": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/@jest/transform": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz",
|
||||
"integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.11.6",
|
||||
"@jest/types": "^29.6.3",
|
||||
"@jridgewell/trace-mapping": "^0.3.18",
|
||||
"babel-plugin-istanbul": "^6.1.1",
|
||||
"chalk": "^4.0.0",
|
||||
"convert-source-map": "^2.0.0",
|
||||
"fast-json-stable-stringify": "^2.1.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-haste-map": "^29.7.0",
|
||||
"jest-regex-util": "^29.6.3",
|
||||
"jest-util": "^29.7.0",
|
||||
"micromatch": "^4.0.4",
|
||||
"pirates": "^4.0.4",
|
||||
"slash": "^3.0.0",
|
||||
"write-file-atomic": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/@jest/types": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
|
||||
"integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/schemas": "^29.6.3",
|
||||
"@types/istanbul-lib-coverage": "^2.0.0",
|
||||
"@types/istanbul-reports": "^3.0.0",
|
||||
"@types/node": "*",
|
||||
"@types/yargs": "^17.0.8",
|
||||
"chalk": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/@sinonjs/commons": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
|
||||
"integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"type-detect": "4.0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/@sinonjs/fake-timers": {
|
||||
"version": "10.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz",
|
||||
"integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@sinonjs/commons": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/@tootallnate/once": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
|
||||
"integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/@types/yargs": {
|
||||
"version": "17.0.32",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
|
||||
"integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/yargs-parser": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/acorn-globals": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz",
|
||||
"integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"acorn": "^8.1.0",
|
||||
"acorn-walk": "^8.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/ansi-styles": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
|
||||
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/babel-jest": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
|
||||
"integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/transform": "^29.7.0",
|
||||
"@types/babel__core": "^7.1.14",
|
||||
"babel-plugin-istanbul": "^6.1.1",
|
||||
"babel-preset-jest": "^29.6.3",
|
||||
"chalk": "^4.0.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"slash": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/babel-plugin-jest-hoist": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz",
|
||||
"integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.3.3",
|
||||
"@babel/types": "^7.3.3",
|
||||
"@types/babel__core": "^7.1.14",
|
||||
"@types/babel__traverse": "^7.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/babel-preset-jest": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz",
|
||||
"integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"babel-plugin-jest-hoist": "^29.6.3",
|
||||
"babel-preset-current-node-syntax": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/cjs-module-lexer": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz",
|
||||
"integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/convert-source-map": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
||||
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/cssom": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz",
|
||||
"integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/data-urls": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz",
|
||||
"integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"abab": "^2.0.6",
|
||||
"whatwg-mimetype": "^3.0.0",
|
||||
"whatwg-url": "^11.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/domexception": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
|
||||
"integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==",
|
||||
"deprecated": "Use your platform's native DOMException instead",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"webidl-conversions": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/emittery": {
|
||||
"version": "0.13.1",
|
||||
"resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz",
|
||||
"integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sindresorhus/emittery?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/html-encoding-sniffer": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
|
||||
"integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"whatwg-encoding": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/http-proxy-agent": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
|
||||
"integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@tootallnate/once": "2",
|
||||
"agent-base": "6",
|
||||
"debug": "4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/iconv-lite": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/jest": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz",
|
||||
"integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/core": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"import-local": "^3.0.2",
|
||||
"jest-cli": "^29.7.0"
|
||||
},
|
||||
"bin": {
|
||||
"jest": "bin/jest.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"node-notifier": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/jest-changed-files": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz",
|
||||
"integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"execa": "^5.0.0",
|
||||
"jest-util": "^29.7.0",
|
||||
"p-limit": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/jest-cli": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz",
|
||||
"integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/core": "^29.7.0",
|
||||
"@jest/test-result": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"chalk": "^4.0.0",
|
||||
"create-jest": "^29.7.0",
|
||||
"exit": "^0.1.2",
|
||||
"import-local": "^3.0.2",
|
||||
"jest-config": "^29.7.0",
|
||||
"jest-util": "^29.7.0",
|
||||
"jest-validate": "^29.7.0",
|
||||
"yargs": "^17.3.1"
|
||||
},
|
||||
"bin": {
|
||||
"jest": "bin/jest.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"node-notifier": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/jest-config": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz",
|
||||
"integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.11.6",
|
||||
"@jest/test-sequencer": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"babel-jest": "^29.7.0",
|
||||
"chalk": "^4.0.0",
|
||||
"ci-info": "^3.2.0",
|
||||
"deepmerge": "^4.2.2",
|
||||
"glob": "^7.1.3",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-circus": "^29.7.0",
|
||||
"jest-environment-node": "^29.7.0",
|
||||
"jest-get-type": "^29.6.3",
|
||||
"jest-regex-util": "^29.6.3",
|
||||
"jest-resolve": "^29.7.0",
|
||||
"jest-runner": "^29.7.0",
|
||||
"jest-util": "^29.7.0",
|
||||
"jest-validate": "^29.7.0",
|
||||
"micromatch": "^4.0.4",
|
||||
"parse-json": "^5.2.0",
|
||||
"pretty-format": "^29.7.0",
|
||||
"slash": "^3.0.0",
|
||||
"strip-json-comments": "^3.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": "*",
|
||||
"ts-node": ">=9.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
},
|
||||
"ts-node": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/jest-docblock": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz",
|
||||
"integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"detect-newline": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/jest-environment-jsdom": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz",
|
||||
"integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/environment": "^29.7.0",
|
||||
"@jest/fake-timers": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/jsdom": "^20.0.0",
|
||||
"@types/node": "*",
|
||||
"jest-mock": "^29.7.0",
|
||||
"jest-util": "^29.7.0",
|
||||
"jsdom": "^20.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"canvas": "^2.5.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"canvas": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/jest-environment-node": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz",
|
||||
"integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/environment": "^29.7.0",
|
||||
"@jest/fake-timers": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/node": "*",
|
||||
"jest-mock": "^29.7.0",
|
||||
"jest-util": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/jest-haste-map": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz",
|
||||
"integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/graceful-fs": "^4.1.3",
|
||||
"@types/node": "*",
|
||||
"anymatch": "^3.0.3",
|
||||
"fb-watchman": "^2.0.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-regex-util": "^29.6.3",
|
||||
"jest-util": "^29.7.0",
|
||||
"jest-worker": "^29.7.0",
|
||||
"micromatch": "^4.0.4",
|
||||
"walker": "^1.0.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "^2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/jest-leak-detector": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz",
|
||||
"integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"jest-get-type": "^29.6.3",
|
||||
"pretty-format": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/jest-mock": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz",
|
||||
"integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/node": "*",
|
||||
"jest-util": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/jest-regex-util": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz",
|
||||
"integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/jest-resolve": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz",
|
||||
"integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"chalk": "^4.0.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-haste-map": "^29.7.0",
|
||||
"jest-pnp-resolver": "^1.2.2",
|
||||
"jest-util": "^29.7.0",
|
||||
"jest-validate": "^29.7.0",
|
||||
"resolve": "^1.20.0",
|
||||
"resolve.exports": "^2.0.0",
|
||||
"slash": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/jest-resolve-dependencies": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz",
|
||||
"integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"jest-regex-util": "^29.6.3",
|
||||
"jest-snapshot": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/jest-runner": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz",
|
||||
"integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/console": "^29.7.0",
|
||||
"@jest/environment": "^29.7.0",
|
||||
"@jest/test-result": "^29.7.0",
|
||||
"@jest/transform": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/node": "*",
|
||||
"chalk": "^4.0.0",
|
||||
"emittery": "^0.13.1",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-docblock": "^29.7.0",
|
||||
"jest-environment-node": "^29.7.0",
|
||||
"jest-haste-map": "^29.7.0",
|
||||
"jest-leak-detector": "^29.7.0",
|
||||
"jest-message-util": "^29.7.0",
|
||||
"jest-resolve": "^29.7.0",
|
||||
"jest-runtime": "^29.7.0",
|
||||
"jest-util": "^29.7.0",
|
||||
"jest-watcher": "^29.7.0",
|
||||
"jest-worker": "^29.7.0",
|
||||
"p-limit": "^3.1.0",
|
||||
"source-map-support": "0.5.13"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/jest-runtime": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz",
|
||||
"integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/environment": "^29.7.0",
|
||||
"@jest/fake-timers": "^29.7.0",
|
||||
"@jest/globals": "^29.7.0",
|
||||
"@jest/source-map": "^29.6.3",
|
||||
"@jest/test-result": "^29.7.0",
|
||||
"@jest/transform": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/node": "*",
|
||||
"chalk": "^4.0.0",
|
||||
"cjs-module-lexer": "^1.0.0",
|
||||
"collect-v8-coverage": "^1.0.0",
|
||||
"glob": "^7.1.3",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-haste-map": "^29.7.0",
|
||||
"jest-message-util": "^29.7.0",
|
||||
"jest-mock": "^29.7.0",
|
||||
"jest-regex-util": "^29.6.3",
|
||||
"jest-resolve": "^29.7.0",
|
||||
"jest-snapshot": "^29.7.0",
|
||||
"jest-util": "^29.7.0",
|
||||
"slash": "^3.0.0",
|
||||
"strip-bom": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/jest-snapshot": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz",
|
||||
"integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.11.6",
|
||||
"@babel/generator": "^7.7.2",
|
||||
"@babel/plugin-syntax-jsx": "^7.7.2",
|
||||
"@babel/plugin-syntax-typescript": "^7.7.2",
|
||||
"@babel/types": "^7.3.3",
|
||||
"@jest/expect-utils": "^29.7.0",
|
||||
"@jest/transform": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"babel-preset-current-node-syntax": "^1.0.0",
|
||||
"chalk": "^4.0.0",
|
||||
"expect": "^29.7.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-diff": "^29.7.0",
|
||||
"jest-get-type": "^29.6.3",
|
||||
"jest-matcher-utils": "^29.7.0",
|
||||
"jest-message-util": "^29.7.0",
|
||||
"jest-util": "^29.7.0",
|
||||
"natural-compare": "^1.4.0",
|
||||
"pretty-format": "^29.7.0",
|
||||
"semver": "^7.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/jest-util": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
|
||||
"integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/node": "*",
|
||||
"chalk": "^4.0.0",
|
||||
"ci-info": "^3.2.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"picomatch": "^2.2.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/jest-validate": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz",
|
||||
"integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
"camelcase": "^6.2.0",
|
||||
"chalk": "^4.0.0",
|
||||
"jest-get-type": "^29.6.3",
|
||||
"leven": "^3.1.0",
|
||||
"pretty-format": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/jest-watcher": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz",
|
||||
"integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/test-result": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/node": "*",
|
||||
"ansi-escapes": "^4.2.1",
|
||||
"chalk": "^4.0.0",
|
||||
"emittery": "^0.13.1",
|
||||
"jest-util": "^29.7.0",
|
||||
"string-length": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/jsdom": {
|
||||
"version": "20.0.3",
|
||||
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz",
|
||||
"integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"abab": "^2.0.6",
|
||||
"acorn": "^8.8.1",
|
||||
"acorn-globals": "^7.0.0",
|
||||
"cssom": "^0.5.0",
|
||||
"cssstyle": "^2.3.0",
|
||||
"data-urls": "^3.0.2",
|
||||
"decimal.js": "^10.4.2",
|
||||
"domexception": "^4.0.0",
|
||||
"escodegen": "^2.0.0",
|
||||
"form-data": "^4.0.0",
|
||||
"html-encoding-sniffer": "^3.0.0",
|
||||
"http-proxy-agent": "^5.0.0",
|
||||
"https-proxy-agent": "^5.0.1",
|
||||
"is-potential-custom-element-name": "^1.0.1",
|
||||
"nwsapi": "^2.2.2",
|
||||
"parse5": "^7.1.1",
|
||||
"saxes": "^6.0.0",
|
||||
"symbol-tree": "^3.2.4",
|
||||
"tough-cookie": "^4.1.2",
|
||||
"w3c-xmlserializer": "^4.0.0",
|
||||
"webidl-conversions": "^7.0.0",
|
||||
"whatwg-encoding": "^2.0.0",
|
||||
"whatwg-mimetype": "^3.0.0",
|
||||
"whatwg-url": "^11.0.0",
|
||||
"ws": "^8.11.0",
|
||||
"xml-name-validator": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"canvas": "^2.5.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"canvas": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/parse5": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
|
||||
"integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"entities": "^4.4.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/pretty-format": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
|
||||
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/schemas": "^29.6.3",
|
||||
"ansi-styles": "^5.0.0",
|
||||
"react-is": "^18.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/react-is": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/saxes": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
|
||||
"integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"xmlchars": "^2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=v12.22.7"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/semver": {
|
||||
"version": "7.6.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
|
||||
"integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/slash": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
||||
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/source-map-support": {
|
||||
"version": "0.5.13",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
|
||||
"integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/tr46": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
|
||||
"integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"punycode": "^2.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/ts-jest": {
|
||||
"version": "29.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.2.tgz",
|
||||
"integrity": "sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"bs-logger": "0.x",
|
||||
"fast-json-stable-stringify": "2.x",
|
||||
"jest-util": "^29.0.0",
|
||||
"json5": "^2.2.3",
|
||||
"lodash.memoize": "4.x",
|
||||
"make-error": "1.x",
|
||||
"semver": "^7.5.3",
|
||||
"yargs-parser": "^21.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"ts-jest": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16.10.0 || ^18.0.0 || >=20.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": ">=7.0.0-beta.0 <8",
|
||||
"@jest/types": "^29.0.0",
|
||||
"babel-jest": "^29.0.0",
|
||||
"jest": "^29.0.0",
|
||||
"typescript": ">=4.3 <6"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@babel/core": {
|
||||
"optional": true
|
||||
},
|
||||
"@jest/types": {
|
||||
"optional": true
|
||||
},
|
||||
"babel-jest": {
|
||||
"optional": true
|
||||
},
|
||||
"esbuild": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/w3c-xmlserializer": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",
|
||||
"integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"xml-name-validator": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/webidl-conversions": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
||||
"integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/whatwg-encoding": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz",
|
||||
"integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"iconv-lite": "0.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/whatwg-mimetype": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
|
||||
"integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/whatwg-url": {
|
||||
"version": "11.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
|
||||
"integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"tr46": "^3.0.0",
|
||||
"webidl-conversions": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/write-file-atomic": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz",
|
||||
"integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"imurmurhash": "^0.1.4",
|
||||
"signal-exit": "^3.0.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/ws": {
|
||||
"version": "8.17.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz",
|
||||
"integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bufferutil": "^4.0.1",
|
||||
"utf-8-validate": ">=5.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"bufferutil": {
|
||||
"optional": true
|
||||
},
|
||||
"utf-8-validate": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-footer/node_modules/xml-name-validator": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
|
||||
"integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-component-header": {
|
||||
"version": "5.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-component-header/-/frontend-component-header-5.0.2.tgz",
|
||||
@@ -2389,9 +3468,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-lib-learning-assistant": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-lib-learning-assistant/-/frontend-lib-learning-assistant-2.0.0.tgz",
|
||||
"integrity": "sha512-7W5/5Rm2h1BjgdEvM8wa4yUuw3+N/6/cZZdvGiuIl3RDSisU/EpT95JMQyuqsBCSHClakIJ3TD7RHsj2eDQcSQ==",
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-lib-learning-assistant/-/frontend-lib-learning-assistant-2.2.2.tgz",
|
||||
"integrity": "sha512-Iy8W9Oz7k6kPp6wvhHgWQOne6I0tJY+/JMLlBhnrSsRZQfYV20IH9oXSDhzBAb4g/SDWvU/hu9fNWwd0l9lTkQ==",
|
||||
"dependencies": {
|
||||
"@edx/brand": "npm:@edx/brand-openedx@1.2.0",
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.36",
|
||||
@@ -2406,7 +3485,7 @@
|
||||
"uuid": "9.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@edx/frontend-platform": "^7.0.0",
|
||||
"@edx/frontend-platform": "^7.0.0 || ^8.0.0",
|
||||
"@openedx/paragon": "^22.0.0",
|
||||
"@reduxjs/toolkit": "1.8.1",
|
||||
"react": "16.14.0 || ^17.0.0",
|
||||
@@ -2468,9 +3547,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-lib-special-exams": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-lib-special-exams/-/frontend-lib-special-exams-3.0.1.tgz",
|
||||
"integrity": "sha512-9kW/sPECgtAg0VaIPaMuytAZQXbg4QdrKMEhwHWQF/sLRW7f33wBjZzWgyB62Aixc0fLceZDBUKC7VKzl0Qbdw==",
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-lib-special-exams/-/frontend-lib-special-exams-3.1.3.tgz",
|
||||
"integrity": "sha512-qkcnjPybt/eEE5txl8srUGTAVRiT92SFkjvPJEx9xuoUWuKTt0AmE0z3CVM2KypmcklBLlUZHVqZQVmgHlbtdA==",
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.34",
|
||||
"@fortawesome/free-brands-svg-icons": "5.11.2",
|
||||
@@ -2481,7 +3560,7 @@
|
||||
"eventemitter3": "^4.0.7"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@edx/frontend-platform": "^7.0.0",
|
||||
"@edx/frontend-platform": "^7.0.0 || ^8.0.0",
|
||||
"@openedx/paragon": "^22.0.0",
|
||||
"@reduxjs/toolkit": "^1.5.1",
|
||||
"prop-types": "^15.7.2",
|
||||
@@ -3261,7 +4340,6 @@
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz",
|
||||
"integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
@@ -3279,7 +4357,6 @@
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
|
||||
"integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/schemas": "^29.6.3",
|
||||
@@ -3297,7 +4374,6 @@
|
||||
"version": "17.0.32",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
|
||||
"integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/yargs-parser": "*"
|
||||
@@ -3307,7 +4383,6 @@
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
|
||||
"integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
@@ -3325,7 +4400,6 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
||||
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -3561,10 +4635,23 @@
|
||||
"node": ">= 10.14.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/expect": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz",
|
||||
"integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"expect": "^29.7.0",
|
||||
"jest-snapshot": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/expect-utils": {
|
||||
"version": "29.6.4",
|
||||
"resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.4.tgz",
|
||||
"integrity": "sha512-FEhkJhqtvBwgSpiTrocquJCdXPsyvNKcl/n7A3u7X4pVoF4bswm11c9d4AV+kfq2Gpv/mM8x7E7DsRvH+djkrg==",
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz",
|
||||
"integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==",
|
||||
"dependencies": {
|
||||
"jest-get-type": "^29.6.3"
|
||||
},
|
||||
@@ -3572,6 +4659,212 @@
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/expect/node_modules/@jest/transform": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz",
|
||||
"integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.11.6",
|
||||
"@jest/types": "^29.6.3",
|
||||
"@jridgewell/trace-mapping": "^0.3.18",
|
||||
"babel-plugin-istanbul": "^6.1.1",
|
||||
"chalk": "^4.0.0",
|
||||
"convert-source-map": "^2.0.0",
|
||||
"fast-json-stable-stringify": "^2.1.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-haste-map": "^29.7.0",
|
||||
"jest-regex-util": "^29.6.3",
|
||||
"jest-util": "^29.7.0",
|
||||
"micromatch": "^4.0.4",
|
||||
"pirates": "^4.0.4",
|
||||
"slash": "^3.0.0",
|
||||
"write-file-atomic": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/expect/node_modules/@jest/types": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
|
||||
"integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/schemas": "^29.6.3",
|
||||
"@types/istanbul-lib-coverage": "^2.0.0",
|
||||
"@types/istanbul-reports": "^3.0.0",
|
||||
"@types/node": "*",
|
||||
"@types/yargs": "^17.0.8",
|
||||
"chalk": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/expect/node_modules/@types/yargs": {
|
||||
"version": "17.0.32",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
|
||||
"integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/yargs-parser": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/expect/node_modules/ansi-styles": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
|
||||
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/expect/node_modules/convert-source-map": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
||||
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@jest/expect/node_modules/jest-haste-map": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz",
|
||||
"integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/graceful-fs": "^4.1.3",
|
||||
"@types/node": "*",
|
||||
"anymatch": "^3.0.3",
|
||||
"fb-watchman": "^2.0.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-regex-util": "^29.6.3",
|
||||
"jest-util": "^29.7.0",
|
||||
"jest-worker": "^29.7.0",
|
||||
"micromatch": "^4.0.4",
|
||||
"walker": "^1.0.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "^2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/expect/node_modules/jest-regex-util": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz",
|
||||
"integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/expect/node_modules/jest-snapshot": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz",
|
||||
"integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.11.6",
|
||||
"@babel/generator": "^7.7.2",
|
||||
"@babel/plugin-syntax-jsx": "^7.7.2",
|
||||
"@babel/plugin-syntax-typescript": "^7.7.2",
|
||||
"@babel/types": "^7.3.3",
|
||||
"@jest/expect-utils": "^29.7.0",
|
||||
"@jest/transform": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"babel-preset-current-node-syntax": "^1.0.0",
|
||||
"chalk": "^4.0.0",
|
||||
"expect": "^29.7.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-diff": "^29.7.0",
|
||||
"jest-get-type": "^29.6.3",
|
||||
"jest-matcher-utils": "^29.7.0",
|
||||
"jest-message-util": "^29.7.0",
|
||||
"jest-util": "^29.7.0",
|
||||
"natural-compare": "^1.4.0",
|
||||
"pretty-format": "^29.7.0",
|
||||
"semver": "^7.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/expect/node_modules/jest-util": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
|
||||
"integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/node": "*",
|
||||
"chalk": "^4.0.0",
|
||||
"ci-info": "^3.2.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"picomatch": "^2.2.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/expect/node_modules/pretty-format": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
|
||||
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/schemas": "^29.6.3",
|
||||
"ansi-styles": "^5.0.0",
|
||||
"react-is": "^18.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/expect/node_modules/react-is": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@jest/expect/node_modules/semver": {
|
||||
"version": "7.6.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
|
||||
"integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/expect/node_modules/slash": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
||||
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/expect/node_modules/write-file-atomic": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz",
|
||||
"integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"imurmurhash": "^0.1.4",
|
||||
"signal-exit": "^3.0.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jest/fake-timers": {
|
||||
"version": "26.6.2",
|
||||
"resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz",
|
||||
@@ -3747,7 +5040,6 @@
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz",
|
||||
"integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@bcoe/v8-coverage": "^0.2.3",
|
||||
@@ -3791,7 +5083,6 @@
|
||||
"version": "7.24.3",
|
||||
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.3.tgz",
|
||||
"integrity": "sha512-5FcvN1JHw2sHJChotgx8Ek0lyuh4kCKelgMTTqhYJJtloNvUfpAFMeNQUtdlIaktwrSV9LtCdqwk48wL2wBacQ==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.0",
|
||||
@@ -3822,7 +5113,6 @@
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz",
|
||||
"integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.11.6",
|
||||
@@ -3849,7 +5139,6 @@
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
|
||||
"integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/schemas": "^29.6.3",
|
||||
@@ -3867,7 +5156,6 @@
|
||||
"version": "17.0.32",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
|
||||
"integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/yargs-parser": "*"
|
||||
@@ -3877,14 +5165,12 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
||||
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": {
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz",
|
||||
"integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.23.9",
|
||||
@@ -3901,7 +5187,6 @@
|
||||
"version": "7.6.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
|
||||
"integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
@@ -3917,7 +5202,6 @@
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz",
|
||||
"integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
@@ -3943,7 +5227,6 @@
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz",
|
||||
"integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
@@ -3953,7 +5236,6 @@
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
|
||||
"integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
@@ -3971,7 +5253,6 @@
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
@@ -3984,7 +5265,6 @@
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
||||
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
@@ -3994,7 +5274,6 @@
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz",
|
||||
"integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"imurmurhash": "^0.1.4",
|
||||
@@ -4008,7 +5287,6 @@
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@jest/schemas": {
|
||||
@@ -4047,7 +5325,6 @@
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz",
|
||||
"integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/console": "^29.7.0",
|
||||
@@ -4063,7 +5340,6 @@
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
|
||||
"integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/schemas": "^29.6.3",
|
||||
@@ -4081,7 +5357,6 @@
|
||||
"version": "17.0.32",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
|
||||
"integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/yargs-parser": "*"
|
||||
@@ -4628,278 +5903,24 @@
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@openedx/frontend-plugin-framework/-/frontend-plugin-framework-1.0.2.tgz",
|
||||
"integrity": "sha512-9KVFtVp14foXuZVzIN7EyXMha9fhbu4SwXBle81HFzuHEpm46+qxtiIOktH6b4TYh74SMzuSphgP30MCHr1P8Q==",
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@openedx/frontend-plugin-framework/-/frontend-plugin-framework-1.1.2.tgz",
|
||||
"integrity": "sha512-DzhkZTbmxX09xXE6yyWs9xSq40cO+CgFF80nq1M7pJAeY0mtv7TWlQT8/YnhmjHS3Kj1u9GdqpRpYu8vHuRurA==",
|
||||
"dependencies": {
|
||||
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
|
||||
"@edx/frontend-component-footer": "13.0.3",
|
||||
"@edx/frontend-component-header": "5.0.2",
|
||||
"@edx/frontend-platform": "^7.1.0",
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.36",
|
||||
"@fortawesome/free-brands-svg-icons": "5.15.4",
|
||||
"@fortawesome/free-regular-svg-icons": "5.15.4",
|
||||
"@fortawesome/free-solid-svg-icons": "5.15.4",
|
||||
"@fortawesome/react-fontawesome": "0.2.0",
|
||||
"@openedx/paragon": "^21.0.0",
|
||||
"classnames": "^2.3.2",
|
||||
"core-js": "3.36.0",
|
||||
"prop-types": "15.8.1",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"react-error-boundary": "^4.0.11",
|
||||
"react-redux": "7.2.9",
|
||||
"react-router": "6.22.2",
|
||||
"react-router-dom": "6.22.2",
|
||||
"redux": "4.2.1",
|
||||
"regenerator-runtime": "0.14.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/@edx/frontend-component-footer": {
|
||||
"version": "13.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-component-footer/-/frontend-component-footer-13.0.3.tgz",
|
||||
"integrity": "sha512-09vX6qC7AcDwG02qhBzKr4x58hpe9FXZrA9ui2cJnsG53pKaNL+wvOSRtDUBNexCf+y/iPg+8RgR+4alkzhZhw==",
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "6.5.1",
|
||||
"@fortawesome/free-brands-svg-icons": "6.5.1",
|
||||
"@fortawesome/free-regular-svg-icons": "6.5.1",
|
||||
"@fortawesome/free-solid-svg-icons": "6.5.1",
|
||||
"@fortawesome/react-fontawesome": "0.2.0",
|
||||
"lodash": "^4.17.21"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@edx/frontend-platform": "^7.0.0",
|
||||
"@openedx/paragon": ">= 21.11.3 < 23.0.0",
|
||||
"prop-types": "^15.5.10",
|
||||
"react": "^16.9.0 || ^17.0.0",
|
||||
"react-dom": "^16.9.0 || ^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/@edx/frontend-component-footer/node_modules/@fortawesome/fontawesome-common-types": {
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.1.tgz",
|
||||
"integrity": "sha512-GkWzv+L6d2bI5f/Vk6ikJ9xtl7dfXtoRu3YGE6nq0p/FFqA1ebMOAWg3XgRyb0I6LYyYkiAo+3/KrwuBp8xG7A==",
|
||||
"hasInstallScript": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/@edx/frontend-component-footer/node_modules/@fortawesome/fontawesome-svg-core": {
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.1.tgz",
|
||||
"integrity": "sha512-MfRCYlQPXoLlpem+egxjfkEuP9UQswTrlCOsknus/NcMoblTH2g0jPrapbcIb04KGA7E2GZxbAccGZfWoYgsrQ==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-common-types": "6.5.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/@edx/frontend-component-footer/node_modules/@fortawesome/free-brands-svg-icons": {
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/free-brands-svg-icons/-/free-brands-svg-icons-6.5.1.tgz",
|
||||
"integrity": "sha512-093l7DAkx0aEtBq66Sf19MgoZewv1zeY9/4C7vSKPO4qMwEsW/2VYTUTpBtLwfb9T2R73tXaRDPmE4UqLCYHfg==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-common-types": "6.5.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/@edx/frontend-component-footer/node_modules/@fortawesome/free-regular-svg-icons": {
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-6.5.1.tgz",
|
||||
"integrity": "sha512-m6ShXn+wvqEU69wSP84coxLbNl7sGVZb+Ca+XZq6k30SzuP3X4TfPqtycgUh9ASwlNh5OfQCd8pDIWxl+O+LlQ==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-common-types": "6.5.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/@edx/frontend-component-footer/node_modules/@fortawesome/free-solid-svg-icons": {
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.5.1.tgz",
|
||||
"integrity": "sha512-S1PPfU3mIJa59biTtXJz1oI0+KAXW6bkAb31XKhxdxtuXDiUIFsih4JR1v5BbxY7hVHsD1RKq+jRkVRaf773NQ==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-common-types": "6.5.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/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",
|
||||
"integrity": "sha512-a/7BiSgobHAgBWeN7N0w+lAhInrGxksn13uK7231n2m8EDPE3BMCl9NZLTGrj9ZXfCmC6LM0QLqXidIizVQ6yg==",
|
||||
"hasInstallScript": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/@fortawesome/fontawesome-svg-core": {
|
||||
"version": "1.2.36",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.36.tgz",
|
||||
"integrity": "sha512-YUcsLQKYb6DmaJjIHdDWpBIGCcyE/W+p/LMGvjQem55Mm2XWVAP5kWTMKWLv9lwpCVjpLxPyOMOyUocP1GxrtA==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-common-types": "^0.2.36"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/@fortawesome/react-fontawesome": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz",
|
||||
"integrity": "sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==",
|
||||
"dependencies": {
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "~1 || ~6",
|
||||
"react": ">=16.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/@openedx/paragon": {
|
||||
"version": "21.13.1",
|
||||
"resolved": "https://registry.npmjs.org/@openedx/paragon/-/paragon-21.13.1.tgz",
|
||||
"integrity": "sha512-sLL+Z3ZWIRM6x+OrKZV0S7/SQpEcSeRcDm7E3FzhsnAWudsJCTELvSW+84uy/8dwV7mJhttsBPqQEtNafbCyYA==",
|
||||
"workspaces": [
|
||||
"example",
|
||||
"component-generator",
|
||||
"www",
|
||||
"icons",
|
||||
"dependent-usage-analyzer"
|
||||
],
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "^6.1.1",
|
||||
"@fortawesome/react-fontawesome": "^0.1.18",
|
||||
"@popperjs/core": "^2.11.4",
|
||||
"bootstrap": "^4.6.2",
|
||||
"chalk": "^4.1.2",
|
||||
"child_process": "^1.0.2",
|
||||
"classnames": "^2.3.1",
|
||||
"email-prop-type": "^3.0.0",
|
||||
"file-selector": "^0.6.0",
|
||||
"font-awesome": "^4.7.0",
|
||||
"glob": "^8.0.3",
|
||||
"inquirer": "^8.2.5",
|
||||
"lodash.uniqby": "^4.7.0",
|
||||
"mailto-link": "^2.0.0",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-bootstrap": "^1.6.5",
|
||||
"react-colorful": "^5.6.1",
|
||||
"react-dropzone": "^14.2.1",
|
||||
"react-focus-on": "^3.5.4",
|
||||
"react-imask": "^7.1.3",
|
||||
"react-loading-skeleton": "^3.1.0",
|
||||
"react-popper": "^2.2.5",
|
||||
"react-proptype-conditional-require": "^1.0.4",
|
||||
"react-responsive": "^8.2.0",
|
||||
"react-table": "^7.7.0",
|
||||
"react-transition-group": "^4.4.2",
|
||||
"tabbable": "^5.3.3",
|
||||
"uncontrollable": "^7.2.1",
|
||||
"uuid": "^9.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"paragon": "bin/paragon-scripts.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.6 || ^17.0.0",
|
||||
"react-dom": "^16.8.6 || ^17.0.0",
|
||||
"react-intl": "^5.25.1 || ^6.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/@openedx/paragon/node_modules/@fortawesome/fontawesome-common-types": {
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.1.tgz",
|
||||
"integrity": "sha512-GkWzv+L6d2bI5f/Vk6ikJ9xtl7dfXtoRu3YGE6nq0p/FFqA1ebMOAWg3XgRyb0I6LYyYkiAo+3/KrwuBp8xG7A==",
|
||||
"hasInstallScript": true,
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/@openedx/paragon/node_modules/@fortawesome/fontawesome-svg-core": {
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.1.tgz",
|
||||
"integrity": "sha512-MfRCYlQPXoLlpem+egxjfkEuP9UQswTrlCOsknus/NcMoblTH2g0jPrapbcIb04KGA7E2GZxbAccGZfWoYgsrQ==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-common-types": "6.5.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/@openedx/paragon/node_modules/@fortawesome/react-fontawesome": {
|
||||
"version": "0.1.19",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.19.tgz",
|
||||
"integrity": "sha512-Hyb+lB8T18cvLNX0S3llz7PcSOAJMLwiVKBuuzwM/nI5uoBw+gQjnf9il0fR1C3DKOI5Kc79pkJ4/xB0Uw9aFQ==",
|
||||
"dependencies": {
|
||||
"prop-types": "^15.8.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@fortawesome/fontawesome-svg-core": "~1 || ~6",
|
||||
"react": ">=16.x"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/@openedx/paragon/node_modules/glob": {
|
||||
"version": "8.1.0",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
|
||||
"integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
|
||||
"dependencies": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^5.0.1",
|
||||
"once": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/@remix-run/router": {
|
||||
"version": "1.15.2",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.2.tgz",
|
||||
"integrity": "sha512-+Rnav+CaoTE5QJc4Jcwh5toUpnVLKYbpU6Ys0zqbakqbaLQHeglLVHPfxOiQqdNmUy5C2lXz5dwC6tQNX2JW2Q==",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/brace-expansion": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
||||
"dependencies": {
|
||||
"balanced-match": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/cli-cursor": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
|
||||
"integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
|
||||
"dependencies": {
|
||||
"restore-cursor": "^3.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/cli-width": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-width/-/cli-width-3.0.0.tgz",
|
||||
"integrity": "sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==",
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
"@edx/frontend-platform": "^7.0.0 || ^8.0.0",
|
||||
"@openedx/paragon": "^21.0.0 || ^22.0.0",
|
||||
"prop-types": "^15.8.0",
|
||||
"react": "^17.0.0",
|
||||
"react-dom": "^17.0.0",
|
||||
"react-error-boundary": "^4.0.11"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/core-js": {
|
||||
@@ -4912,123 +5933,6 @@
|
||||
"url": "https://opencollective.com/core-js"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/figures": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
|
||||
"integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
|
||||
"dependencies": {
|
||||
"escape-string-regexp": "^1.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/inquirer": {
|
||||
"version": "8.2.6",
|
||||
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.6.tgz",
|
||||
"integrity": "sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg==",
|
||||
"dependencies": {
|
||||
"ansi-escapes": "^4.2.1",
|
||||
"chalk": "^4.1.1",
|
||||
"cli-cursor": "^3.1.0",
|
||||
"cli-width": "^3.0.0",
|
||||
"external-editor": "^3.0.3",
|
||||
"figures": "^3.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"mute-stream": "0.0.8",
|
||||
"ora": "^5.4.1",
|
||||
"run-async": "^2.4.0",
|
||||
"rxjs": "^7.5.5",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"through": "^2.3.6",
|
||||
"wrap-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/minimatch": {
|
||||
"version": "5.1.6",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
|
||||
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/mute-stream": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
|
||||
"integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA=="
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/react-error-boundary": {
|
||||
"version": "4.0.13",
|
||||
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.13.tgz",
|
||||
"integrity": "sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.13.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/react-router": {
|
||||
"version": "6.22.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.2.tgz",
|
||||
"integrity": "sha512-YD3Dzprzpcq+tBMHBS822tCjnWD3iIZbTeSXMY9LPSG541EfoBGyZ3bS25KEnaZjLcmQpw2AVLkFyfgXY8uvcw==",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.15.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/react-router-dom": {
|
||||
"version": "6.22.2",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.2.tgz",
|
||||
"integrity": "sha512-WgqxD2qySEIBPZ3w0sHH+PUAiamDeszls9tzqMPBDA1YYVucTBXLU7+gtRfcSnhe92A3glPnvSxK2dhNoAVOIQ==",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.15.2",
|
||||
"react-router": "6.22.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8",
|
||||
"react-dom": ">=16.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/redux": {
|
||||
"version": "4.2.1",
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
|
||||
@@ -5042,37 +5946,16 @@
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/restore-cursor": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
|
||||
"integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
|
||||
"node_modules/@openedx/frontend-slot-footer": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@openedx/frontend-slot-footer/-/frontend-slot-footer-1.0.2.tgz",
|
||||
"integrity": "sha512-Wmx/Das4wr3jYQ1/wk9ctYcM9ztfpY5fm6d5UKFSnKK1DbUbjliaPC3mdGR4wVRnH4MAf1OnNGZ8oj/bTDPGHg==",
|
||||
"dependencies": {
|
||||
"onetime": "^5.1.0",
|
||||
"signal-exit": "^3.0.2"
|
||||
"@openedx/frontend-plugin-framework": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/rxjs": {
|
||||
"version": "7.8.1",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
|
||||
"integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
"peerDependencies": {
|
||||
"@edx/frontend-component-footer": "*",
|
||||
"react": "^17.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/paragon": {
|
||||
@@ -5995,6 +6878,21 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/react-hooks/node_modules/react-error-boundary": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz",
|
||||
"integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"npm": ">=6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.13.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@testing-library/user-event": {
|
||||
"version": "13.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-13.5.0.tgz",
|
||||
@@ -6293,6 +7191,29 @@
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
|
||||
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
|
||||
},
|
||||
"node_modules/@types/jsdom": {
|
||||
"version": "20.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz",
|
||||
"integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"@types/tough-cookie": "*",
|
||||
"parse5": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/jsdom/node_modules/parse5": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
|
||||
"integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"entities": "^4.4.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/json-schema": {
|
||||
"version": "7.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz",
|
||||
@@ -6492,6 +7413,12 @@
|
||||
"@types/jest": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/tough-cookie": {
|
||||
"version": "4.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz",
|
||||
"integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@types/triple-beam": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.2.tgz",
|
||||
@@ -8161,6 +9088,18 @@
|
||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||
}
|
||||
},
|
||||
"node_modules/bs-logger": {
|
||||
"version": "0.2.6",
|
||||
"resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
|
||||
"integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-json-stable-stringify": "2.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/bser": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
|
||||
@@ -8727,7 +9666,6 @@
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.1",
|
||||
@@ -8740,14 +9678,12 @@
|
||||
"node_modules/cliui/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
},
|
||||
"node_modules/cliui/node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -8756,7 +9692,6 @@
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
@@ -8770,7 +9705,6 @@
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
@@ -9240,6 +10174,638 @@
|
||||
"js-yaml": "bin/js-yaml.js"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz",
|
||||
"integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
"chalk": "^4.0.0",
|
||||
"exit": "^0.1.2",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-config": "^29.7.0",
|
||||
"jest-util": "^29.7.0",
|
||||
"prompts": "^2.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"create-jest": "bin/create-jest.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/@jest/environment": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz",
|
||||
"integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/fake-timers": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/node": "*",
|
||||
"jest-mock": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/@jest/fake-timers": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz",
|
||||
"integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
"@sinonjs/fake-timers": "^10.0.2",
|
||||
"@types/node": "*",
|
||||
"jest-message-util": "^29.7.0",
|
||||
"jest-mock": "^29.7.0",
|
||||
"jest-util": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/@jest/globals": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz",
|
||||
"integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/environment": "^29.7.0",
|
||||
"@jest/expect": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"jest-mock": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/@jest/source-map": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz",
|
||||
"integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "^0.3.18",
|
||||
"callsites": "^3.0.0",
|
||||
"graceful-fs": "^4.2.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/@jest/test-sequencer": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz",
|
||||
"integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/test-result": "^29.7.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-haste-map": "^29.7.0",
|
||||
"slash": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/@jest/transform": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz",
|
||||
"integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.11.6",
|
||||
"@jest/types": "^29.6.3",
|
||||
"@jridgewell/trace-mapping": "^0.3.18",
|
||||
"babel-plugin-istanbul": "^6.1.1",
|
||||
"chalk": "^4.0.0",
|
||||
"convert-source-map": "^2.0.0",
|
||||
"fast-json-stable-stringify": "^2.1.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-haste-map": "^29.7.0",
|
||||
"jest-regex-util": "^29.6.3",
|
||||
"jest-util": "^29.7.0",
|
||||
"micromatch": "^4.0.4",
|
||||
"pirates": "^4.0.4",
|
||||
"slash": "^3.0.0",
|
||||
"write-file-atomic": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/@jest/types": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
|
||||
"integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/schemas": "^29.6.3",
|
||||
"@types/istanbul-lib-coverage": "^2.0.0",
|
||||
"@types/istanbul-reports": "^3.0.0",
|
||||
"@types/node": "*",
|
||||
"@types/yargs": "^17.0.8",
|
||||
"chalk": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/@sinonjs/commons": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
|
||||
"integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"type-detect": "4.0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/@sinonjs/fake-timers": {
|
||||
"version": "10.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz",
|
||||
"integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@sinonjs/commons": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/@types/yargs": {
|
||||
"version": "17.0.32",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
|
||||
"integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/yargs-parser": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/ansi-styles": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
|
||||
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/babel-jest": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz",
|
||||
"integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/transform": "^29.7.0",
|
||||
"@types/babel__core": "^7.1.14",
|
||||
"babel-plugin-istanbul": "^6.1.1",
|
||||
"babel-preset-jest": "^29.6.3",
|
||||
"chalk": "^4.0.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"slash": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/babel-plugin-jest-hoist": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz",
|
||||
"integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/template": "^7.3.3",
|
||||
"@babel/types": "^7.3.3",
|
||||
"@types/babel__core": "^7.1.14",
|
||||
"@types/babel__traverse": "^7.0.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/babel-preset-jest": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz",
|
||||
"integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"babel-plugin-jest-hoist": "^29.6.3",
|
||||
"babel-preset-current-node-syntax": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/cjs-module-lexer": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz",
|
||||
"integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/create-jest/node_modules/convert-source-map": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
||||
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/create-jest/node_modules/emittery": {
|
||||
"version": "0.13.1",
|
||||
"resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz",
|
||||
"integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sindresorhus/emittery?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/jest-config": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz",
|
||||
"integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.11.6",
|
||||
"@jest/test-sequencer": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"babel-jest": "^29.7.0",
|
||||
"chalk": "^4.0.0",
|
||||
"ci-info": "^3.2.0",
|
||||
"deepmerge": "^4.2.2",
|
||||
"glob": "^7.1.3",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-circus": "^29.7.0",
|
||||
"jest-environment-node": "^29.7.0",
|
||||
"jest-get-type": "^29.6.3",
|
||||
"jest-regex-util": "^29.6.3",
|
||||
"jest-resolve": "^29.7.0",
|
||||
"jest-runner": "^29.7.0",
|
||||
"jest-util": "^29.7.0",
|
||||
"jest-validate": "^29.7.0",
|
||||
"micromatch": "^4.0.4",
|
||||
"parse-json": "^5.2.0",
|
||||
"pretty-format": "^29.7.0",
|
||||
"slash": "^3.0.0",
|
||||
"strip-json-comments": "^3.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": "*",
|
||||
"ts-node": ">=9.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/node": {
|
||||
"optional": true
|
||||
},
|
||||
"ts-node": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/jest-docblock": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz",
|
||||
"integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"detect-newline": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/jest-environment-node": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz",
|
||||
"integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/environment": "^29.7.0",
|
||||
"@jest/fake-timers": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/node": "*",
|
||||
"jest-mock": "^29.7.0",
|
||||
"jest-util": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/jest-haste-map": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz",
|
||||
"integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/graceful-fs": "^4.1.3",
|
||||
"@types/node": "*",
|
||||
"anymatch": "^3.0.3",
|
||||
"fb-watchman": "^2.0.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-regex-util": "^29.6.3",
|
||||
"jest-util": "^29.7.0",
|
||||
"jest-worker": "^29.7.0",
|
||||
"micromatch": "^4.0.4",
|
||||
"walker": "^1.0.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "^2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/jest-leak-detector": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz",
|
||||
"integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"jest-get-type": "^29.6.3",
|
||||
"pretty-format": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/jest-mock": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz",
|
||||
"integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/node": "*",
|
||||
"jest-util": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/jest-regex-util": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz",
|
||||
"integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/jest-resolve": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz",
|
||||
"integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"chalk": "^4.0.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-haste-map": "^29.7.0",
|
||||
"jest-pnp-resolver": "^1.2.2",
|
||||
"jest-util": "^29.7.0",
|
||||
"jest-validate": "^29.7.0",
|
||||
"resolve": "^1.20.0",
|
||||
"resolve.exports": "^2.0.0",
|
||||
"slash": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/jest-runner": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz",
|
||||
"integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/console": "^29.7.0",
|
||||
"@jest/environment": "^29.7.0",
|
||||
"@jest/test-result": "^29.7.0",
|
||||
"@jest/transform": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/node": "*",
|
||||
"chalk": "^4.0.0",
|
||||
"emittery": "^0.13.1",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-docblock": "^29.7.0",
|
||||
"jest-environment-node": "^29.7.0",
|
||||
"jest-haste-map": "^29.7.0",
|
||||
"jest-leak-detector": "^29.7.0",
|
||||
"jest-message-util": "^29.7.0",
|
||||
"jest-resolve": "^29.7.0",
|
||||
"jest-runtime": "^29.7.0",
|
||||
"jest-util": "^29.7.0",
|
||||
"jest-watcher": "^29.7.0",
|
||||
"jest-worker": "^29.7.0",
|
||||
"p-limit": "^3.1.0",
|
||||
"source-map-support": "0.5.13"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/jest-runtime": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz",
|
||||
"integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/environment": "^29.7.0",
|
||||
"@jest/fake-timers": "^29.7.0",
|
||||
"@jest/globals": "^29.7.0",
|
||||
"@jest/source-map": "^29.6.3",
|
||||
"@jest/test-result": "^29.7.0",
|
||||
"@jest/transform": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/node": "*",
|
||||
"chalk": "^4.0.0",
|
||||
"cjs-module-lexer": "^1.0.0",
|
||||
"collect-v8-coverage": "^1.0.0",
|
||||
"glob": "^7.1.3",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-haste-map": "^29.7.0",
|
||||
"jest-message-util": "^29.7.0",
|
||||
"jest-mock": "^29.7.0",
|
||||
"jest-regex-util": "^29.6.3",
|
||||
"jest-resolve": "^29.7.0",
|
||||
"jest-snapshot": "^29.7.0",
|
||||
"jest-util": "^29.7.0",
|
||||
"slash": "^3.0.0",
|
||||
"strip-bom": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/jest-snapshot": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz",
|
||||
"integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.11.6",
|
||||
"@babel/generator": "^7.7.2",
|
||||
"@babel/plugin-syntax-jsx": "^7.7.2",
|
||||
"@babel/plugin-syntax-typescript": "^7.7.2",
|
||||
"@babel/types": "^7.3.3",
|
||||
"@jest/expect-utils": "^29.7.0",
|
||||
"@jest/transform": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"babel-preset-current-node-syntax": "^1.0.0",
|
||||
"chalk": "^4.0.0",
|
||||
"expect": "^29.7.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-diff": "^29.7.0",
|
||||
"jest-get-type": "^29.6.3",
|
||||
"jest-matcher-utils": "^29.7.0",
|
||||
"jest-message-util": "^29.7.0",
|
||||
"jest-util": "^29.7.0",
|
||||
"natural-compare": "^1.4.0",
|
||||
"pretty-format": "^29.7.0",
|
||||
"semver": "^7.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/jest-util": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
|
||||
"integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/node": "*",
|
||||
"chalk": "^4.0.0",
|
||||
"ci-info": "^3.2.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"picomatch": "^2.2.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/jest-validate": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz",
|
||||
"integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
"camelcase": "^6.2.0",
|
||||
"chalk": "^4.0.0",
|
||||
"jest-get-type": "^29.6.3",
|
||||
"leven": "^3.1.0",
|
||||
"pretty-format": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/jest-watcher": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz",
|
||||
"integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/test-result": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/node": "*",
|
||||
"ansi-escapes": "^4.2.1",
|
||||
"chalk": "^4.0.0",
|
||||
"emittery": "^0.13.1",
|
||||
"jest-util": "^29.7.0",
|
||||
"string-length": "^4.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/pretty-format": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
|
||||
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/schemas": "^29.6.3",
|
||||
"ansi-styles": "^5.0.0",
|
||||
"react-is": "^18.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/react-is": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/create-jest/node_modules/semver": {
|
||||
"version": "7.6.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
|
||||
"integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/slash": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
||||
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/source-map": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
|
||||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/source-map-support": {
|
||||
"version": "0.5.13",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz",
|
||||
"integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"source-map": "^0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/create-jest/node_modules/write-file-atomic": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz",
|
||||
"integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"imurmurhash": "^0.1.4",
|
||||
"signal-exit": "^3.0.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
@@ -9613,6 +11179,20 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/dedent": {
|
||||
"version": "1.5.3",
|
||||
"resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz",
|
||||
"integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"babel-plugin-macros": "^3.1.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"babel-plugin-macros": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/deep-equal": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.1.tgz",
|
||||
@@ -11272,15 +12852,15 @@
|
||||
}
|
||||
},
|
||||
"node_modules/expect": {
|
||||
"version": "29.6.4",
|
||||
"resolved": "https://registry.npmjs.org/expect/-/expect-29.6.4.tgz",
|
||||
"integrity": "sha512-F2W2UyQ8XYyftHT57dtfg8Ue3X5qLgm2sSug0ivvLRH/VKNRL/pDxg/TH7zVzbQB0tu80clNFy6LU7OS/VSEKA==",
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz",
|
||||
"integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==",
|
||||
"dependencies": {
|
||||
"@jest/expect-utils": "^29.6.4",
|
||||
"@jest/expect-utils": "^29.7.0",
|
||||
"jest-get-type": "^29.6.3",
|
||||
"jest-matcher-utils": "^29.6.4",
|
||||
"jest-message-util": "^29.6.3",
|
||||
"jest-util": "^29.6.3"
|
||||
"jest-matcher-utils": "^29.7.0",
|
||||
"jest-message-util": "^29.7.0",
|
||||
"jest-util": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
@@ -11303,17 +12883,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/expect/node_modules/@types/yargs": {
|
||||
"version": "17.0.24",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz",
|
||||
"integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==",
|
||||
"version": "17.0.32",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
|
||||
"integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==",
|
||||
"dependencies": {
|
||||
"@types/yargs-parser": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/expect/node_modules/jest-util": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.3.tgz",
|
||||
"integrity": "sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA==",
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
|
||||
"integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==",
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/node": "*",
|
||||
@@ -14295,6 +15875,428 @@
|
||||
"node": ">=8.12.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz",
|
||||
"integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/environment": "^29.7.0",
|
||||
"@jest/expect": "^29.7.0",
|
||||
"@jest/test-result": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/node": "*",
|
||||
"chalk": "^4.0.0",
|
||||
"co": "^4.6.0",
|
||||
"dedent": "^1.0.0",
|
||||
"is-generator-fn": "^2.0.0",
|
||||
"jest-each": "^29.7.0",
|
||||
"jest-matcher-utils": "^29.7.0",
|
||||
"jest-message-util": "^29.7.0",
|
||||
"jest-runtime": "^29.7.0",
|
||||
"jest-snapshot": "^29.7.0",
|
||||
"jest-util": "^29.7.0",
|
||||
"p-limit": "^3.1.0",
|
||||
"pretty-format": "^29.7.0",
|
||||
"pure-rand": "^6.0.0",
|
||||
"slash": "^3.0.0",
|
||||
"stack-utils": "^2.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/@jest/environment": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz",
|
||||
"integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/fake-timers": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/node": "*",
|
||||
"jest-mock": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/@jest/fake-timers": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz",
|
||||
"integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
"@sinonjs/fake-timers": "^10.0.2",
|
||||
"@types/node": "*",
|
||||
"jest-message-util": "^29.7.0",
|
||||
"jest-mock": "^29.7.0",
|
||||
"jest-util": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/@jest/globals": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz",
|
||||
"integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/environment": "^29.7.0",
|
||||
"@jest/expect": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"jest-mock": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/@jest/source-map": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz",
|
||||
"integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "^0.3.18",
|
||||
"callsites": "^3.0.0",
|
||||
"graceful-fs": "^4.2.9"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/@jest/transform": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz",
|
||||
"integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.11.6",
|
||||
"@jest/types": "^29.6.3",
|
||||
"@jridgewell/trace-mapping": "^0.3.18",
|
||||
"babel-plugin-istanbul": "^6.1.1",
|
||||
"chalk": "^4.0.0",
|
||||
"convert-source-map": "^2.0.0",
|
||||
"fast-json-stable-stringify": "^2.1.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-haste-map": "^29.7.0",
|
||||
"jest-regex-util": "^29.6.3",
|
||||
"jest-util": "^29.7.0",
|
||||
"micromatch": "^4.0.4",
|
||||
"pirates": "^4.0.4",
|
||||
"slash": "^3.0.0",
|
||||
"write-file-atomic": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/@jest/types": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
|
||||
"integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/schemas": "^29.6.3",
|
||||
"@types/istanbul-lib-coverage": "^2.0.0",
|
||||
"@types/istanbul-reports": "^3.0.0",
|
||||
"@types/node": "*",
|
||||
"@types/yargs": "^17.0.8",
|
||||
"chalk": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/@sinonjs/commons": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
|
||||
"integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"type-detect": "4.0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/@sinonjs/fake-timers": {
|
||||
"version": "10.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz",
|
||||
"integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@sinonjs/commons": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/@types/yargs": {
|
||||
"version": "17.0.32",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
|
||||
"integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/yargs-parser": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/ansi-styles": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
|
||||
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/cjs-module-lexer": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz",
|
||||
"integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/convert-source-map": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
||||
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/jest-each": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz",
|
||||
"integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
"chalk": "^4.0.0",
|
||||
"jest-get-type": "^29.6.3",
|
||||
"jest-util": "^29.7.0",
|
||||
"pretty-format": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/jest-haste-map": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz",
|
||||
"integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/graceful-fs": "^4.1.3",
|
||||
"@types/node": "*",
|
||||
"anymatch": "^3.0.3",
|
||||
"fb-watchman": "^2.0.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-regex-util": "^29.6.3",
|
||||
"jest-util": "^29.7.0",
|
||||
"jest-worker": "^29.7.0",
|
||||
"micromatch": "^4.0.4",
|
||||
"walker": "^1.0.8"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "^2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/jest-mock": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz",
|
||||
"integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/node": "*",
|
||||
"jest-util": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/jest-regex-util": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz",
|
||||
"integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/jest-resolve": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz",
|
||||
"integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"chalk": "^4.0.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-haste-map": "^29.7.0",
|
||||
"jest-pnp-resolver": "^1.2.2",
|
||||
"jest-util": "^29.7.0",
|
||||
"jest-validate": "^29.7.0",
|
||||
"resolve": "^1.20.0",
|
||||
"resolve.exports": "^2.0.0",
|
||||
"slash": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/jest-runtime": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz",
|
||||
"integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/environment": "^29.7.0",
|
||||
"@jest/fake-timers": "^29.7.0",
|
||||
"@jest/globals": "^29.7.0",
|
||||
"@jest/source-map": "^29.6.3",
|
||||
"@jest/test-result": "^29.7.0",
|
||||
"@jest/transform": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/node": "*",
|
||||
"chalk": "^4.0.0",
|
||||
"cjs-module-lexer": "^1.0.0",
|
||||
"collect-v8-coverage": "^1.0.0",
|
||||
"glob": "^7.1.3",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-haste-map": "^29.7.0",
|
||||
"jest-message-util": "^29.7.0",
|
||||
"jest-mock": "^29.7.0",
|
||||
"jest-regex-util": "^29.6.3",
|
||||
"jest-resolve": "^29.7.0",
|
||||
"jest-snapshot": "^29.7.0",
|
||||
"jest-util": "^29.7.0",
|
||||
"slash": "^3.0.0",
|
||||
"strip-bom": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/jest-snapshot": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz",
|
||||
"integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.11.6",
|
||||
"@babel/generator": "^7.7.2",
|
||||
"@babel/plugin-syntax-jsx": "^7.7.2",
|
||||
"@babel/plugin-syntax-typescript": "^7.7.2",
|
||||
"@babel/types": "^7.3.3",
|
||||
"@jest/expect-utils": "^29.7.0",
|
||||
"@jest/transform": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
"babel-preset-current-node-syntax": "^1.0.0",
|
||||
"chalk": "^4.0.0",
|
||||
"expect": "^29.7.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"jest-diff": "^29.7.0",
|
||||
"jest-get-type": "^29.6.3",
|
||||
"jest-matcher-utils": "^29.7.0",
|
||||
"jest-message-util": "^29.7.0",
|
||||
"jest-util": "^29.7.0",
|
||||
"natural-compare": "^1.4.0",
|
||||
"pretty-format": "^29.7.0",
|
||||
"semver": "^7.5.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/jest-util": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
|
||||
"integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
"@types/node": "*",
|
||||
"chalk": "^4.0.0",
|
||||
"ci-info": "^3.2.0",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"picomatch": "^2.2.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/jest-validate": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz",
|
||||
"integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
"camelcase": "^6.2.0",
|
||||
"chalk": "^4.0.0",
|
||||
"jest-get-type": "^29.6.3",
|
||||
"leven": "^3.1.0",
|
||||
"pretty-format": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/pretty-format": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
|
||||
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/schemas": "^29.6.3",
|
||||
"ansi-styles": "^5.0.0",
|
||||
"react-is": "^18.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/react-is": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/semver": {
|
||||
"version": "7.6.2",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
|
||||
"integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/slash": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
||||
"integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-circus/node_modules/write-file-atomic": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz",
|
||||
"integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"imurmurhash": "^0.1.4",
|
||||
"signal-exit": "^3.0.7"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.13.0 || ^14.15.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jest-cli": {
|
||||
"version": "26.6.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.6.3.tgz",
|
||||
@@ -14595,14 +16597,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jest-diff": {
|
||||
"version": "29.6.4",
|
||||
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.4.tgz",
|
||||
"integrity": "sha512-9F48UxR9e4XOEZvoUXEHSWY4qC4zERJaOfrbBg9JpbJOO43R1vN76REt/aMGZoY6GD5g84nnJiBIVlscegefpw==",
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz",
|
||||
"integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==",
|
||||
"dependencies": {
|
||||
"chalk": "^4.0.0",
|
||||
"diff-sequences": "^29.6.3",
|
||||
"jest-get-type": "^29.6.3",
|
||||
"pretty-format": "^29.6.3"
|
||||
"pretty-format": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
@@ -14620,9 +16622,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jest-diff/node_modules/pretty-format": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz",
|
||||
"integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==",
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
|
||||
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
|
||||
"dependencies": {
|
||||
"@jest/schemas": "^29.6.3",
|
||||
"ansi-styles": "^5.0.0",
|
||||
@@ -14633,9 +16635,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jest-diff/node_modules/react-is": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
|
||||
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
|
||||
},
|
||||
"node_modules/jest-docblock": {
|
||||
"version": "26.0.0",
|
||||
@@ -14959,14 +16961,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jest-matcher-utils": {
|
||||
"version": "29.6.4",
|
||||
"resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.4.tgz",
|
||||
"integrity": "sha512-KSzwyzGvK4HcfnserYqJHYi7sZVqdREJ9DMPAKVbS98JsIAvumihaNUbjrWw0St7p9IY7A9UskCW5MYlGmBQFQ==",
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz",
|
||||
"integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==",
|
||||
"dependencies": {
|
||||
"chalk": "^4.0.0",
|
||||
"jest-diff": "^29.6.4",
|
||||
"jest-diff": "^29.7.0",
|
||||
"jest-get-type": "^29.6.3",
|
||||
"pretty-format": "^29.6.3"
|
||||
"pretty-format": "^29.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||
@@ -14984,9 +16986,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jest-matcher-utils/node_modules/pretty-format": {
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz",
|
||||
"integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==",
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz",
|
||||
"integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==",
|
||||
"dependencies": {
|
||||
"@jest/schemas": "^29.6.3",
|
||||
"ansi-styles": "^5.0.0",
|
||||
@@ -14997,9 +16999,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/jest-matcher-utils/node_modules/react-is": {
|
||||
"version": "18.2.0",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
|
||||
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="
|
||||
},
|
||||
"node_modules/jest-message-util": {
|
||||
"version": "29.7.0",
|
||||
@@ -15835,7 +17837,6 @@
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz",
|
||||
"integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
@@ -15851,7 +17852,6 @@
|
||||
"version": "29.6.3",
|
||||
"resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz",
|
||||
"integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/schemas": "^29.6.3",
|
||||
@@ -15869,7 +17869,6 @@
|
||||
"version": "17.0.32",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
|
||||
"integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/yargs-parser": "*"
|
||||
@@ -15879,7 +17878,6 @@
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz",
|
||||
"integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/types": "^29.6.3",
|
||||
@@ -15897,7 +17895,6 @@
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
|
||||
"integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"has-flag": "^4.0.0"
|
||||
@@ -16570,6 +18567,12 @@
|
||||
"semver": "bin/semver"
|
||||
}
|
||||
},
|
||||
"node_modules/make-error": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/makeerror": {
|
||||
"version": "1.0.12",
|
||||
"resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz",
|
||||
@@ -19486,6 +21489,22 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/pure-rand": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz",
|
||||
"integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://github.com/sponsors/dubzzz"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fast-check"
|
||||
}
|
||||
],
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/purgecss": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/purgecss/-/purgecss-5.0.0.tgz",
|
||||
@@ -19854,16 +21873,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-error-boundary": {
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-3.1.4.tgz",
|
||||
"integrity": "sha512-uM9uPzZJTF6wRQORmSrvOIgt4lJ9MC1sNgEOj2XGsDTRE4kmpWxg7ENK9EWNKJRMAOY9z0MuF4yIfl6gp4sotA==",
|
||||
"version": "4.0.13",
|
||||
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.0.13.tgz",
|
||||
"integrity": "sha512-b6PwbdSv8XeOSYvjt8LpgpKrZ0yGdtZokYwkwV2wlcZbxgopHX/hgPl5VgpnoVOWd868n1hktM8Qm4b+02MiLQ==",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"npm": ">=6"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.13.1"
|
||||
}
|
||||
@@ -20756,6 +22772,15 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve.exports": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz",
|
||||
"integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/restore-cursor": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
|
||||
@@ -23949,7 +25974,6 @@
|
||||
"version": "9.2.0",
|
||||
"resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz",
|
||||
"integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==",
|
||||
"dev": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/trace-mapping": "^0.3.12",
|
||||
@@ -23964,7 +25988,6 @@
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
|
||||
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
|
||||
"dev": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/validate-npm-package-license": {
|
||||
@@ -24779,7 +26802,6 @@
|
||||
"version": "17.7.2",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
|
||||
"integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cliui": "^8.0.1",
|
||||
"escalade": "^3.1.1",
|
||||
@@ -24797,7 +26819,6 @@
|
||||
"version": "21.1.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
||||
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
@@ -24805,14 +26826,12 @@
|
||||
"node_modules/yargs/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
||||
},
|
||||
"node_modules/yargs/node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
@@ -24821,7 +26840,6 @@
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
|
||||
10
package.json
10
package.json
@@ -31,19 +31,19 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
|
||||
"@edx/frontend-component-footer": "^13.0.4",
|
||||
"@edx/frontend-component-header": "^5.0.2",
|
||||
"@edx/frontend-lib-learning-assistant": "^2.0.0",
|
||||
"@edx/frontend-lib-special-exams": "^3.0.1",
|
||||
"@edx/frontend-lib-learning-assistant": "^2.2.2",
|
||||
"@edx/frontend-lib-special-exams": "^3.1.3",
|
||||
"@edx/frontend-platform": "^7.1.2",
|
||||
"@edx/openedx-atlas": "^0.6.0",
|
||||
"@edx/react-unit-test-utils": "^2.0.0",
|
||||
"@edx/react-unit-test-utils": "2.0.0",
|
||||
"@fortawesome/fontawesome-svg-core": "1.3.0",
|
||||
"@fortawesome/free-brands-svg-icons": "5.15.4",
|
||||
"@fortawesome/free-regular-svg-icons": "5.15.4",
|
||||
"@fortawesome/free-solid-svg-icons": "5.15.4",
|
||||
"@fortawesome/react-fontawesome": "^0.1.4",
|
||||
"@openedx/frontend-plugin-framework": "^1.0.2",
|
||||
"@openedx/frontend-plugin-framework": "^1.1.2",
|
||||
"@openedx/frontend-slot-footer": "^1.0.2",
|
||||
"@openedx/paragon": "^22.3.0",
|
||||
"@popperjs/core": "2.11.8",
|
||||
"@reduxjs/toolkit": "1.8.1",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Button, Icon } from '@openedx/paragon';
|
||||
import { Search } from '@openedx/paragon/icons';
|
||||
import { Button } from '@openedx/paragon';
|
||||
import { ManageSearch } from '@openedx/paragon/icons';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import messages from './messages';
|
||||
import { useCoursewareSearchFeatureFlag, useCoursewareSearchParams } from './hooks';
|
||||
@@ -25,16 +25,17 @@ const CoursewareSearchToggle = ({
|
||||
if (!enabled) { return null; }
|
||||
|
||||
return (
|
||||
<div className="courseware-searc-toggle">
|
||||
<div className="courseware-search-toggle">
|
||||
<Button
|
||||
variant="tertiary"
|
||||
variant="outline-primary"
|
||||
size="sm"
|
||||
className="p-1 mt-2 mr-2 rounded-lg"
|
||||
className="p-1 mt-2 mr-2"
|
||||
aria-label={intl.formatMessage(messages.searchOpenAction)}
|
||||
onClick={handleSearchOpenClick}
|
||||
data-testid="courseware-search-open-button"
|
||||
iconAfter={ManageSearch}
|
||||
>
|
||||
<Icon src={Search} />
|
||||
{intl.formatMessage(messages.contentSearchButton)}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -2,79 +2,84 @@ import { defineMessages } from '@edx/frontend-platform/i18n';
|
||||
|
||||
const messages = defineMessages({
|
||||
searchOpenAction: {
|
||||
id: 'learn.coursewareSerch.openAction',
|
||||
id: 'learn.coursewareSearch.openAction',
|
||||
defaultMessage: 'Search within this course',
|
||||
description: 'Aria-label for a button that will pop up Courseware Search.',
|
||||
},
|
||||
contentSearchButton: {
|
||||
id: 'learn.coursewareSearch.contentSearchButton',
|
||||
defaultMessage: 'Content search',
|
||||
description: 'Text for a button that will pop up Courseware Search.',
|
||||
},
|
||||
searchSubmitLabel: {
|
||||
id: 'learn.coursewareSerch.submitLabel',
|
||||
id: 'learn.coursewareSearch.submitLabel',
|
||||
defaultMessage: 'Search',
|
||||
description: 'Button label that will submit Courseware Search.',
|
||||
},
|
||||
searchClearAction: {
|
||||
id: 'learn.coursewareSerch.clearAction',
|
||||
id: 'learn.coursewareSearch.clearAction',
|
||||
defaultMessage: 'Clear search',
|
||||
description: 'Button label that will the current Courseware Search input.',
|
||||
},
|
||||
searchCloseAction: {
|
||||
id: 'learn.coursewareSerch.closeAction',
|
||||
id: 'learn.coursewareSearch.closeAction',
|
||||
defaultMessage: 'Close the search form',
|
||||
description: 'Aria-label for a button that will close Courseware Search.',
|
||||
},
|
||||
searchModuleTitle: {
|
||||
id: 'learn.coursewareSerch.searchModuleTitle',
|
||||
id: 'learn.coursewareSearch.searchModuleTitle',
|
||||
defaultMessage: 'Search this course',
|
||||
description: 'Title for the Courseware Search module.',
|
||||
},
|
||||
searchBarPlaceholderText: {
|
||||
id: 'learn.coursewareSerch.searchBarPlaceholderText',
|
||||
id: 'learn.coursewareSearch.searchBarPlaceholderText',
|
||||
defaultMessage: 'Search',
|
||||
description: 'Placeholder text for the Courseware Search input control',
|
||||
},
|
||||
loading: {
|
||||
id: 'learn.coursewareSerch.loading',
|
||||
id: 'learn.coursewareSearch.loading',
|
||||
defaultMessage: 'Searching...',
|
||||
description: 'Screen reader text to use on the spinner while the search is performing.',
|
||||
},
|
||||
searchResultsNone: {
|
||||
id: 'learn.coursewareSerch.searchResultsNone',
|
||||
id: 'learn.coursewareSearch.searchResultsNone',
|
||||
defaultMessage: 'No results found.',
|
||||
description: 'Text to show when the Courseware Search found no results matching the criteria.',
|
||||
},
|
||||
searchResultsLabel: {
|
||||
id: 'learn.coursewareSerch.searchResultsLabel',
|
||||
id: 'learn.coursewareSearch.searchResultsLabel',
|
||||
defaultMessage: 'Results for "{keyword}":',
|
||||
description: 'Text to show above the search results response list.',
|
||||
},
|
||||
searchResultsError: {
|
||||
id: 'learn.coursewareSerch.searchResultsError',
|
||||
id: 'learn.coursewareSearch.searchResultsError',
|
||||
defaultMessage: 'There was an error on the search process. Please try again in a few minutes. If the problem persists, please contact the support team.',
|
||||
description: 'Error message to show to the users when there\'s an error with the endpoint or the returned payload format.',
|
||||
},
|
||||
|
||||
// These are translations for labeling the filters
|
||||
'filter:all': {
|
||||
id: 'learn.coursewareSerch.filter:all',
|
||||
id: 'learn.coursewareSearch.filter:all',
|
||||
defaultMessage: 'All content',
|
||||
description: 'Label for the search results filter that shows all content (no filter).',
|
||||
},
|
||||
'filter:text': {
|
||||
id: 'learn.coursewareSerch.filter:text',
|
||||
id: 'learn.coursewareSearch.filter:text',
|
||||
defaultMessage: 'Text',
|
||||
description: 'Label for the search results filter that shows results with text content.',
|
||||
},
|
||||
'filter:video': {
|
||||
id: 'learn.coursewareSerch.filter:video',
|
||||
id: 'learn.coursewareSearch.filter:video',
|
||||
defaultMessage: 'Video',
|
||||
description: 'Label for the search results filter that shows results with video content.',
|
||||
},
|
||||
'filter:sequence': {
|
||||
id: 'learn.coursewareSerch.filter:sequence',
|
||||
id: 'learn.coursewareSearch.filter:sequence',
|
||||
defaultMessage: 'Section',
|
||||
description: 'Label for the search results filter that shows results with section content.',
|
||||
},
|
||||
'filter:other': {
|
||||
id: 'learn.coursewareSerch.filter:other',
|
||||
id: 'learn.coursewareSearch.filter:other',
|
||||
defaultMessage: 'Other',
|
||||
description: 'Label for the search results filter that shows results with other content.',
|
||||
},
|
||||
|
||||
@@ -5,6 +5,7 @@ import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Button } from '@openedx/paragon';
|
||||
import { PluginSlot } from '@openedx/frontend-plugin-framework';
|
||||
import { AlertList } from '../../generic/user-messages';
|
||||
|
||||
import CourseDates from './widgets/CourseDates';
|
||||
@@ -16,7 +17,6 @@ import { fetchOutlineTab } from '../data';
|
||||
import messages from './messages';
|
||||
import Section from './Section';
|
||||
import ShiftDatesAlert from '../suggested-schedule-messaging/ShiftDatesAlert';
|
||||
import UpgradeNotification from '../../generic/upgrade-notification/UpgradeNotification';
|
||||
import UpgradeToShiftDatesAlert from '../suggested-schedule-messaging/UpgradeToShiftDatesAlert';
|
||||
import useCertificateAvailableAlert from './alerts/certificate-status-alert';
|
||||
import useCourseEndAlert from './alerts/course-end-alert';
|
||||
@@ -38,11 +38,9 @@ const OutlineTab = ({ intl }) => {
|
||||
isSelfPaced,
|
||||
org,
|
||||
title,
|
||||
userTimezone,
|
||||
} = useModel('courseHomeMeta', courseId);
|
||||
|
||||
const {
|
||||
accessExpiration,
|
||||
courseBlocks: {
|
||||
courses,
|
||||
sections,
|
||||
@@ -51,20 +49,12 @@ const OutlineTab = ({ intl }) => {
|
||||
selectedGoal,
|
||||
weeklyLearningGoalEnabled,
|
||||
} = {},
|
||||
datesBannerInfo,
|
||||
datesWidget: {
|
||||
courseDateBlocks,
|
||||
},
|
||||
enableProctoredExams,
|
||||
offer,
|
||||
timeOffsetMillis,
|
||||
verifiedMode,
|
||||
} = useModel('outline', courseId);
|
||||
|
||||
const {
|
||||
marketingUrl,
|
||||
} = useModel('coursewareMeta', courseId);
|
||||
|
||||
const [expandAll, setExpandAll] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -194,18 +184,9 @@ const OutlineTab = ({ intl }) => {
|
||||
/>
|
||||
)}
|
||||
<CourseTools />
|
||||
<UpgradeNotification
|
||||
offer={offer}
|
||||
verifiedMode={verifiedMode}
|
||||
accessExpiration={accessExpiration}
|
||||
contentTypeGatingEnabled={datesBannerInfo.contentTypeGatingEnabled}
|
||||
marketingUrl={marketingUrl}
|
||||
upsellPageName="course_home"
|
||||
userTimezone={userTimezone}
|
||||
shouldDisplayBorder
|
||||
timeOffsetMillis={timeOffsetMillis}
|
||||
courseId={courseId}
|
||||
org={org}
|
||||
<PluginSlot
|
||||
id="outline_tab_notifications_slot"
|
||||
pluginProps={{ courseId }}
|
||||
/>
|
||||
<CourseDates />
|
||||
<CourseHandouts />
|
||||
|
||||
@@ -5,7 +5,7 @@ import React from 'react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { Factory } from 'rosie';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { sendTrackEvent, sendTrackingLogEvent } from '@edx/frontend-platform/analytics';
|
||||
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import MockAdapter from 'axios-mock-adapter';
|
||||
import Cookies from 'js-cookie';
|
||||
@@ -132,6 +132,16 @@ describe('Outline Tab', () => {
|
||||
expect(expandedSectionNode).toHaveAttribute('aria-expanded', 'true');
|
||||
});
|
||||
|
||||
it('includes outline_tab_notifications_slot', async () => {
|
||||
const { courseBlocks } = await buildMinimalCourseBlocks(courseId, 'Title', { resumeBlock: true });
|
||||
setTabData({
|
||||
course_blocks: { blocks: courseBlocks.blocks },
|
||||
});
|
||||
await fetchAndRender();
|
||||
|
||||
expect(screen.getByTestId('outline_tab_notifications_slot')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('handles expand/collapse all button click', async () => {
|
||||
await fetchAndRender();
|
||||
// Button renders as "Expand All"
|
||||
@@ -1166,80 +1176,6 @@ describe('Outline Tab', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Upgrade Card', () => {
|
||||
it('renders title when upgrade is available', async () => {
|
||||
await fetchAndRender();
|
||||
expect(screen.queryByRole('heading', { name: 'Pursue a verified certificate' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('displays link to upgrade', async () => {
|
||||
await fetchAndRender();
|
||||
expect(screen.getByRole('link', { name: 'Upgrade for $149' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('viewing upgrade card sends analytics', async () => {
|
||||
sendTrackEvent.mockClear();
|
||||
sendTrackingLogEvent.mockClear();
|
||||
await fetchAndRender();
|
||||
|
||||
expect(sendTrackEvent).toHaveBeenCalledTimes(1);
|
||||
expect(sendTrackEvent).toHaveBeenCalledWith('Promotion Viewed', {
|
||||
org_key: 'edX',
|
||||
courserun_key: courseId,
|
||||
creative: 'sidebarupsell',
|
||||
name: 'In-Course Verification Prompt',
|
||||
position: 'sidebar-message',
|
||||
promotion_id: 'courseware_verified_certificate_upsell',
|
||||
});
|
||||
|
||||
expect(sendTrackingLogEvent).toHaveBeenCalledTimes(1);
|
||||
expect(sendTrackingLogEvent).toHaveBeenCalledWith('edx.bi.course.upgrade.sidebarupsell.displayed', {
|
||||
org_key: 'edX',
|
||||
courserun_key: courseId,
|
||||
});
|
||||
});
|
||||
|
||||
it('clicking upgrade link sends analytics', async () => {
|
||||
await fetchAndRender();
|
||||
|
||||
// Clearing after render to remove any events sent on view (ex. 'Promotion Viewed')
|
||||
sendTrackEvent.mockClear();
|
||||
sendTrackingLogEvent.mockClear();
|
||||
const upgradeButton = screen.getByRole('link', { name: 'Upgrade for $149' });
|
||||
|
||||
fireEvent.click(upgradeButton);
|
||||
|
||||
expect(sendTrackEvent).toHaveBeenCalledTimes(2);
|
||||
expect(sendTrackEvent).toHaveBeenNthCalledWith(1, 'Promotion Clicked', {
|
||||
org_key: 'edX',
|
||||
courserun_key: courseId,
|
||||
creative: 'sidebarupsell',
|
||||
name: 'In-Course Verification Prompt',
|
||||
position: 'sidebar-message',
|
||||
promotion_id: 'courseware_verified_certificate_upsell',
|
||||
});
|
||||
expect(sendTrackEvent).toHaveBeenNthCalledWith(2, 'edx.bi.ecommerce.upsell_links_clicked', {
|
||||
org_key: 'edX',
|
||||
courserun_key: courseId,
|
||||
linkCategory: 'green_upgrade',
|
||||
linkName: 'course_home_green',
|
||||
linkType: 'button',
|
||||
pageName: 'course_home',
|
||||
});
|
||||
|
||||
expect(sendTrackingLogEvent).toHaveBeenCalledTimes(2);
|
||||
expect(sendTrackingLogEvent).toHaveBeenNthCalledWith(1, 'edx.bi.course.upgrade.sidebarupsell.clicked', {
|
||||
org_key: 'edX',
|
||||
courserun_key: courseId,
|
||||
});
|
||||
expect(sendTrackingLogEvent).toHaveBeenNthCalledWith(2, 'edx.course.enrollment.upgrade.clicked', {
|
||||
org_key: 'edX',
|
||||
courserun_key: courseId,
|
||||
location: 'sidebar-message',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Account Activation Alert', () => {
|
||||
beforeEach(() => {
|
||||
const intersectionObserverMock = () => ({
|
||||
|
||||
@@ -16,23 +16,27 @@ const CourseTabsNavigation = ({
|
||||
return (
|
||||
<div id="courseTabsNavigation" className={classNames('course-tabs-navigation', className)}>
|
||||
<div className="container-xl">
|
||||
<Tabs
|
||||
className="nav-underline-tabs"
|
||||
aria-label={intl.formatMessage(messages.courseMaterial)}
|
||||
>
|
||||
{tabs.map(({ url, title, slug }) => (
|
||||
<a
|
||||
key={slug}
|
||||
className={classNames('nav-item flex-shrink-0 nav-link', { active: slug === activeTabSlug })}
|
||||
href={url}
|
||||
<div className="nav-bar">
|
||||
<div className="nav-menu">
|
||||
<Tabs
|
||||
className="nav-underline-tabs"
|
||||
aria-label={intl.formatMessage(messages.courseMaterial)}
|
||||
>
|
||||
{title}
|
||||
</a>
|
||||
))}
|
||||
</Tabs>
|
||||
</div>
|
||||
<div className="course-tabs-navigation__search-toggle">
|
||||
<CoursewareSearchToggle />
|
||||
{tabs.map(({ url, title, slug }) => (
|
||||
<a
|
||||
key={slug}
|
||||
className={classNames('nav-item flex-shrink-0 nav-link', { active: slug === activeTabSlug })}
|
||||
href={url}
|
||||
>
|
||||
{title}
|
||||
</a>
|
||||
))}
|
||||
</Tabs>
|
||||
</div>
|
||||
<div className="search-toggle">
|
||||
<CoursewareSearchToggle />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{show && <CoursewareSearch />}
|
||||
</div>
|
||||
|
||||
@@ -16,9 +16,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__search-toggle {
|
||||
position: absolute;
|
||||
top: .05rem;
|
||||
right: 0;
|
||||
.nav-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.nav-menu {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.search-toggle {
|
||||
flex-grow: 0;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -18,11 +18,20 @@ const SidebarProvider = ({
|
||||
unitId,
|
||||
children,
|
||||
}) => {
|
||||
const { verifiedMode } = useModel('courseHomeMeta', courseId);
|
||||
const topic = useModel('discussionTopics', unitId);
|
||||
const shouldDisplayFullScreen = useWindowSize().width < breakpoints.large.minWidth;
|
||||
const shouldDisplaySidebarOpen = useWindowSize().width > breakpoints.medium.minWidth;
|
||||
const query = new URLSearchParams(window.location.search);
|
||||
const initialSidebar = (shouldDisplaySidebarOpen || query.get('sidebar') === 'true')
|
||||
? SIDEBARS.DISCUSSIONS_NOTIFICATIONS.ID : null;
|
||||
const isInitiallySidebarOpen = shouldDisplaySidebarOpen || query.get('sidebar') === 'true';
|
||||
const sidebarKey = `sidebar.${courseId}`;
|
||||
|
||||
let initialSidebar = shouldDisplayFullScreen && sidebarKey in localStorage ? getLocalStorage(sidebarKey)
|
||||
: SIDEBARS.DISCUSSIONS_NOTIFICATIONS.ID;
|
||||
|
||||
if (!shouldDisplayFullScreen && isInitiallySidebarOpen) {
|
||||
initialSidebar = SIDEBARS.DISCUSSIONS_NOTIFICATIONS.ID;
|
||||
}
|
||||
const [currentSidebar, setCurrentSidebar] = useState(initialSidebar);
|
||||
const [notificationStatus, setNotificationStatus] = useState(getLocalStorage(`notificationStatus.${courseId}`));
|
||||
const [hideDiscussionbar, setHideDiscussionbar] = useState(false);
|
||||
@@ -30,8 +39,6 @@ const SidebarProvider = ({
|
||||
const [upgradeNotificationCurrentState, setUpgradeNotificationCurrentState] = useState(
|
||||
getLocalStorage(`upgradeNotificationCurrentState.${courseId}`),
|
||||
);
|
||||
const topic = useModel('discussionTopics', unitId);
|
||||
const { verifiedMode } = useModel('courseHomeMeta', courseId);
|
||||
const isDiscussionbarAvailable = (topic?.id && topic?.enabledInContext) || false;
|
||||
const isNotificationbarAvailable = !isEmpty(verifiedMode);
|
||||
|
||||
@@ -43,7 +50,9 @@ const SidebarProvider = ({
|
||||
useEffect(() => {
|
||||
setHideDiscussionbar(!isDiscussionbarAvailable);
|
||||
setHideNotificationbar(!isNotificationbarAvailable);
|
||||
setCurrentSidebar(SIDEBARS.DISCUSSIONS_NOTIFICATIONS.ID);
|
||||
if (initialSidebar && currentSidebar !== initialSidebar) {
|
||||
setCurrentSidebar(SIDEBARS.DISCUSSIONS_NOTIFICATIONS.ID);
|
||||
}
|
||||
}, [unitId, topic]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -52,17 +61,36 @@ const SidebarProvider = ({
|
||||
}
|
||||
}, [hideDiscussionbar, hideNotificationbar]);
|
||||
|
||||
const toggleSidebar = useCallback((sidebarId = null, widgetId = null) => {
|
||||
if (widgetId) {
|
||||
setHideDiscussionbar(prevWidgetId => (widgetId === WIDGETS.DISCUSSIONS ? true : prevWidgetId));
|
||||
setHideNotificationbar(prevWidgetId => (widgetId === WIDGETS.NOTIFICATIONS ? true : prevWidgetId));
|
||||
} else {
|
||||
setCurrentSidebar(prevSidebar => (sidebarId === prevSidebar ? null : sidebarId));
|
||||
setHideDiscussionbar(!isDiscussionbarAvailable);
|
||||
setHideNotificationbar(!isNotificationbarAvailable);
|
||||
const handleWidgetToggle = useCallback((widgetId, sidebarId) => {
|
||||
setHideDiscussionbar(prevWidgetId => (widgetId === WIDGETS.DISCUSSIONS ? true : prevWidgetId));
|
||||
setHideNotificationbar(prevWidgetId => (widgetId === WIDGETS.NOTIFICATIONS ? true : prevWidgetId));
|
||||
setLocalStorage(sidebarKey, sidebarId);
|
||||
}, []);
|
||||
|
||||
const handleSidebarToggle = useCallback((sidebarId) => {
|
||||
setCurrentSidebar(prevSidebar => (sidebarId === prevSidebar ? null : sidebarId));
|
||||
setHideDiscussionbar(!isDiscussionbarAvailable);
|
||||
setHideNotificationbar(!isNotificationbarAvailable);
|
||||
setLocalStorage(sidebarKey, sidebarId === currentSidebar ? null : sidebarId);
|
||||
}, [currentSidebar, isDiscussionbarAvailable, isNotificationbarAvailable]);
|
||||
|
||||
const clearSidebarKeyIfWidgetsUnavailable = useCallback((widgetId) => {
|
||||
if ((!isNotificationbarAvailable && widgetId === WIDGETS.DISCUSSIONS)
|
||||
|| (!isDiscussionbarAvailable && widgetId === WIDGETS.NOTIFICATIONS)) {
|
||||
setLocalStorage(sidebarKey, null);
|
||||
}
|
||||
}, [isDiscussionbarAvailable, isNotificationbarAvailable]);
|
||||
|
||||
const toggleSidebar = useCallback((sidebarId = null, widgetId = null) => {
|
||||
if (widgetId) {
|
||||
handleWidgetToggle(widgetId, sidebarId);
|
||||
} else {
|
||||
handleSidebarToggle(sidebarId);
|
||||
}
|
||||
|
||||
clearSidebarKeyIfWidgetsUnavailable(widgetId);
|
||||
}, [handleWidgetToggle, handleSidebarToggle, clearSidebarKeyIfWidgetsUnavailable]);
|
||||
|
||||
const contextValue = useMemo(() => ({
|
||||
toggleSidebar,
|
||||
onNotificationSeen,
|
||||
|
||||
@@ -104,7 +104,7 @@ SidebarBase.propTypes = {
|
||||
|
||||
SidebarBase.defaultProps = {
|
||||
title: '',
|
||||
width: '50rem',
|
||||
width: '45rem',
|
||||
allowFullHeight: false,
|
||||
showTitleBar: true,
|
||||
className: '',
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import React, { useContext, useEffect, useMemo } from 'react';
|
||||
|
||||
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||
import { PluginSlot } from '@openedx/frontend-plugin-framework';
|
||||
import { useModel } from '../../../../../../generic/model-store';
|
||||
import UpgradeNotification from '../../../../../../generic/upgrade-notification/UpgradeNotification';
|
||||
import { WIDGETS } from '../../../../../../constants';
|
||||
import SidebarContext from '../../../SidebarContext';
|
||||
|
||||
@@ -20,17 +20,11 @@ const NotificationsWidget = () => {
|
||||
const course = useModel('coursewareMeta', courseId);
|
||||
|
||||
const {
|
||||
accessExpiration,
|
||||
contentTypeGatingEnabled,
|
||||
end,
|
||||
enrollmentEnd,
|
||||
enrollmentMode,
|
||||
enrollmentStart,
|
||||
marketingUrl,
|
||||
offer,
|
||||
start,
|
||||
timeOffsetMillis,
|
||||
userTimezone,
|
||||
verificationStatus,
|
||||
} = course;
|
||||
|
||||
@@ -58,6 +52,10 @@ const NotificationsWidget = () => {
|
||||
verification_status: verificationStatus,
|
||||
};
|
||||
|
||||
const onToggleSidebar = () => {
|
||||
toggleSidebar(currentSidebar, WIDGETS.NOTIFICATIONS);
|
||||
};
|
||||
|
||||
// After three seconds, update notificationSeen (to hide red dot)
|
||||
useEffect(() => {
|
||||
setTimeout(onNotificationSeen, 3000);
|
||||
@@ -68,21 +66,14 @@ const NotificationsWidget = () => {
|
||||
|
||||
return (
|
||||
<div className="border border-light-400 rounded-sm" data-testid="notification-widget">
|
||||
<UpgradeNotification
|
||||
offer={offer}
|
||||
verifiedMode={verifiedMode}
|
||||
accessExpiration={accessExpiration}
|
||||
contentTypeGatingEnabled={contentTypeGatingEnabled}
|
||||
marketingUrl={marketingUrl}
|
||||
upsellPageName="in_course"
|
||||
userTimezone={userTimezone}
|
||||
shouldDisplayBorder={false}
|
||||
timeOffsetMillis={timeOffsetMillis}
|
||||
courseId={courseId}
|
||||
org={org}
|
||||
upgradeNotificationCurrentState={upgradeNotificationCurrentState}
|
||||
setupgradeNotificationCurrentState={setUpgradeNotificationCurrentState}
|
||||
toggleSidebar={() => toggleSidebar(currentSidebar, WIDGETS.NOTIFICATIONS)}
|
||||
<PluginSlot
|
||||
id="notification_widget_slot"
|
||||
pluginProps={{
|
||||
courseId,
|
||||
notificationCurrentState: upgradeNotificationCurrentState,
|
||||
setNotificationCurrentState: setUpgradeNotificationCurrentState,
|
||||
toggleSidebar: onToggleSidebar,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -18,9 +18,22 @@ import SidebarContext from '../../../SidebarContext';
|
||||
import NotificationsWidget from './NotificationsWidget';
|
||||
import setupDiscussionSidebar from '../../../../test-utils';
|
||||
|
||||
initializeMockApp();
|
||||
jest.mock('@edx/frontend-platform/analytics');
|
||||
|
||||
/* eslint-disable react/prop-types */
|
||||
jest.mock('@openedx/frontend-plugin-framework', () => ({
|
||||
...jest.requireActual('@openedx/frontend-plugin-framework'),
|
||||
Plugin: () => 'Plugin',
|
||||
PluginSlot: ({ id, pluginProps }) => (
|
||||
<div data-testid={id}>
|
||||
<button type="button" onClick={pluginProps?.toggleSidebar}>Close</button>
|
||||
PluginSlot_{id}
|
||||
</div>
|
||||
),
|
||||
}));
|
||||
|
||||
initializeMockApp();
|
||||
|
||||
describe('NotificationsWidget', () => {
|
||||
let axiosMock;
|
||||
let store;
|
||||
@@ -78,7 +91,7 @@ describe('NotificationsWidget', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('renders upgrade card', async () => {
|
||||
it('includes notification_widget_slot', async () => {
|
||||
await fetchAndRender(
|
||||
<SidebarContext.Provider value={{
|
||||
currentSidebar: ID,
|
||||
@@ -90,11 +103,7 @@ describe('NotificationsWidget', () => {
|
||||
<NotificationsWidget />
|
||||
</SidebarContext.Provider>,
|
||||
);
|
||||
const UpgradeNotification = document.querySelector('.upgrade-notification');
|
||||
|
||||
expect(UpgradeNotification).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: 'Upgrade for $149' })).toBeInTheDocument();
|
||||
expect(screen.queryByText('You have no new notifications at this time.')).not.toBeInTheDocument();
|
||||
expect(screen.getByTestId('notification_widget_slot')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders no notifications bar if no verified mode', async () => {
|
||||
|
||||
@@ -9,12 +9,14 @@ import {
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { useSelector } from 'react-redux';
|
||||
import SequenceExamWrapper from '@edx/frontend-lib-special-exams';
|
||||
import { PluginSlot } from '@openedx/frontend-plugin-framework';
|
||||
import { useToggle } from '@openedx/paragon';
|
||||
|
||||
import PageLoading from '@src/generic/PageLoading';
|
||||
import { useModel } from '@src/generic/model-store';
|
||||
import { useSequenceBannerTextAlert, useSequenceEntranceExamAlert } from '@src/alerts/sequence-alerts/hooks';
|
||||
import SequenceContainerSlot from '../../../plugin-slots/SequenceContainerSlot';
|
||||
|
||||
import { getCoursewareOutlineSidebarSettings } from '../../data/selectors';
|
||||
import CourseLicense from '../course-license';
|
||||
import Sidebar from '../sidebar/Sidebar';
|
||||
import NewSidebar from '../new-sidebar/Sidebar';
|
||||
@@ -36,6 +38,7 @@ const Sequence = ({
|
||||
previousSequenceHandler,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const [isOpen, open, close] = useToggle();
|
||||
const {
|
||||
canAccessProctoredExams,
|
||||
license,
|
||||
@@ -49,7 +52,7 @@ const Sequence = ({
|
||||
const unit = useModel('units', unitId);
|
||||
const sequenceStatus = useSelector(state => state.courseware.sequenceStatus);
|
||||
const sequenceMightBeUnit = useSelector(state => state.courseware.sequenceMightBeUnit);
|
||||
|
||||
const { enableNavigationSidebar: isEnabledOutlineSidebar } = useSelector(getCoursewareOutlineSidebarSettings);
|
||||
const handleNext = () => {
|
||||
const nextIndex = sequence.unitIds.indexOf(unitId) + 1;
|
||||
const newUnitId = sequence.unitIds[nextIndex];
|
||||
@@ -144,33 +147,57 @@ const Sequence = ({
|
||||
|
||||
const gated = sequence && sequence.gatedContent !== undefined && sequence.gatedContent.gated;
|
||||
|
||||
const renderUnitNavigation = (isAtTop) => (
|
||||
<UnitNavigation
|
||||
sequenceId={sequenceId}
|
||||
unitId={unitId}
|
||||
isAtTop={isAtTop}
|
||||
onClickPrevious={() => {
|
||||
logEvent('edx.ui.lms.sequence.previous_selected', 'bottom');
|
||||
handlePrevious();
|
||||
}}
|
||||
onClickNext={() => {
|
||||
logEvent('edx.ui.lms.sequence.next_selected', 'bottom');
|
||||
handleNext();
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
const defaultContent = (
|
||||
<>
|
||||
<div className="sequence-container d-inline-flex flex-row w-100">
|
||||
<CourseOutlineTrigger />
|
||||
<CourseOutlineTray />
|
||||
<div className="sequence w-100">
|
||||
<div className="sequence-navigation-container">
|
||||
<SequenceNavigation
|
||||
sequenceId={sequenceId}
|
||||
unitId={unitId}
|
||||
className="mb-4"
|
||||
nextHandler={() => {
|
||||
logEvent('edx.ui.lms.sequence.next_selected', 'top');
|
||||
handleNext();
|
||||
}}
|
||||
onNavigate={(destinationUnitId) => {
|
||||
logEvent('edx.ui.lms.sequence.tab_selected', 'top', destinationUnitId);
|
||||
handleNavigate(destinationUnitId);
|
||||
}}
|
||||
previousHandler={() => {
|
||||
logEvent('edx.ui.lms.sequence.previous_selected', 'top');
|
||||
handlePrevious();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{!isEnabledOutlineSidebar && (
|
||||
<div className="sequence-navigation-container">
|
||||
<SequenceNavigation
|
||||
sequenceId={sequenceId}
|
||||
unitId={unitId}
|
||||
nextHandler={() => {
|
||||
logEvent('edx.ui.lms.sequence.next_selected', 'top');
|
||||
handleNext();
|
||||
}}
|
||||
onNavigate={(destinationUnitId) => {
|
||||
logEvent('edx.ui.lms.sequence.tab_selected', 'top', destinationUnitId);
|
||||
handleNavigate(destinationUnitId);
|
||||
}}
|
||||
previousHandler={() => {
|
||||
logEvent('edx.ui.lms.sequence.previous_selected', 'top');
|
||||
handlePrevious();
|
||||
}}
|
||||
{...{
|
||||
nextSequenceHandler,
|
||||
handleNavigate,
|
||||
isOpen,
|
||||
open,
|
||||
close,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="unit-container flex-grow-1">
|
||||
<div className="unit-container flex-grow-1 pt-4">
|
||||
<SequenceContent
|
||||
courseId={courseId}
|
||||
gated={gated}
|
||||
@@ -178,31 +205,12 @@ const Sequence = ({
|
||||
unitId={unitId}
|
||||
unitLoadedHandler={handleUnitLoaded}
|
||||
/>
|
||||
{unitHasLoaded && (
|
||||
<UnitNavigation
|
||||
sequenceId={sequenceId}
|
||||
unitId={unitId}
|
||||
onClickPrevious={() => {
|
||||
logEvent('edx.ui.lms.sequence.previous_selected', 'bottom');
|
||||
handlePrevious();
|
||||
}}
|
||||
onClickNext={() => {
|
||||
logEvent('edx.ui.lms.sequence.next_selected', 'bottom');
|
||||
handleNext();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{unitHasLoaded && renderUnitNavigation(false)}
|
||||
</div>
|
||||
</div>
|
||||
{isNewDiscussionSidebarViewEnabled ? <NewSidebar /> : <Sidebar />}
|
||||
</div>
|
||||
<PluginSlot
|
||||
id="sequence_container_plugin"
|
||||
pluginProps={{
|
||||
courseId,
|
||||
unitId,
|
||||
}}
|
||||
/>
|
||||
<SequenceContainerSlot courseId={courseId} unitId={unitId} />
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -216,6 +224,7 @@ const Sequence = ({
|
||||
originalUserIsStaff={originalUserIsStaff}
|
||||
canAccessProctoredExams={canAccessProctoredExams}
|
||||
>
|
||||
{isEnabledOutlineSidebar && renderUnitNavigation(true)}
|
||||
{defaultContent}
|
||||
</SequenceExamWrapper>
|
||||
<CourseLicense license={license || undefined} />
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Factory } from 'rosie';
|
||||
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||
@@ -25,6 +24,7 @@ describe('Sequence', () => {
|
||||
{ type: 'vertical' },
|
||||
{ courseId: courseMetadata.id },
|
||||
));
|
||||
const enableNavigationSidebar = { enable_navigation_sidebar: false };
|
||||
|
||||
beforeAll(async () => {
|
||||
const store = await initializeTestStore({ courseMetadata, unitBlocks });
|
||||
@@ -92,7 +92,11 @@ describe('Sequence', () => {
|
||||
{ courseId: courseMetadata.id, unitBlocks, sequenceBlock: sequenceBlocks[0] },
|
||||
)];
|
||||
const testStore = await initializeTestStore({
|
||||
courseMetadata, unitBlocks, sequenceBlocks, sequenceMetadata,
|
||||
courseMetadata,
|
||||
unitBlocks,
|
||||
sequenceBlocks,
|
||||
sequenceMetadata,
|
||||
enableNavigationSidebar: { enable_navigation_sidebar: true },
|
||||
}, false);
|
||||
const { container } = render(
|
||||
<SidebarWrapper overrideData={{ sequenceId: sequenceBlocks[0].id }} />,
|
||||
@@ -102,8 +106,8 @@ describe('Sequence', () => {
|
||||
await waitFor(() => expect(screen.queryByText('Loading locked content messaging...')).toBeInTheDocument());
|
||||
// `Previous`, `Prerequisite` and `Close Tray` buttons.
|
||||
expect(screen.getAllByRole('button').length).toEqual(3);
|
||||
// `Active` and `Next` buttons.
|
||||
expect(screen.getAllByRole('link').length).toEqual(2);
|
||||
// `Next` button.
|
||||
expect(screen.getAllByRole('link').length).toEqual(1);
|
||||
|
||||
expect(screen.getByText('Content Locked')).toBeInTheDocument();
|
||||
const unitContainer = container.querySelector('.unit-container');
|
||||
@@ -125,7 +129,7 @@ describe('Sequence', () => {
|
||||
{ courseId: courseMetadata.id, unitBlocks, sequenceBlock: sequenceBlocks[0] },
|
||||
)];
|
||||
const testStore = await initializeTestStore({
|
||||
courseMetadata, unitBlocks, sequenceBlocks, sequenceMetadata,
|
||||
courseMetadata, unitBlocks, sequenceBlocks, sequenceMetadata, enableNavigationSidebar,
|
||||
}, false);
|
||||
render(
|
||||
<Sequence {...mockData} {...{ sequenceId: sequenceBlocks[0].id }} />,
|
||||
@@ -156,14 +160,16 @@ describe('Sequence', () => {
|
||||
expect(await screen.findByText('Loading learning sequence...')).toBeInTheDocument();
|
||||
// `Previous`, `Prerequisite` and `Close Tray` buttons.
|
||||
expect(screen.getAllByRole('button')).toHaveLength(3);
|
||||
// Renders `Next` button plus one button for each unit.
|
||||
expect(screen.getAllByRole('link')).toHaveLength(1 + unitBlocks.length);
|
||||
// Renders `Next` button.
|
||||
expect(screen.getAllByRole('link')).toHaveLength(1);
|
||||
|
||||
loadUnit();
|
||||
await waitFor(() => expect(screen.queryByText('Loading learning sequence...')).not.toBeInTheDocument());
|
||||
// At this point there will be 2 `Previous` and 2 `Next` buttons.
|
||||
expect(screen.getAllByRole('button', { name: /previous/i }).length).toEqual(2);
|
||||
expect(screen.getAllByRole('link', { name: /next/i }).length).toEqual(2);
|
||||
// Renders two `Next` buttons for top and bottom unit navigations.
|
||||
expect(screen.getAllByRole('link')).toHaveLength(2);
|
||||
});
|
||||
|
||||
describe('sequence and unit navigation buttons', () => {
|
||||
@@ -179,7 +185,9 @@ describe('Sequence', () => {
|
||||
)];
|
||||
|
||||
beforeAll(async () => {
|
||||
testStore = await initializeTestStore({ courseMetadata, unitBlocks, sequenceBlocks }, false);
|
||||
testStore = await initializeTestStore({
|
||||
courseMetadata, unitBlocks, sequenceBlocks, enableNavigationSidebar,
|
||||
}, false);
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -340,7 +348,11 @@ describe('Sequence', () => {
|
||||
{ courseId: courseMetadata.id, unitBlocks: block.children.length ? unitBlocks : [], sequenceBlock: block },
|
||||
));
|
||||
const innerTestStore = await initializeTestStore({
|
||||
courseMetadata, unitBlocks, sequenceBlocks: testSequenceBlocks, sequenceMetadata: testSequenceMetadata,
|
||||
courseMetadata,
|
||||
unitBlocks,
|
||||
sequenceBlocks: testSequenceBlocks,
|
||||
sequenceMetadata: testSequenceMetadata,
|
||||
enableNavigationSidebar,
|
||||
}, false);
|
||||
const testData = {
|
||||
...mockData,
|
||||
|
||||
@@ -2,6 +2,7 @@ import React, { Suspense } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { PluginSlot } from '@openedx/frontend-plugin-framework';
|
||||
|
||||
import { useModel } from '@src/generic/model-store';
|
||||
import PageLoading from '@src/generic/PageLoading';
|
||||
@@ -24,19 +25,24 @@ const UnitSuspense = ({
|
||||
meta.contentTypeGatingEnabled && unit.containsContentTypeGatedContent
|
||||
);
|
||||
|
||||
const suspenseComponent = (message, Component) => (
|
||||
<Suspense fallback={<PageLoading srMessage={formatMessage(message)} />}>
|
||||
<Component courseId={courseId} />
|
||||
</Suspense>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
{shouldDisplayContentGating && (
|
||||
suspenseComponent(messages.loadingLockedContent, LockPaywall)
|
||||
<Suspense fallback={<PageLoading srMessage={formatMessage(messages.loadingLockedContent)} />}>
|
||||
<PluginSlot
|
||||
id="gated_unit_content_message_slot"
|
||||
pluginProps={{
|
||||
courseId,
|
||||
}}
|
||||
>
|
||||
<LockPaywall courseId={courseId} />
|
||||
</PluginSlot>
|
||||
</Suspense>
|
||||
)}
|
||||
{shouldDisplayHonorCode && (
|
||||
suspenseComponent(messages.loadingHonorCode, HonorCode)
|
||||
<Suspense fallback={<PageLoading srMessage={formatMessage(messages.loadingHonorCode)} />}>
|
||||
<HonorCode courseId={courseId} />
|
||||
</Suspense>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -64,7 +64,7 @@ describe('UnitSuspense component', () => {
|
||||
describe('output', () => {
|
||||
describe('LockPaywall', () => {
|
||||
const testNoPaywall = () => {
|
||||
it('does not display LockPaywal', () => {
|
||||
it('does not display LockPaywall', () => {
|
||||
el = shallow(<UnitSuspense {...props} />);
|
||||
expect(el.instance.findByType(LockPaywall).length).toEqual(0);
|
||||
});
|
||||
@@ -79,8 +79,9 @@ describe('UnitSuspense component', () => {
|
||||
it('displays LockPaywall in Suspense wrapper with PageLoading fallback', () => {
|
||||
el = shallow(<UnitSuspense {...props} />);
|
||||
const [component] = el.instance.findByType(LockPaywall);
|
||||
expect(component.parent.type).toEqual('Suspense');
|
||||
expect(component.parent.props.fallback)
|
||||
expect(component.parent.type).toEqual('PluginSlot');
|
||||
expect(component.parent.parent.type).toEqual('Suspense');
|
||||
expect(component.parent.parent.props.fallback)
|
||||
.toEqual(<PageLoading srMessage={formatMessage(messages.loadingLockedContent)} />);
|
||||
expect(component.props.courseId).toEqual(props.courseId);
|
||||
});
|
||||
|
||||
@@ -28,14 +28,10 @@ exports[`Unit component output snapshot: not bookmarked, do not show content 1`]
|
||||
>
|
||||
unit-title
|
||||
</h3>
|
||||
<PluginSlot
|
||||
id="unit_title_plugin"
|
||||
pluginProps={
|
||||
Object {
|
||||
"courseId": "test-course-id",
|
||||
"unitId": "test-props-id",
|
||||
}
|
||||
}
|
||||
<UnitTitleSlot
|
||||
courseId="test-course-id"
|
||||
unitId="test-props-id"
|
||||
unitTitle="unit-title"
|
||||
/>
|
||||
</div>
|
||||
<h2
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||
import React from 'react';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
@@ -50,12 +51,6 @@ const useIFrameBehavior = ({
|
||||
if (type === messageTypes.resize) {
|
||||
setIframeHeight(payload.height);
|
||||
|
||||
// We observe exit from the video xblock fullscreen mode
|
||||
// and scroll to the previously saved scroll position
|
||||
if (windowTopOffset !== null) {
|
||||
window.scrollTo(0, Number(windowTopOffset));
|
||||
}
|
||||
|
||||
if (!hasLoaded && iframeHeight === 0 && payload.height > 0) {
|
||||
setHasLoaded(true);
|
||||
if (onLoaded) {
|
||||
@@ -63,6 +58,12 @@ const useIFrameBehavior = ({
|
||||
}
|
||||
}
|
||||
} else if (type === messageTypes.videoFullScreen) {
|
||||
// We observe exit from the video xblock fullscreen mode
|
||||
// and scroll to the previously saved scroll position
|
||||
if (!payload.open && windowTopOffset !== null) {
|
||||
window.scrollTo(0, Number(windowTopOffset));
|
||||
}
|
||||
|
||||
// We listen for this message from LMS to know when we need to
|
||||
// save or reset scroll position on toggle video xblock fullscreen mode
|
||||
setWindowTopOffset(payload.open ? window.scrollY : null);
|
||||
@@ -94,6 +95,10 @@ const useIFrameBehavior = ({
|
||||
const handleIFrameLoad = () => {
|
||||
if (!hasLoaded) {
|
||||
setShowError(true);
|
||||
sendTrackEvent('edx.bi.error.learning.iframe_load_failed', {
|
||||
iframeUrl,
|
||||
unitId: id,
|
||||
});
|
||||
logError('Unit iframe failed to load. Server possibly returned 4xx or 5xx response.', {
|
||||
iframeUrl,
|
||||
});
|
||||
@@ -105,6 +110,11 @@ const useIFrameBehavior = ({
|
||||
};
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
setIframeHeight(0);
|
||||
setHasLoaded(false);
|
||||
}, [iframeUrl]);
|
||||
|
||||
return {
|
||||
iframeHeight,
|
||||
handleIFrameLoad,
|
||||
|
||||
@@ -5,6 +5,7 @@ import { getEffects, mockUseKeyedState } from '@edx/react-unit-test-utils';
|
||||
import { logError } from '@edx/frontend-platform/logging';
|
||||
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||
import { fetchCourse } from '@src/courseware/data';
|
||||
import { processEvent } from '@src/course-home/data/thunks';
|
||||
import { useEventListener } from '@src/generic/hooks';
|
||||
@@ -17,6 +18,8 @@ jest.mock('@edx/frontend-platform', () => ({
|
||||
getConfig: jest.fn(),
|
||||
}));
|
||||
|
||||
jest.mock('@edx/frontend-platform/analytics');
|
||||
|
||||
jest.mock('react', () => ({
|
||||
...jest.requireActual('react'),
|
||||
useEffect: jest.fn(),
|
||||
@@ -154,6 +157,9 @@ describe('useIFrameBehavior hook', () => {
|
||||
const resizeMessage = (height = 23) => ({
|
||||
data: { type: messageTypes.resize, payload: { height } },
|
||||
});
|
||||
const videoFullScreenMessage = (open = false) => ({
|
||||
data: { type: messageTypes.videoFullScreen, payload: { open } },
|
||||
});
|
||||
const testSetIFrameHeight = (height = 23) => {
|
||||
const { cb } = useEventListener.mock.calls[0][1];
|
||||
cb(resizeMessage(height));
|
||||
@@ -209,7 +215,7 @@ describe('useIFrameBehavior hook', () => {
|
||||
state.mockVals({ ...defaultStateVals, windowTopOffset });
|
||||
hook = useIFrameBehavior(props);
|
||||
const { cb } = useEventListener.mock.calls[0][1];
|
||||
cb(resizeMessage());
|
||||
cb(videoFullScreenMessage());
|
||||
expect(window.scrollTo).toHaveBeenCalledWith(0, windowTopOffset);
|
||||
});
|
||||
it('does not scroll if towverticalp offset is not set', () => {
|
||||
@@ -268,6 +274,16 @@ describe('useIFrameBehavior hook', () => {
|
||||
expect(state.setState.showError).toHaveBeenCalledWith(true);
|
||||
expect(logError).toHaveBeenCalled();
|
||||
});
|
||||
it('sends track event if has not loaded', () => {
|
||||
hook = useIFrameBehavior(props);
|
||||
hook.handleIFrameLoad();
|
||||
const eventName = 'edx.bi.error.learning.iframe_load_failed';
|
||||
const eventProperties = {
|
||||
unitId: props.id,
|
||||
iframeUrl: props.iframeUrl,
|
||||
};
|
||||
expect(sendTrackEvent).toHaveBeenCalledWith(eventName, eventProperties);
|
||||
});
|
||||
it('does not set/log errors if loaded', () => {
|
||||
state.mockVals({ ...defaultStateVals, hasLoaded: true });
|
||||
hook = useIFrameBehavior(props);
|
||||
@@ -275,6 +291,12 @@ describe('useIFrameBehavior hook', () => {
|
||||
expect(state.setState.showError).not.toHaveBeenCalled();
|
||||
expect(logError).not.toHaveBeenCalled();
|
||||
});
|
||||
it('does not send track event if loaded', () => {
|
||||
state.mockVals({ ...defaultStateVals, hasLoaded: true });
|
||||
hook = useIFrameBehavior(props);
|
||||
hook.handleIFrameLoad();
|
||||
expect(sendTrackEvent).not.toHaveBeenCalled();
|
||||
});
|
||||
it('registers an event handler to process fetchCourse events.', () => {
|
||||
hook = useIFrameBehavior(props);
|
||||
hook.handleIFrameLoad();
|
||||
|
||||
@@ -7,7 +7,6 @@ import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { useModel } from '@src/generic/model-store';
|
||||
import { usePluginsCallback } from '@src/generic/plugin-store';
|
||||
|
||||
import { PluginSlot } from '@openedx/frontend-plugin-framework';
|
||||
import BookmarkButton from '../../bookmark/BookmarkButton';
|
||||
import messages from '../messages';
|
||||
import ContentIFrame from './ContentIFrame';
|
||||
@@ -15,6 +14,7 @@ import UnitSuspense from './UnitSuspense';
|
||||
import { modelKeys, views } from './constants';
|
||||
import { useExamAccess, useShouldDisplayHonorCode } from './hooks';
|
||||
import { getIFrameUrl } from './urls';
|
||||
import UnitTitleSlot from '../../../../plugin-slots/UnitTitleSlot';
|
||||
|
||||
const Unit = ({
|
||||
courseId,
|
||||
@@ -43,13 +43,7 @@ const Unit = ({
|
||||
<div className="unit">
|
||||
<div className="mb-0">
|
||||
<h3 className="h3">{unit.title}</h3>
|
||||
<PluginSlot
|
||||
id="unit_title_plugin"
|
||||
pluginProps={{
|
||||
courseId,
|
||||
unitId: id,
|
||||
}}
|
||||
/>
|
||||
<UnitTitleSlot courseId={courseId} unitId={id} unitTitle={unit.title} />
|
||||
</div>
|
||||
<h2 className="sr-only">{formatMessage(messages.headerPlaceholder)}</h2>
|
||||
<BookmarkButton
|
||||
|
||||
@@ -10,8 +10,9 @@ import {
|
||||
isRtl,
|
||||
getLocale,
|
||||
} from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { PluginSlot } from '@openedx/frontend-plugin-framework';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { GetCourseExitNavigation } from '../../course-exit';
|
||||
import UnitButton from './UnitButton';
|
||||
import SequenceNavigationTabs from './SequenceNavigationTabs';
|
||||
@@ -29,6 +30,11 @@ const SequenceNavigation = ({
|
||||
onNavigate,
|
||||
nextHandler,
|
||||
previousHandler,
|
||||
nextSequenceHandler,
|
||||
handleNavigate,
|
||||
isOpen,
|
||||
open,
|
||||
close,
|
||||
}) => {
|
||||
const sequence = useModel('sequences', sequenceId);
|
||||
const {
|
||||
@@ -95,17 +101,37 @@ const SequenceNavigation = ({
|
||||
const nextArrow = isRtl(getLocale()) ? ChevronLeft : ChevronRight;
|
||||
|
||||
return navigationDisabledNextSequence || (
|
||||
<Button
|
||||
variant="link"
|
||||
className="next-btn"
|
||||
onClick={nextHandler}
|
||||
disabled={disabled}
|
||||
iconAfter={nextArrow}
|
||||
as={disabled ? undefined : Link}
|
||||
to={disabled ? undefined : nextLink}
|
||||
<PluginSlot
|
||||
id="next_button_slot"
|
||||
pluginProps={{
|
||||
courseId,
|
||||
disabled,
|
||||
buttonText,
|
||||
nextArrow,
|
||||
nextLink,
|
||||
shouldDisplayNotificationTriggerInSequence,
|
||||
sequenceId,
|
||||
unitId,
|
||||
nextSequenceHandler,
|
||||
handleNavigate,
|
||||
isOpen,
|
||||
open,
|
||||
close,
|
||||
linkComponent: Link,
|
||||
}}
|
||||
>
|
||||
{shouldDisplayNotificationTriggerInSequence ? null : buttonText}
|
||||
</Button>
|
||||
<Button
|
||||
variant="link"
|
||||
className="next-btn"
|
||||
onClick={nextHandler}
|
||||
disabled={disabled}
|
||||
iconAfter={nextArrow}
|
||||
as={disabled ? undefined : Link}
|
||||
to={disabled ? undefined : nextLink}
|
||||
>
|
||||
{shouldDisplayNotificationTriggerInSequence ? null : buttonText}
|
||||
</Button>
|
||||
</PluginSlot>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -126,11 +152,21 @@ SequenceNavigation.propTypes = {
|
||||
onNavigate: PropTypes.func.isRequired,
|
||||
nextHandler: PropTypes.func.isRequired,
|
||||
previousHandler: PropTypes.func.isRequired,
|
||||
close: PropTypes.func,
|
||||
open: PropTypes.func,
|
||||
isOpen: PropTypes.bool,
|
||||
handleNavigate: PropTypes.func,
|
||||
nextSequenceHandler: PropTypes.func,
|
||||
};
|
||||
|
||||
SequenceNavigation.defaultProps = {
|
||||
className: null,
|
||||
unitId: null,
|
||||
close: null,
|
||||
open: null,
|
||||
isOpen: false,
|
||||
handleNavigate: null,
|
||||
nextSequenceHandler: null,
|
||||
};
|
||||
|
||||
export default injectIntl(SequenceNavigation);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import { Link } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button } from '@openedx/paragon';
|
||||
@@ -21,6 +21,7 @@ const UnitNavigation = ({
|
||||
unitId,
|
||||
onClickPrevious,
|
||||
onClickNext,
|
||||
isAtTop,
|
||||
}) => {
|
||||
const {
|
||||
isFirstUnit, isLastUnit, nextLink, previousLink,
|
||||
@@ -33,7 +34,7 @@ const UnitNavigation = ({
|
||||
return (
|
||||
<Button
|
||||
variant="outline-secondary"
|
||||
className="previous-button mr-2 d-flex align-items-center justify-content-center"
|
||||
className="previous-button mr-sm-2 d-flex align-items-center justify-content-center"
|
||||
disabled={disabled}
|
||||
onClick={onClickPrevious}
|
||||
as={disabled ? undefined : Link}
|
||||
@@ -68,7 +69,7 @@ const UnitNavigation = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="unit-navigation d-flex">
|
||||
<div className={classNames('unit-navigation d-flex', { 'top-unit-navigation mb-3 w-100': isAtTop })}>
|
||||
{renderPreviousButton()}
|
||||
{renderNextButton()}
|
||||
</div>
|
||||
@@ -81,10 +82,12 @@ UnitNavigation.propTypes = {
|
||||
unitId: PropTypes.string,
|
||||
onClickPrevious: PropTypes.func.isRequired,
|
||||
onClickNext: PropTypes.func.isRequired,
|
||||
isAtTop: PropTypes.bool,
|
||||
};
|
||||
|
||||
UnitNavigation.defaultProps = {
|
||||
unitId: null,
|
||||
isAtTop: false,
|
||||
};
|
||||
|
||||
export default injectIntl(UnitNavigation);
|
||||
|
||||
@@ -28,8 +28,8 @@ const SidebarProvider = ({
|
||||
const { alwaysOpenAuxiliarySidebar } = useSelector(getCoursewareOutlineSidebarSettings);
|
||||
const isInitiallySidebarOpen = shouldDisplaySidebarOpen || query.get('sidebar') === 'true';
|
||||
|
||||
let initialSidebar = null;
|
||||
if (isInitiallySidebarOpen && alwaysOpenAuxiliarySidebar) {
|
||||
let initialSidebar = shouldDisplayFullScreen ? getLocalStorage(`sidebar.${courseId}`) : null;
|
||||
if (!shouldDisplayFullScreen && isInitiallySidebarOpen && alwaysOpenAuxiliarySidebar) {
|
||||
initialSidebar = isUnitHasDiscussionTopics
|
||||
? SIDEBARS[discussionsSidebar.ID].ID
|
||||
: verifiedMode && SIDEBARS[notificationsSidebar.ID].ID;
|
||||
@@ -57,7 +57,9 @@ const SidebarProvider = ({
|
||||
|
||||
const toggleSidebar = useCallback((sidebarId) => {
|
||||
// Switch to new sidebar or hide the current sidebar
|
||||
setCurrentSidebar(sidebarId === currentSidebar ? null : sidebarId);
|
||||
const newSidebar = sidebarId === currentSidebar ? null : sidebarId;
|
||||
setCurrentSidebar(newSidebar);
|
||||
setLocalStorage(`sidebar.${courseId}`, newSidebar);
|
||||
}, [currentSidebar]);
|
||||
|
||||
const contextValue = useMemo(() => ({
|
||||
|
||||
@@ -20,7 +20,7 @@ const SidebarTriggers = () => {
|
||||
return (
|
||||
<div
|
||||
className={classNames({ 'ml-1': !isMobileView, 'border-primary-700 sidebar-active': isActive })}
|
||||
style={{ borderBottom: isActive ? '2px solid' : null }}
|
||||
style={{ borderBottom: '2px solid', borderColor: isActive ? 'inherit' : 'transparent' }}
|
||||
key={sidebarId}
|
||||
>
|
||||
<Trigger onClick={() => toggleSidebar(sidebarId)} key={sidebarId} />
|
||||
|
||||
@@ -38,11 +38,11 @@ const SidebarBase = ({
|
||||
<section
|
||||
className={classNames('ml-0 border border-light-400 rounded-sm h-auto align-top zindex-0', {
|
||||
'bg-white m-0 border-0 fixed-top vh-100 rounded-0': shouldDisplayFullScreen,
|
||||
'min-vh-100': !shouldDisplayFullScreen,
|
||||
'align-self-start': !shouldDisplayFullScreen,
|
||||
'd-none': currentSidebar !== sidebarId,
|
||||
}, className)}
|
||||
data-testid={`sidebar-${sidebarId}`}
|
||||
style={{ minWidth: shouldDisplayFullScreen ? '100%' : width }}
|
||||
style={{ width: shouldDisplayFullScreen ? '100%' : width }}
|
||||
aria-label={ariaLabel}
|
||||
id="course-sidebar"
|
||||
>
|
||||
@@ -98,7 +98,7 @@ SidebarBase.propTypes = {
|
||||
};
|
||||
|
||||
SidebarBase.defaultProps = {
|
||||
width: '410px',
|
||||
width: '31rem',
|
||||
showTitleBar: true,
|
||||
};
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ const CourseOutlineTray = ({ intl }) => {
|
||||
};
|
||||
|
||||
const sidebarHeading = (
|
||||
<div className="outline-sidebar-heading-wrapper sticky d-flex justify-content-between align-items-center bg-light-200 p-2.5 pl-4">
|
||||
<div className="outline-sidebar-heading-wrapper sticky d-flex justify-content-between align-self-start align-items-center bg-light-200 p-2.5 pl-4">
|
||||
{isDisplaySequenceLevel && backButtonTitle ? (
|
||||
<Button
|
||||
variant="link"
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
.outline-sidebar-heading-wrapper {
|
||||
border: 1px solid #d7d3d1;
|
||||
align-self: flex-start;
|
||||
|
||||
&.sticky {
|
||||
position: sticky;
|
||||
|
||||
@@ -25,8 +25,8 @@ const CourseOutlineTrigger = ({ intl, isMobileView }) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classNames('outline-sidebar-heading-wrapper bg-light-200 collapsed', {
|
||||
'flex-shrink-0 mr-4 p-2.5 sticky': isDisplayForDesktopView,
|
||||
<div className={classNames('outline-sidebar-heading-wrapper bg-light-200 collapsed align-self-start', {
|
||||
'flex-shrink-0 mr-4 p-2.5': isDisplayForDesktopView,
|
||||
'p-0': isDisplayForMobileView,
|
||||
})}
|
||||
>
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
CheckCircle as CheckCircleIcon,
|
||||
LmsCompletionSolid as LmsCompletionSolidIcon,
|
||||
} from '@openedx/paragon/icons';
|
||||
|
||||
import { DashedCircleIcon } from '../icons';
|
||||
|
||||
const CompletionIcon = ({ completionStat: { completed = 0, total = 0 } }) => {
|
||||
const percentage = total !== 0 ? Math.min((completed / total) * 100, 100) : 0;
|
||||
const remainder = 100 - percentage;
|
||||
|
||||
switch (true) {
|
||||
case !completed:
|
||||
return <LmsCompletionSolidIcon className="text-gray-300" data-testid="completion-solid-icon" />;
|
||||
case completed === total:
|
||||
return <CheckCircleIcon className="text-success" data-testid="check-circle-icon" />;
|
||||
default:
|
||||
return <DashedCircleIcon percentage={percentage} remainder={remainder} data-testid="dashed-circle-icon" />;
|
||||
}
|
||||
};
|
||||
|
||||
CompletionIcon.propTypes = {
|
||||
completionStat: PropTypes.shape({
|
||||
completed: PropTypes.number,
|
||||
total: PropTypes.number,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
export default CompletionIcon;
|
||||
@@ -0,0 +1,23 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
|
||||
import CompletionIcon from './CompletionIcon';
|
||||
|
||||
describe('CompletionIcon', () => {
|
||||
it('renders check circle icon when completion is equal to total', () => {
|
||||
const completionStat = { completed: 5, total: 5 };
|
||||
render(<CompletionIcon completionStat={completionStat} />);
|
||||
expect(screen.getByTestId('check-circle-icon')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders dashed circle icon when completion is between 0 and total', () => {
|
||||
const completionStat = { completed: 2, total: 5 };
|
||||
render(<CompletionIcon completionStat={completionStat} />);
|
||||
expect(screen.getByTestId('dashed-circle-icon')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders completion solid icon when completion is 0', () => {
|
||||
const completionStat = { completed: 0, total: 5 };
|
||||
render(<CompletionIcon completionStat={completionStat} />);
|
||||
expect(screen.getByTestId('completion-solid-icon')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -3,14 +3,11 @@ import classNames from 'classnames';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Button, Icon } from '@openedx/paragon';
|
||||
import {
|
||||
CheckCircle as CheckCircleIcon,
|
||||
ChevronRight as ChevronRightIcon,
|
||||
LmsCompletionSolid as LmsCompletionSolidIcon,
|
||||
} from '@openedx/paragon/icons';
|
||||
import { ChevronRight as ChevronRightIcon } from '@openedx/paragon/icons';
|
||||
|
||||
import courseOutlineMessages from '@src/course-home/outline-tab/messages';
|
||||
import { getSequenceId } from '@src/courseware/data/selectors';
|
||||
import CompletionIcon from './CompletionIcon';
|
||||
|
||||
const SidebarSection = ({ intl, section, handleSelectSection }) => {
|
||||
const {
|
||||
@@ -18,6 +15,7 @@ const SidebarSection = ({ intl, section, handleSelectSection }) => {
|
||||
complete,
|
||||
title,
|
||||
sequenceIds,
|
||||
completionStat,
|
||||
} = section;
|
||||
|
||||
const activeSequenceId = useSelector(getSequenceId);
|
||||
@@ -26,7 +24,7 @@ const SidebarSection = ({ intl, section, handleSelectSection }) => {
|
||||
const sectionTitle = (
|
||||
<>
|
||||
<div className="col-auto p-0">
|
||||
{complete ? <CheckCircleIcon className="text-success" /> : <LmsCompletionSolidIcon className="text-gray-300" />}
|
||||
<CompletionIcon completionStat={completionStat} />
|
||||
</div>
|
||||
<div className="col-10 ml-3 p-0 flex-grow-1 text-dark-500 text-left text-break">
|
||||
{title}
|
||||
@@ -63,6 +61,10 @@ SidebarSection.propTypes = {
|
||||
id: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
sequenceIds: PropTypes.arrayOf(PropTypes.string),
|
||||
completionStat: PropTypes.shape({
|
||||
completed: PropTypes.number,
|
||||
total: PropTypes.number,
|
||||
}),
|
||||
}).isRequired,
|
||||
handleSelectSection: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { render } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import { AppProvider } from '@edx/frontend-platform/react';
|
||||
@@ -40,7 +40,7 @@ describe('<SidebarSection />', () => {
|
||||
const { getByText, container } = render(<RootWrapper />);
|
||||
|
||||
expect(getByText(section.title)).toBeInTheDocument();
|
||||
expect(screen.getByText(`, ${courseOutlineMessages.incompleteSection.defaultMessage}`)).toBeInTheDocument();
|
||||
expect(getByText(`, ${courseOutlineMessages.incompleteSection.defaultMessage}`)).toBeInTheDocument();
|
||||
expect(container.querySelector('.text-success')).not.toBeInTheDocument();
|
||||
|
||||
const button = getByText(section.title);
|
||||
@@ -51,13 +51,13 @@ describe('<SidebarSection />', () => {
|
||||
|
||||
it('renders correctly when section is complete', async () => {
|
||||
await initTestStore();
|
||||
const { getByText, container } = render(
|
||||
<RootWrapper section={{ ...section, complete: true }} />,
|
||||
const { getByText, getByTestId } = render(
|
||||
<RootWrapper section={{ ...section, completionStat: { completed: 4, total: 4 }, complete: true }} />,
|
||||
);
|
||||
|
||||
expect(getByText(section.title)).toBeInTheDocument();
|
||||
expect(screen.getByText(`, ${courseOutlineMessages.completedSection.defaultMessage}`)).toBeInTheDocument();
|
||||
expect(container.querySelector('.text-success')).toBeInTheDocument();
|
||||
expect(getByText(`, ${courseOutlineMessages.completedSection.defaultMessage}`)).toBeInTheDocument();
|
||||
expect(getByTestId('check-circle-icon')).toBeInTheDocument();
|
||||
|
||||
const button = getByText(section.title);
|
||||
userEvent.click(button);
|
||||
|
||||
@@ -4,13 +4,10 @@ import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Collapsible } from '@openedx/paragon';
|
||||
import {
|
||||
CheckCircle as CheckCircleIcon,
|
||||
LmsCompletionSolid as LmsCompletionSolidIcon,
|
||||
} from '@openedx/paragon/icons';
|
||||
|
||||
import courseOutlineMessages from '@src/course-home/outline-tab/messages';
|
||||
import { getCourseOutline, getSequenceId } from '@src/courseware/data/selectors';
|
||||
import CompletionIcon from './CompletionIcon';
|
||||
import SidebarUnit from './SidebarUnit';
|
||||
import { UNIT_ICON_TYPES } from './UnitIcon';
|
||||
|
||||
@@ -28,6 +25,7 @@ const SidebarSequence = ({
|
||||
specialExamInfo,
|
||||
unitIds,
|
||||
type,
|
||||
completionStat,
|
||||
} = sequence;
|
||||
|
||||
const [open, setOpen] = useState(defaultOpen);
|
||||
@@ -38,7 +36,7 @@ const SidebarSequence = ({
|
||||
const sectionTitle = (
|
||||
<>
|
||||
<div className="col-auto p-0" style={{ fontSize: '1.1rem' }}>
|
||||
{complete ? <CheckCircleIcon className="text-success" /> : <LmsCompletionSolidIcon className="text-gray-300" />}
|
||||
<CompletionIcon completionStat={completionStat} />
|
||||
</div>
|
||||
<div className="col-9 d-flex flex-column flex-grow-1 ml-3 mr-auto p-0 text-left">
|
||||
<span className="align-middle text-dark-500">{title}</span>
|
||||
@@ -92,6 +90,10 @@ SidebarSequence.propTypes = {
|
||||
type: PropTypes.string,
|
||||
specialExamInfo: PropTypes.string,
|
||||
unitIds: PropTypes.arrayOf(PropTypes.string),
|
||||
completionStat: PropTypes.shape({
|
||||
completed: PropTypes.number,
|
||||
total: PropTypes.number,
|
||||
}),
|
||||
}).isRequired,
|
||||
activeUnitId: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
@@ -6,8 +6,8 @@ import { IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import courseOutlineMessages from '@src/course-home/outline-tab/messages';
|
||||
import { initializeMockApp, initializeTestStore } from '@src/setupTest';
|
||||
import SidebarSequence from './SidebarSequence';
|
||||
import messages from '../messages';
|
||||
import SidebarSequence from './SidebarSequence';
|
||||
|
||||
initializeMockApp();
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { sendTrackEvent, sendTrackingLogEvent } from '@edx/frontend-platform/analytics';
|
||||
|
||||
import { checkBlockCompletion } from '@src/courseware/data';
|
||||
import UnitIcon, { UNIT_ICON_TYPES } from './UnitIcon';
|
||||
import { getCourseOutline } from '@src/courseware/data/selectors';
|
||||
import messages from '../messages';
|
||||
import UnitIcon, { UNIT_ICON_TYPES } from './UnitIcon';
|
||||
|
||||
const SidebarUnit = ({
|
||||
id,
|
||||
@@ -25,8 +27,31 @@ const SidebarUnit = ({
|
||||
icon = UNIT_ICON_TYPES.other,
|
||||
} = unit;
|
||||
const dispatch = useDispatch();
|
||||
const { sequences = {} } = useSelector(getCourseOutline);
|
||||
|
||||
const logEvent = (eventName, widgetPlacement) => {
|
||||
const findSequenceByUnitId = (unitId) => Object.values(sequences).find(seq => seq.unitIds.includes(unitId));
|
||||
const activeSequence = findSequenceByUnitId(activeUnitId);
|
||||
const targetSequence = findSequenceByUnitId(id);
|
||||
const payload = {
|
||||
id: activeUnitId,
|
||||
current_tab: activeSequence.unitIds.indexOf(activeUnitId) + 1,
|
||||
tab_count: activeSequence.unitIds.length,
|
||||
target_id: id,
|
||||
target_tab: targetSequence.unitIds.indexOf(id) + 1,
|
||||
widget_placement: widgetPlacement,
|
||||
};
|
||||
|
||||
if (activeSequence.id !== targetSequence.id) {
|
||||
payload.target_tab_count = targetSequence.unitIds.length;
|
||||
}
|
||||
|
||||
sendTrackEvent(eventName, payload);
|
||||
sendTrackingLogEvent(eventName, payload);
|
||||
};
|
||||
|
||||
const handleClick = () => {
|
||||
logEvent('edx.ui.lms.sequence.tab_selected', 'left');
|
||||
dispatch(checkBlockCompletion(courseId, sequenceId, activeUnitId));
|
||||
};
|
||||
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
import { AppProvider } from '@edx/frontend-platform/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import { sendTrackEvent, sendTrackingLogEvent } from '@edx/frontend-platform/analytics';
|
||||
|
||||
import { initializeMockApp, initializeTestStore } from '@src/setupTest';
|
||||
import SidebarUnit from './SidebarUnit';
|
||||
|
||||
jest.mock('@edx/frontend-platform/analytics', () => ({
|
||||
sendTrackEvent: jest.fn(),
|
||||
sendTrackingLogEvent: jest.fn(),
|
||||
}));
|
||||
|
||||
initializeMockApp();
|
||||
|
||||
describe('<SidebarUnit />', () => {
|
||||
@@ -28,11 +35,12 @@ describe('<SidebarUnit />', () => {
|
||||
<MemoryRouter>
|
||||
<SidebarUnit
|
||||
isFirst
|
||||
id="unit1"
|
||||
id={unit.id}
|
||||
courseId="course123"
|
||||
sequenceId={sequenceId}
|
||||
unit={{ ...unit, icon: 'video', isLocked: false }}
|
||||
isActive={false}
|
||||
activeUnitId={unit.id}
|
||||
{...props}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
@@ -78,4 +86,22 @@ describe('<SidebarUnit />', () => {
|
||||
|
||||
expect(screen.getByText(unit.title)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('sends log event correctly when unit is clicked', async () => {
|
||||
await initTestStore();
|
||||
renderWithProvider({ unit: { ...unit } });
|
||||
const logData = {
|
||||
id: unit.id,
|
||||
current_tab: 1,
|
||||
tab_count: 1,
|
||||
target_id: unit.id,
|
||||
target_tab: 1,
|
||||
widget_placement: 'left',
|
||||
};
|
||||
|
||||
userEvent.click(screen.getByText(unit.title));
|
||||
|
||||
expect(sendTrackEvent).toHaveBeenCalledWith('edx.ui.lms.sequence.tab_selected', logData);
|
||||
expect(sendTrackingLogEvent).toHaveBeenCalledWith('edx.ui.lms.sequence.tab_selected', logData);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const DashedCircleIcon = (props) => (
|
||||
<svg
|
||||
width={24}
|
||||
height={24}
|
||||
viewBox="0 0 40 40"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
{...props}
|
||||
>
|
||||
<circle
|
||||
cx="20"
|
||||
cy="20"
|
||||
r="15"
|
||||
stroke="#ccc"
|
||||
strokeWidth="3"
|
||||
strokeDasharray="2.6 2.3"
|
||||
fill="transparent"
|
||||
strokeDashoffset="27"
|
||||
/>
|
||||
<circle
|
||||
cx="20"
|
||||
cy="20"
|
||||
r="15"
|
||||
fill="transparent"
|
||||
stroke="#0d7d4d"
|
||||
strokeWidth="3"
|
||||
strokeDasharray={`${props.percentage} ${props.remainder}`}
|
||||
strokeDashoffset="29"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
DashedCircleIcon.propTypes = {
|
||||
percentage: PropTypes.number.isRequired,
|
||||
remainder: PropTypes.number.isRequired,
|
||||
};
|
||||
|
||||
export default DashedCircleIcon;
|
||||
@@ -0,0 +1,2 @@
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export { default as DashedCircleIcon } from './DashedCircleIcon';
|
||||
@@ -2,8 +2,9 @@ import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import classNames from 'classnames';
|
||||
import { useContext, useEffect, useMemo } from 'react';
|
||||
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||
import { PluginSlot } from '@openedx/frontend-plugin-framework';
|
||||
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
|
||||
import { useModel } from '@src/generic/model-store';
|
||||
import UpgradeNotification from '@src/generic/upgrade-notification/UpgradeNotification';
|
||||
|
||||
import messages from '../../../messages';
|
||||
import SidebarBase from '../../common/SidebarBase';
|
||||
@@ -21,17 +22,11 @@ const NotificationTray = ({ intl }) => {
|
||||
const course = useModel('coursewareMeta', courseId);
|
||||
|
||||
const {
|
||||
accessExpiration,
|
||||
contentTypeGatingEnabled,
|
||||
end,
|
||||
enrollmentEnd,
|
||||
enrollmentMode,
|
||||
enrollmentStart,
|
||||
marketingUrl,
|
||||
offer,
|
||||
start,
|
||||
timeOffsetMillis,
|
||||
userTimezone,
|
||||
verificationStatus,
|
||||
} = course;
|
||||
|
||||
@@ -40,10 +35,10 @@ const NotificationTray = ({ intl }) => {
|
||||
org,
|
||||
verifiedMode,
|
||||
username,
|
||||
isStaff,
|
||||
} = useModel('courseHomeMeta', courseId);
|
||||
|
||||
const { administrator } = getAuthenticatedUser();
|
||||
const activeCourseModes = useMemo(() => courseModes?.map(mode => mode.slug), [courseModes]);
|
||||
|
||||
const notificationTrayEventProperties = {
|
||||
course_end: end,
|
||||
course_modes: activeCourseModes,
|
||||
@@ -57,8 +52,9 @@ const NotificationTray = ({ intl }) => {
|
||||
org_key: org,
|
||||
username,
|
||||
verification_status: verificationStatus,
|
||||
is_staff: isStaff,
|
||||
is_admin: administrator,
|
||||
};
|
||||
|
||||
// After three seconds, update notificationSeen (to hide red dot)
|
||||
useEffect(() => {
|
||||
setTimeout(onNotificationSeen, 3000);
|
||||
@@ -70,6 +66,7 @@ const NotificationTray = ({ intl }) => {
|
||||
title={intl.formatMessage(messages.notificationTitle)}
|
||||
ariaLabel={intl.formatMessage(messages.notificationTray)}
|
||||
sidebarId={ID}
|
||||
width="45rem"
|
||||
className={classNames({
|
||||
'h-100': !verifiedMode && !shouldDisplayFullScreen,
|
||||
'ml-4': !shouldDisplayFullScreen,
|
||||
@@ -77,20 +74,13 @@ const NotificationTray = ({ intl }) => {
|
||||
>
|
||||
<div>{verifiedMode
|
||||
? (
|
||||
<UpgradeNotification
|
||||
offer={offer}
|
||||
verifiedMode={verifiedMode}
|
||||
accessExpiration={accessExpiration}
|
||||
contentTypeGatingEnabled={contentTypeGatingEnabled}
|
||||
marketingUrl={marketingUrl}
|
||||
upsellPageName="in_course"
|
||||
userTimezone={userTimezone}
|
||||
shouldDisplayBorder={false}
|
||||
timeOffsetMillis={timeOffsetMillis}
|
||||
courseId={courseId}
|
||||
org={org}
|
||||
upgradeNotificationCurrentState={upgradeNotificationCurrentState}
|
||||
setupgradeNotificationCurrentState={setUpgradeNotificationCurrentState}
|
||||
<PluginSlot
|
||||
id="notification_tray_slot"
|
||||
pluginProps={{
|
||||
courseId,
|
||||
notificationCurrentState: upgradeNotificationCurrentState,
|
||||
setNotificationCurrentState: setUpgradeNotificationCurrentState,
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<p className="p-3 small">{intl.formatMessage(messages.noNotificationsMessage)}</p>
|
||||
|
||||
@@ -81,7 +81,7 @@ describe('NotificationTray', () => {
|
||||
.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders upgrade card', async () => {
|
||||
it('includes notification_tray_slot', async () => {
|
||||
await fetchAndRender(
|
||||
<SidebarContext.Provider value={{
|
||||
currentSidebar: ID,
|
||||
@@ -91,15 +91,7 @@ describe('NotificationTray', () => {
|
||||
<NotificationTray />
|
||||
</SidebarContext.Provider>,
|
||||
);
|
||||
const UpgradeNotification = document.querySelector('.upgrade-notification');
|
||||
|
||||
expect(UpgradeNotification)
|
||||
.toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: 'Upgrade for $149' }))
|
||||
.toBeInTheDocument();
|
||||
expect(screen.queryByText('You have no new notifications at this time.'))
|
||||
.not
|
||||
.toBeInTheDocument();
|
||||
expect(screen.getByTestId('notification_tray_slot')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders no notifications message if no verified mode', async () => {
|
||||
|
||||
@@ -78,8 +78,12 @@ const slice = createSlice({
|
||||
const sequenceId = Object.keys(state.courseOutline.sequences)
|
||||
.find(id => state.courseOutline.sequences[id].unitIds.includes(unitId));
|
||||
const sequenceUnits = state.courseOutline.sequences[sequenceId].unitIds;
|
||||
const completedUnits = sequenceUnits.filter((id) => state.courseOutline.units[id].complete);
|
||||
const isAllUnitsAreComplete = sequenceUnits.every((id) => state.courseOutline.units[id].complete);
|
||||
|
||||
// Update amount of completed units of the sequence
|
||||
state.courseOutline.sequences[sequenceId].completionStat.completed = completedUnits.length;
|
||||
|
||||
if (isAllUnitsAreComplete) {
|
||||
state.courseOutline.sequences[sequenceId].complete = true;
|
||||
}
|
||||
@@ -98,6 +102,12 @@ const slice = createSlice({
|
||||
state.courseOutlineShouldUpdate = true;
|
||||
}
|
||||
|
||||
// Update amount of completed units of the section
|
||||
state.courseOutline.sections[sectionId].completionStat.completed = sectionSequences.reduce(
|
||||
(acc, id) => acc + state.courseOutline.sequences[id].completionStat.completed,
|
||||
0,
|
||||
);
|
||||
|
||||
if (isAllSequencesAreComplete) {
|
||||
state.courseOutline.sections[sectionId].complete = true;
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ export function fetchCourse(courseId) {
|
||||
logError(courseHomeMetadataResult.reason);
|
||||
}
|
||||
if (!fetchedCoursewareOutlineSidebarTogglesResult) {
|
||||
logError(fetchedCoursewareOutlineSidebarTogglesResult.reason);
|
||||
logError(coursewareOutlineSidebarTogglesResult.reason);
|
||||
}
|
||||
if (fetchedMetadata && fetchedCourseHomeMetadata) {
|
||||
if (courseHomeMetadataResult.value.courseAccess.hasAccess && fetchedOutline) {
|
||||
|
||||
@@ -169,6 +169,10 @@ export function normalizeOutlineBlocks(courseId, blocks) {
|
||||
id: block.id,
|
||||
title: block.display_name,
|
||||
sequenceIds: block.children || [],
|
||||
completionStat: {
|
||||
completed: block.completion_stat?.completion,
|
||||
total: block.completion_stat?.completable_children,
|
||||
},
|
||||
};
|
||||
break;
|
||||
|
||||
@@ -181,6 +185,10 @@ export function normalizeOutlineBlocks(courseId, blocks) {
|
||||
type: block.type,
|
||||
specialExamInfo: block.special_exam_info,
|
||||
unitIds: block.children || [],
|
||||
completionStat: {
|
||||
completed: block.completion_stat?.completion,
|
||||
total: block.completion_stat?.completable_children,
|
||||
},
|
||||
};
|
||||
break;
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import { LearningHeader as Header } from '@edx/frontend-component-header';
|
||||
import Footer from '@edx/frontend-component-footer';
|
||||
import { useParams, Navigate } from 'react-router-dom';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import FooterSlot from '@openedx/frontend-slot-footer';
|
||||
import useActiveEnterpriseAlert from '../alerts/active-enteprise-alert';
|
||||
import { AlertList } from './user-messages';
|
||||
import { fetchDiscussionTab } from '../course-home/data/thunks';
|
||||
@@ -32,7 +32,7 @@ const CourseAccessErrorPage = ({ intl }) => {
|
||||
<PageLoading
|
||||
srMessage={intl.formatMessage(messages.loading)}
|
||||
/>
|
||||
<Footer />
|
||||
<FooterSlot />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -51,7 +51,7 @@ const CourseAccessErrorPage = ({ intl }) => {
|
||||
}}
|
||||
/>
|
||||
</main>
|
||||
<Footer />
|
||||
<FooterSlot />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,564 +0,0 @@
|
||||
import React, { useEffect } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
import {
|
||||
useIntl, FormattedDate, FormattedMessage, injectIntl,
|
||||
} from '@edx/frontend-platform/i18n';
|
||||
import { sendTrackEvent, sendTrackingLogEvent } from '@edx/frontend-platform/analytics';
|
||||
import { Button, Icon, IconButton } from '@openedx/paragon';
|
||||
import { Close } from '@openedx/paragon/icons';
|
||||
import { setLocalStorage } from '../../data/localStorage';
|
||||
import { UpgradeButton } from '../upgrade-button';
|
||||
import {
|
||||
VerifiedCertBullet,
|
||||
UnlockGradedBullet,
|
||||
FullAccessBullet,
|
||||
SupportMissionBullet,
|
||||
} from '../upsell-bullets/UpsellBullets';
|
||||
import messages from '../messages';
|
||||
|
||||
const UpsellNoFBECardContent = () => (
|
||||
<ul className="fa-ul upgrade-notification-ul pt-0">
|
||||
<VerifiedCertBullet />
|
||||
<SupportMissionBullet />
|
||||
</ul>
|
||||
);
|
||||
|
||||
const UpsellFBEFarAwayCardContent = () => (
|
||||
<ul className="fa-ul upgrade-notification-ul">
|
||||
<VerifiedCertBullet />
|
||||
<UnlockGradedBullet />
|
||||
<FullAccessBullet />
|
||||
<SupportMissionBullet />
|
||||
</ul>
|
||||
);
|
||||
|
||||
const UpsellFBESoonCardContent = ({ accessExpirationDate, timezoneFormatArgs }) => {
|
||||
const includingAnyProgress = (
|
||||
<span className="font-weight-bold">
|
||||
<FormattedMessage
|
||||
id="learning.generic.upgradeNotification.expirationAccessLoss.progress"
|
||||
defaultMessage="including any progress"
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
|
||||
const date = (
|
||||
<FormattedDate
|
||||
key="accessDate"
|
||||
day="numeric"
|
||||
month="long"
|
||||
value={new Date(accessExpirationDate)}
|
||||
{...timezoneFormatArgs}
|
||||
/>
|
||||
);
|
||||
|
||||
const benefitsOfUpgrading = (
|
||||
<a className="inline-link-underline font-weight-bold" rel="noopener noreferrer" target="_blank" href="https://support.edx.org/hc/en-us/articles/360013426573-What-are-the-differences-between-audit-free-and-verified-paid-courses-">
|
||||
<FormattedMessage
|
||||
id="learning.generic.upgradeNotification.expirationVerifiedCert.benefits"
|
||||
defaultMessage="benefits of upgrading"
|
||||
/>
|
||||
</a>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="upgrade-notification-text">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="learning.generic.upgradeNotification.expirationAccessLoss"
|
||||
defaultMessage="You will lose all access to this course, {includingAnyProgress}, on {date}."
|
||||
values={{
|
||||
includingAnyProgress,
|
||||
date,
|
||||
}}
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="learning.generic.upgradeNotification.expirationVerifiedCert"
|
||||
defaultMessage="Upgrading your course enables you to pursue a verified certificate and unlocks numerous features. Learn more about the {benefitsOfUpgrading}."
|
||||
values={{ benefitsOfUpgrading }}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
UpsellFBESoonCardContent.propTypes = {
|
||||
accessExpirationDate: PropTypes.PropTypes.instanceOf(Date).isRequired,
|
||||
timezoneFormatArgs: PropTypes.shape({
|
||||
timeZone: PropTypes.string,
|
||||
}),
|
||||
};
|
||||
|
||||
UpsellFBESoonCardContent.defaultProps = {
|
||||
timezoneFormatArgs: {},
|
||||
};
|
||||
|
||||
const PastExpirationCardContent = () => (
|
||||
<div className="upgrade-notification-text">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="learning.generic.upgradeNotification.pastExpiration.content"
|
||||
defaultMessage="The upgrade deadline for this course passed. To upgrade, enroll in the next available session."
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
const ExpirationCountdown = ({
|
||||
courseId, hoursToExpiration, setupgradeNotificationCurrentState, type,
|
||||
}) => {
|
||||
let expirationText;
|
||||
if (hoursToExpiration >= 24) { // More than 1 day left
|
||||
// setupgradeNotificationCurrentState is available in NotificationTray (not course home)
|
||||
if (setupgradeNotificationCurrentState) {
|
||||
if (type === 'access') {
|
||||
setupgradeNotificationCurrentState('accessDaysLeft');
|
||||
setLocalStorage(`upgradeNotificationCurrentState.${courseId}`, 'accessDaysLeft');
|
||||
}
|
||||
if (type === 'offer') {
|
||||
setupgradeNotificationCurrentState('FPDdaysLeft');
|
||||
setLocalStorage(`upgradeNotificationCurrentState.${courseId}`, 'FPDdaysLeft');
|
||||
}
|
||||
}
|
||||
expirationText = (
|
||||
<FormattedMessage
|
||||
id="learning.generic.upgradeNotification.expirationDays"
|
||||
defaultMessage={`{dayCount, number} {dayCount, plural,
|
||||
one {day}
|
||||
other {days}} left`}
|
||||
values={{
|
||||
dayCount: (Math.floor(hoursToExpiration / 24)),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
} else if (hoursToExpiration >= 1) { // More than 1 hour left
|
||||
// setupgradeNotificationCurrentState is available in NotificationTray (not course home)
|
||||
if (setupgradeNotificationCurrentState) {
|
||||
if (type === 'access') {
|
||||
setupgradeNotificationCurrentState('accessHoursLeft');
|
||||
setLocalStorage(`upgradeNotificationCurrentState.${courseId}`, 'accessHoursLeft');
|
||||
}
|
||||
if (type === 'offer') {
|
||||
setupgradeNotificationCurrentState('FPDHoursLeft');
|
||||
setLocalStorage(`upgradeNotificationCurrentState.${courseId}`, 'FPDHoursLeft');
|
||||
}
|
||||
}
|
||||
expirationText = (
|
||||
<FormattedMessage
|
||||
id="learning.generic.upgradeNotification.expirationHours"
|
||||
defaultMessage={`{hourCount, number} {hourCount, plural,
|
||||
one {hour}
|
||||
other {hours}} left`}
|
||||
values={{
|
||||
hourCount: (hoursToExpiration),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
} else { // Less than 1 hour
|
||||
// setupgradeNotificationCurrentState is available in NotificationTray (not course home)
|
||||
if (setupgradeNotificationCurrentState) {
|
||||
if (type === 'access') {
|
||||
setupgradeNotificationCurrentState('accessLastHour');
|
||||
setLocalStorage(`upgradeNotificationCurrentState.${courseId}`, 'accessLastHour');
|
||||
}
|
||||
if (type === 'offer') {
|
||||
setupgradeNotificationCurrentState('FPDLastHour');
|
||||
setLocalStorage(`upgradeNotificationCurrentState.${courseId}`, 'FPDLastHour');
|
||||
}
|
||||
}
|
||||
expirationText = (
|
||||
<FormattedMessage
|
||||
id="learning.generic.upgradeNotification.expirationMinutes"
|
||||
defaultMessage="Less than 1 hour left"
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (<div className="upsell-warning">{expirationText}</div>);
|
||||
};
|
||||
|
||||
ExpirationCountdown.propTypes = {
|
||||
courseId: PropTypes.string.isRequired,
|
||||
hoursToExpiration: PropTypes.number.isRequired,
|
||||
setupgradeNotificationCurrentState: PropTypes.func,
|
||||
type: PropTypes.string,
|
||||
};
|
||||
ExpirationCountdown.defaultProps = {
|
||||
setupgradeNotificationCurrentState: null,
|
||||
type: null,
|
||||
};
|
||||
|
||||
const AccessExpirationDateBanner = ({
|
||||
courseId, accessExpirationDate, timezoneFormatArgs, setupgradeNotificationCurrentState,
|
||||
}) => {
|
||||
if (setupgradeNotificationCurrentState) {
|
||||
setupgradeNotificationCurrentState('accessDateView');
|
||||
setLocalStorage(`upgradeNotificationCurrentState.${courseId}`, 'accessDateView');
|
||||
}
|
||||
return (
|
||||
<div className="upsell-warning-light">
|
||||
<FormattedMessage
|
||||
id="learning.generic.upgradeNotification.expiration"
|
||||
defaultMessage="Course access will expire {date}"
|
||||
values={{
|
||||
date: (
|
||||
<FormattedDate
|
||||
key="accessExpireDate"
|
||||
day="numeric"
|
||||
month="long"
|
||||
value={accessExpirationDate}
|
||||
{...timezoneFormatArgs}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
AccessExpirationDateBanner.propTypes = {
|
||||
courseId: PropTypes.string.isRequired,
|
||||
accessExpirationDate: PropTypes.PropTypes.instanceOf(Date).isRequired,
|
||||
timezoneFormatArgs: PropTypes.shape({
|
||||
timeZone: PropTypes.string,
|
||||
}),
|
||||
setupgradeNotificationCurrentState: PropTypes.func,
|
||||
};
|
||||
|
||||
AccessExpirationDateBanner.defaultProps = {
|
||||
timezoneFormatArgs: {},
|
||||
setupgradeNotificationCurrentState: null,
|
||||
};
|
||||
|
||||
const PastExpirationDateBanner = ({
|
||||
courseId, accessExpirationDate, timezoneFormatArgs, setupgradeNotificationCurrentState,
|
||||
}) => {
|
||||
if (setupgradeNotificationCurrentState) {
|
||||
setupgradeNotificationCurrentState('PastExpirationDate');
|
||||
setLocalStorage(`upgradeNotificationCurrentState.${courseId}`, 'PastExpirationDate');
|
||||
}
|
||||
return (
|
||||
<div className="upsell-warning">
|
||||
<FormattedMessage
|
||||
id="learning.generic.upgradeNotification.pastExpiration.banner"
|
||||
defaultMessage="Upgrade deadline passed on {date}"
|
||||
values={{
|
||||
date: (
|
||||
<FormattedDate
|
||||
key="accessExpireDate"
|
||||
day="numeric"
|
||||
month="long"
|
||||
value={accessExpirationDate}
|
||||
{...timezoneFormatArgs}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
PastExpirationDateBanner.propTypes = {
|
||||
courseId: PropTypes.string.isRequired,
|
||||
accessExpirationDate: PropTypes.PropTypes.instanceOf(Date).isRequired,
|
||||
timezoneFormatArgs: PropTypes.shape({
|
||||
timeZone: PropTypes.string,
|
||||
}),
|
||||
setupgradeNotificationCurrentState: PropTypes.func,
|
||||
};
|
||||
|
||||
PastExpirationDateBanner.defaultProps = {
|
||||
timezoneFormatArgs: {},
|
||||
setupgradeNotificationCurrentState: null,
|
||||
};
|
||||
|
||||
const UpgradeNotification = ({
|
||||
accessExpiration,
|
||||
contentTypeGatingEnabled,
|
||||
marketingUrl,
|
||||
courseId,
|
||||
offer,
|
||||
org,
|
||||
setupgradeNotificationCurrentState,
|
||||
shouldDisplayBorder,
|
||||
timeOffsetMillis,
|
||||
upsellPageName,
|
||||
userTimezone,
|
||||
verifiedMode,
|
||||
toggleSidebar,
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const dateNow = Date.now();
|
||||
const timezoneFormatArgs = userTimezone ? { timeZone: userTimezone } : {};
|
||||
const correctedTime = new Date(dateNow + timeOffsetMillis);
|
||||
const accessExpirationDate = accessExpiration ? new Date(accessExpiration.expirationDate) : null;
|
||||
const pastExpirationDeadline = accessExpiration ? new Date(dateNow) > accessExpirationDate : false;
|
||||
|
||||
const eventProperties = {
|
||||
org_key: org,
|
||||
courserun_key: courseId,
|
||||
};
|
||||
|
||||
const promotionEventProperties = {
|
||||
creative: 'sidebarupsell',
|
||||
name: 'In-Course Verification Prompt',
|
||||
position: 'sidebar-message',
|
||||
promotion_id: 'courseware_verified_certificate_upsell',
|
||||
...eventProperties,
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
sendTrackingLogEvent('edx.bi.course.upgrade.sidebarupsell.displayed', eventProperties);
|
||||
sendTrackEvent('Promotion Viewed', promotionEventProperties);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
if (!verifiedMode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const logClick = () => {
|
||||
sendTrackingLogEvent('edx.bi.course.upgrade.sidebarupsell.clicked', eventProperties);
|
||||
sendTrackingLogEvent('edx.course.enrollment.upgrade.clicked', {
|
||||
...eventProperties,
|
||||
location: 'sidebar-message',
|
||||
});
|
||||
sendTrackEvent('Promotion Clicked', promotionEventProperties);
|
||||
sendTrackEvent('edx.bi.ecommerce.upsell_links_clicked', {
|
||||
...eventProperties,
|
||||
linkCategory: 'green_upgrade',
|
||||
linkName: `${upsellPageName}_green`,
|
||||
linkType: 'button',
|
||||
pageName: upsellPageName,
|
||||
});
|
||||
};
|
||||
|
||||
const logClickPastExpiration = () => {
|
||||
sendTrackEvent('edx.bi.ecommerce.upgrade_notification.past_expiration.button_clicked', {
|
||||
...eventProperties,
|
||||
linkCategory: 'upgrade_notification',
|
||||
linkName: `${upsellPageName}_course_details`,
|
||||
linkType: 'button',
|
||||
pageName: upsellPageName,
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
There are 5 parts that change in the upgrade card:
|
||||
upgradeNotificationHeaderText
|
||||
expirationBanner
|
||||
upsellMessage
|
||||
callToActionButton
|
||||
offerCode
|
||||
*/
|
||||
let upgradeNotificationHeaderText;
|
||||
let expirationBanner;
|
||||
let upsellMessage;
|
||||
let callToActionButton;
|
||||
let offerCode;
|
||||
|
||||
if (!!accessExpiration && !!contentTypeGatingEnabled) {
|
||||
const hoursToAccessExpiration = Math.floor((accessExpirationDate - correctedTime) / 1000 / 60 / 60);
|
||||
|
||||
if (hoursToAccessExpiration >= (7 * 24)) {
|
||||
if (offer) { // countdown to the first purchase discount if there is one
|
||||
const hoursToDiscountExpiration = Math.floor((new Date(offer.expirationDate) - correctedTime) / 1000 / 60 / 60);
|
||||
upgradeNotificationHeaderText = (
|
||||
<FormattedMessage
|
||||
id="learning.generic.upgradeNotification.firstTimeLearnerDiscount"
|
||||
defaultMessage="{percentage}% First-Time Learner Discount"
|
||||
values={{
|
||||
percentage: (offer.percentage),
|
||||
}}
|
||||
/>
|
||||
);
|
||||
expirationBanner = (
|
||||
<ExpirationCountdown
|
||||
courseId={courseId}
|
||||
hoursToExpiration={hoursToDiscountExpiration}
|
||||
setupgradeNotificationCurrentState={setupgradeNotificationCurrentState}
|
||||
type="offer"
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
upgradeNotificationHeaderText = (
|
||||
<FormattedMessage
|
||||
id="learning.generic.upgradeNotification.accessExpiration"
|
||||
defaultMessage="Upgrade your course today"
|
||||
/>
|
||||
);
|
||||
expirationBanner = (
|
||||
<AccessExpirationDateBanner
|
||||
courseId={courseId}
|
||||
accessExpirationDate={accessExpirationDate}
|
||||
timezoneFormatArgs={timezoneFormatArgs}
|
||||
setupgradeNotificationCurrentState={setupgradeNotificationCurrentState}
|
||||
/>
|
||||
);
|
||||
}
|
||||
upsellMessage = <UpsellFBEFarAwayCardContent />;
|
||||
} else if (hoursToAccessExpiration < (7 * 24) && hoursToAccessExpiration >= 0) {
|
||||
// more urgent messaging if there's less than 7 days left to access expiration
|
||||
upgradeNotificationHeaderText = (
|
||||
<FormattedMessage
|
||||
id="learning.generic.upgradeNotification.accessExpirationUrgent"
|
||||
defaultMessage="Course Access Expiration"
|
||||
/>
|
||||
);
|
||||
expirationBanner = (
|
||||
<ExpirationCountdown
|
||||
courseId={courseId}
|
||||
hoursToExpiration={hoursToAccessExpiration}
|
||||
setupgradeNotificationCurrentState={setupgradeNotificationCurrentState}
|
||||
type="access"
|
||||
/>
|
||||
);
|
||||
upsellMessage = (
|
||||
<UpsellFBESoonCardContent
|
||||
accessExpirationDate={accessExpirationDate}
|
||||
timezoneFormatArgs={timezoneFormatArgs}
|
||||
/>
|
||||
);
|
||||
} else { // access expiration deadline has passed
|
||||
upgradeNotificationHeaderText = (
|
||||
<FormattedMessage
|
||||
id="learning.generic.upgradeNotification.accessExpirationPast"
|
||||
defaultMessage="Course Access Expiration"
|
||||
/>
|
||||
);
|
||||
expirationBanner = (
|
||||
<PastExpirationDateBanner
|
||||
courseId={courseId}
|
||||
accessExpirationDate={accessExpirationDate}
|
||||
timezoneFormatArgs={timezoneFormatArgs}
|
||||
setupgradeNotificationCurrentState={setupgradeNotificationCurrentState}
|
||||
/>
|
||||
);
|
||||
upsellMessage = (
|
||||
<PastExpirationCardContent />
|
||||
);
|
||||
}
|
||||
} else { // FBE is turned off
|
||||
upgradeNotificationHeaderText = (
|
||||
<FormattedMessage
|
||||
id="learning.generic.upgradeNotification.pursueAverifiedCertificate"
|
||||
defaultMessage="Pursue a verified certificate"
|
||||
/>
|
||||
);
|
||||
upsellMessage = (<UpsellNoFBECardContent />);
|
||||
}
|
||||
|
||||
if (pastExpirationDeadline) {
|
||||
callToActionButton = (
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={logClickPastExpiration}
|
||||
href={marketingUrl}
|
||||
block
|
||||
>
|
||||
View Course Details
|
||||
</Button>
|
||||
);
|
||||
} else {
|
||||
callToActionButton = (
|
||||
<UpgradeButton
|
||||
offer={offer}
|
||||
onClick={logClick}
|
||||
verifiedMode={verifiedMode}
|
||||
block
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (offer) { // if there's a first purchase discount, message the code at the bottom
|
||||
offerCode = (
|
||||
<div className="text-center discount-info">
|
||||
<FormattedMessage
|
||||
id="learning.generic.upgradeNotification.code"
|
||||
defaultMessage="Use code {code} at checkout"
|
||||
values={{
|
||||
code: (<span className="font-weight-bold">{offer.code}</span>),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<section className={classNames('upgrade-notification small', { 'card mb-4': shouldDisplayBorder })}>
|
||||
<div id="courseHome-upgradeNotification">
|
||||
<h2
|
||||
className={classNames('h5 upgrade-notification-header', {
|
||||
'd-flex align-items-center mr-2 ml-4 my-1.5 font-size-18': !!toggleSidebar,
|
||||
})}
|
||||
id="outline-sidebar-upgrade-header"
|
||||
>
|
||||
{upgradeNotificationHeaderText}
|
||||
{!!toggleSidebar && (
|
||||
<div className="d-inline-flex ml-auto">
|
||||
<IconButton
|
||||
src={Close}
|
||||
size="sm"
|
||||
iconAs={Icon}
|
||||
onClick={toggleSidebar}
|
||||
className="icon-hover"
|
||||
alt={intl.formatMessage(messages.close)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</h2>
|
||||
{expirationBanner}
|
||||
<div className="upgrade-notification-message">
|
||||
{upsellMessage}
|
||||
</div>
|
||||
<div className="upgrade-notification-button">
|
||||
{callToActionButton}
|
||||
</div>
|
||||
{offerCode}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
UpgradeNotification.propTypes = {
|
||||
courseId: PropTypes.string.isRequired,
|
||||
org: PropTypes.string.isRequired,
|
||||
accessExpiration: PropTypes.shape({
|
||||
expirationDate: PropTypes.string,
|
||||
}),
|
||||
contentTypeGatingEnabled: PropTypes.bool,
|
||||
marketingUrl: PropTypes.string,
|
||||
offer: PropTypes.shape({
|
||||
expirationDate: PropTypes.string,
|
||||
percentage: PropTypes.number,
|
||||
code: PropTypes.string,
|
||||
}),
|
||||
toggleSidebar: PropTypes.func,
|
||||
shouldDisplayBorder: PropTypes.bool,
|
||||
setupgradeNotificationCurrentState: PropTypes.func,
|
||||
timeOffsetMillis: PropTypes.number,
|
||||
upsellPageName: PropTypes.string.isRequired,
|
||||
userTimezone: PropTypes.string,
|
||||
verifiedMode: PropTypes.shape({
|
||||
currencySymbol: PropTypes.string.isRequired,
|
||||
price: PropTypes.number.isRequired,
|
||||
upgradeUrl: PropTypes.string.isRequired,
|
||||
}),
|
||||
};
|
||||
|
||||
UpgradeNotification.defaultProps = {
|
||||
accessExpiration: null,
|
||||
contentTypeGatingEnabled: false,
|
||||
marketingUrl: null,
|
||||
offer: null,
|
||||
setupgradeNotificationCurrentState: null,
|
||||
shouldDisplayBorder: null,
|
||||
timeOffsetMillis: 0,
|
||||
userTimezone: null,
|
||||
verifiedMode: null,
|
||||
toggleSidebar: null,
|
||||
};
|
||||
|
||||
export default injectIntl(UpgradeNotification);
|
||||
@@ -1,46 +0,0 @@
|
||||
.upgrade-notification {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
|
||||
.upgrade-notification-header {
|
||||
margin: 1.25rem;
|
||||
}
|
||||
|
||||
.upsell-warning {
|
||||
background-color: $danger-100;
|
||||
}
|
||||
|
||||
.upsell-warning-light {
|
||||
background-color: $warning-100;
|
||||
}
|
||||
|
||||
.upsell-warning, .upsell-warning-light {
|
||||
padding: 0.5rem 1.25rem;
|
||||
}
|
||||
|
||||
// .fa-ul added so specificity is higher than Font Awesome's .fa-ul.
|
||||
// An additional Font Awesome stylesheet is imported by Braze in
|
||||
// stage/production but not devstack.
|
||||
.upgrade-notification-ul.fa-ul {
|
||||
padding: 0.875rem 1.25rem 0;
|
||||
margin: 0 0 1rem 2.5rem;
|
||||
}
|
||||
|
||||
.upgrade-notification-text {
|
||||
padding: 0.875rem 1.25rem 0 1.25rem;
|
||||
}
|
||||
|
||||
.upgrade-notification-button {
|
||||
padding: 1.25rem;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.discount-info {
|
||||
border-top: 1px solid $light-400;
|
||||
padding-top: .75rem;
|
||||
padding-bottom: .75rem;
|
||||
}
|
||||
|
||||
.font-size-18 {
|
||||
font-size: 18px !important;
|
||||
}
|
||||
@@ -1,322 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Factory } from 'rosie';
|
||||
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||
|
||||
import {
|
||||
fireEvent,
|
||||
initializeMockApp,
|
||||
render,
|
||||
screen,
|
||||
waitFor,
|
||||
} from '../../setupTest';
|
||||
import UpgradeNotification from './UpgradeNotification';
|
||||
|
||||
initializeMockApp();
|
||||
jest.mock('@edx/frontend-platform/analytics');
|
||||
const dateNow = new Date('2021-04-13T11:01:58.000Z');
|
||||
jest
|
||||
.spyOn(global.Date, 'now')
|
||||
.mockImplementation(() => dateNow.valueOf());
|
||||
|
||||
describe('Upgrade Notification', () => {
|
||||
function buildAndRender(attributes) {
|
||||
const upgradeNotificationData = Factory.build('upgradeNotificationData', { ...attributes });
|
||||
render(<UpgradeNotification {...upgradeNotificationData} />);
|
||||
}
|
||||
|
||||
it('sends upgrade click info to segment', async () => {
|
||||
sendTrackEvent.mockClear();
|
||||
buildAndRender({ pageName: 'test' });
|
||||
|
||||
const upgradeButton = await waitFor(() => screen.queryByRole('link', { name: 'Upgrade for $149' }));
|
||||
fireEvent.click(upgradeButton);
|
||||
|
||||
expect(sendTrackEvent).toHaveBeenCalledTimes(3);
|
||||
expect(sendTrackEvent).toHaveBeenNthCalledWith(3, 'edx.bi.ecommerce.upsell_links_clicked', {
|
||||
org_key: 'edX',
|
||||
courserun_key: 'course-v1:edX+DemoX+Demo_Course',
|
||||
linkCategory: 'green_upgrade',
|
||||
linkName: 'test_green',
|
||||
linkType: 'button',
|
||||
pageName: 'test',
|
||||
});
|
||||
});
|
||||
|
||||
it('does not render when there is no verified mode', async () => {
|
||||
buildAndRender({ verifiedMode: null });
|
||||
expect(screen.queryByRole('link', { name: 'Upgrade for $149' })).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders non-FBE when there is a verified mode but no FBE', async () => {
|
||||
buildAndRender();
|
||||
expect(screen.getByRole('heading', { name: 'Pursue a verified certificate' })).toBeInTheDocument();
|
||||
expect(screen.getByText(/Earn a.*?of completion to showcase on your resumé/s).textContent).toMatch('Earn a verified certificate of completion to showcase on your resumé');
|
||||
expect(screen.getByText(/Support our.*?at edX/s).textContent).toMatch('Support our mission at edX');
|
||||
expect(screen.getByRole('link', { name: 'Upgrade for $149' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders non-FBE when there is a verified mode and access expiration, but no content gating', async () => {
|
||||
const expirationDate = new Date(dateNow);
|
||||
expirationDate.setMinutes(expirationDate.getMinutes() + 45);
|
||||
buildAndRender({
|
||||
accessExpiration: {
|
||||
expirationDate: expirationDate.toString(),
|
||||
},
|
||||
});
|
||||
expect(screen.getByRole('heading', { name: 'Pursue a verified certificate' })).toBeInTheDocument();
|
||||
expect(screen.getByText(/Earn a.*?of completion to showcase on your resumé/s).textContent).toMatch('Earn a verified certificate of completion to showcase on your resumé');
|
||||
expect(screen.getByText(/Support our.*?at edX/s).textContent).toMatch('Support our mission at edX');
|
||||
expect(screen.getByRole('link', { name: 'Upgrade for $149' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders non-FBE when there is a verified mode and content gating, but no access expiration', async () => {
|
||||
buildAndRender({
|
||||
contentTypeGatingEnabled: true,
|
||||
accessExpiration: null,
|
||||
});
|
||||
expect(screen.getByRole('heading', { name: 'Pursue a verified certificate' })).toBeInTheDocument();
|
||||
expect(screen.getByText(/Earn a.*?of completion to showcase on your resumé/s).textContent).toMatch('Earn a verified certificate of completion to showcase on your resumé');
|
||||
expect(screen.getByText(/Support our.*?at edX/s).textContent).toMatch('Support our mission at edX');
|
||||
expect(screen.getByRole('link', { name: 'Upgrade for $149' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders non-FBE with a discount properly', async () => {
|
||||
const discountExpirationDate = new Date(dateNow);
|
||||
discountExpirationDate.setDate(discountExpirationDate.getDate() + 6);
|
||||
buildAndRender({
|
||||
offer: {
|
||||
expirationDate: discountExpirationDate.toString(),
|
||||
percentage: 15,
|
||||
code: 'Welcome15',
|
||||
discountedPrice: '$126.65',
|
||||
originalPrice: '$149',
|
||||
upgradeUrl: 'www.exampleUpgradeUrl.com',
|
||||
},
|
||||
});
|
||||
expect(screen.getByRole('heading', { name: 'Pursue a verified certificate' })).toBeInTheDocument();
|
||||
expect(screen.getByText(/Earn a.*?of completion to showcase on your resumé/s).textContent).toMatch('Earn a verified certificate of completion to showcase on your resumé');
|
||||
expect(screen.getByText(/Support our.*?at edX/s).textContent).toMatch('Support our mission at edX');
|
||||
expect(screen.getByText(/Upgrade for/).textContent).toMatch('$126.65 ($149)');
|
||||
expect(screen.getByText(/Use code.*?at checkout/s).textContent).toMatch('Use code Welcome15 at checkout');
|
||||
});
|
||||
|
||||
it('renders FBE expiration within an hour properly', async () => {
|
||||
const expirationDate = new Date(dateNow);
|
||||
expirationDate.setMinutes(expirationDate.getMinutes() + 45);
|
||||
buildAndRender({
|
||||
accessExpiration: {
|
||||
expirationDate: expirationDate.toString(),
|
||||
},
|
||||
contentTypeGatingEnabled: true,
|
||||
});
|
||||
expect(screen.getByRole('heading', { name: 'Course Access Expiration' })).toBeInTheDocument();
|
||||
expect(screen.getByText('Less than 1 hour left')).toBeInTheDocument();
|
||||
expect(screen.getByText(/You will lose all access to this course.*?on/s).textContent).toMatch('You will lose all access to this course, including any progress, on April 13.');
|
||||
expect(screen.getByText(/Upgrading your course enables you/s).textContent).toMatch('Upgrading your course enables you to pursue a verified certificate and unlocks numerous features. Learn more about the benefits of upgrading.');
|
||||
expect(screen.getByRole('link', { name: 'Upgrade for $149' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders FBE expiration within 24 hours properly', async () => {
|
||||
const expirationDate = new Date(dateNow);
|
||||
expirationDate.setHours(expirationDate.getHours() + 12);
|
||||
buildAndRender({
|
||||
accessExpiration: {
|
||||
expirationDate: expirationDate.toString(),
|
||||
},
|
||||
contentTypeGatingEnabled: true,
|
||||
});
|
||||
expect(screen.getByRole('heading', { name: 'Course Access Expiration' })).toBeInTheDocument();
|
||||
expect(screen.getByText('12 hours left')).toBeInTheDocument();
|
||||
expect(screen.getByText(/You will lose all access to this course.*?on/s)).toHaveTextContent('You will lose all access to this course, including any progress, on April 13.');
|
||||
expect(screen.getByText(/Upgrading your course enables you/s).textContent).toMatch('Upgrading your course enables you to pursue a verified certificate and unlocks numerous features. Learn more about the benefits of upgrading.');
|
||||
expect(screen.getByRole('link', { name: 'Upgrade for $149' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders FBE expiration within 7 days properly', async () => {
|
||||
const expirationDate = new Date(dateNow);
|
||||
expirationDate.setDate(expirationDate.getDate() + 6);
|
||||
buildAndRender({
|
||||
accessExpiration: {
|
||||
expirationDate: expirationDate.toString(),
|
||||
},
|
||||
contentTypeGatingEnabled: true,
|
||||
});
|
||||
expect(screen.getByRole('heading', { name: 'Course Access Expiration' })).toBeInTheDocument();
|
||||
expect(screen.getByText('6 days left')).toBeInTheDocument(); // setting the time to 12 will mean that it's slightly less than 12
|
||||
expect(screen.getByText(/You will lose all access to this course.*?on/s).textContent).toMatch('You will lose all access to this course, including any progress, on April 19.');
|
||||
expect(screen.getByText(/Upgrading your course enables you/s).textContent).toMatch('Upgrading your course enables you to pursue a verified certificate and unlocks numerous features. Learn more about the benefits of upgrading.');
|
||||
expect(screen.getByRole('link', { name: 'Upgrade for $149' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders FBE expiration greater than 7 days properly', async () => {
|
||||
const expirationDate = new Date(dateNow);
|
||||
expirationDate.setDate(expirationDate.getDate() + 14);
|
||||
buildAndRender({
|
||||
accessExpiration: {
|
||||
expirationDate: expirationDate.toString(),
|
||||
},
|
||||
contentTypeGatingEnabled: true,
|
||||
});
|
||||
expect(screen.getByRole('heading', { name: 'Upgrade your course today' })).toBeInTheDocument();
|
||||
expect(screen.getByText(/Course access will expire/s).textContent).toMatch('Course access will expire April 27');
|
||||
expect(screen.getByText(/Earn a.*?of completion to showcase on your resumé/s).textContent).toMatch('Earn a verified certificate of completion to showcase on your resumé');
|
||||
expect(screen.getByText(/Unlock your access/s).textContent).toMatch('Unlock your access to all course activities, including graded assignments');
|
||||
expect(screen.getByText(/to course content and materials/s).textContent).toMatch('Full access to course content and materials, even after the course ends');
|
||||
expect(screen.getByText(/Support our.*?at edX/s).textContent).toMatch('Support our mission at edX');
|
||||
expect(screen.getByRole('link', { name: 'Upgrade for $149' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders discount less than an hour properly', async () => {
|
||||
const accessExpirationDate = new Date(dateNow);
|
||||
accessExpirationDate.setDate(accessExpirationDate.getDate() + 21);
|
||||
const discountExpirationDate = new Date(dateNow);
|
||||
discountExpirationDate.setMinutes(discountExpirationDate.getMinutes() + 30);
|
||||
buildAndRender({
|
||||
accessExpiration: {
|
||||
expirationDate: accessExpirationDate.toString(),
|
||||
},
|
||||
contentTypeGatingEnabled: true,
|
||||
offer: {
|
||||
expirationDate: discountExpirationDate.toString(),
|
||||
percentage: 15,
|
||||
code: 'Welcome15',
|
||||
discountedPrice: '$126.65',
|
||||
originalPrice: '$149',
|
||||
upgradeUrl: 'www.exampleUpgradeUrl.com',
|
||||
},
|
||||
});
|
||||
expect(screen.getByRole('heading', { name: '15% First-Time Learner Discount' })).toBeInTheDocument();
|
||||
expect(screen.getByText('Less than 1 hour left')).toBeInTheDocument();
|
||||
expect(screen.getByText(/Earn a.*?of completion to showcase on your resumé/s).textContent).toMatch('Earn a verified certificate of completion to showcase on your resumé');
|
||||
expect(screen.getByText(/Unlock your access/s).textContent).toMatch('Unlock your access to all course activities, including graded assignments');
|
||||
expect(screen.getByText(/to course content and materials/s).textContent).toMatch('Full access to course content and materials, even after the course ends');
|
||||
expect(screen.getByText(/Support our.*?at edX/s).textContent).toMatch('Support our mission at edX');
|
||||
expect(screen.getByText(/Upgrade for/).textContent).toMatch('$126.65 ($149)');
|
||||
expect(screen.getByText(/Use code.*?at checkout/s).textContent).toMatch('Use code Welcome15 at checkout');
|
||||
});
|
||||
|
||||
it('renders discount less than a day properly', async () => {
|
||||
const accessExpirationDate = new Date(dateNow);
|
||||
accessExpirationDate.setDate(accessExpirationDate.getDate() + 21);
|
||||
const discountExpirationDate = new Date(dateNow);
|
||||
discountExpirationDate.setHours(discountExpirationDate.getHours() + 12);
|
||||
buildAndRender({
|
||||
accessExpiration: {
|
||||
expirationDate: accessExpirationDate.toString(),
|
||||
},
|
||||
contentTypeGatingEnabled: true,
|
||||
offer: {
|
||||
expirationDate: discountExpirationDate.toString(),
|
||||
percentage: 15,
|
||||
code: 'Welcome15',
|
||||
discountedPrice: '$126.65',
|
||||
originalPrice: '$149',
|
||||
upgradeUrl: 'www.exampleUpgradeUrl.com',
|
||||
},
|
||||
});
|
||||
expect(screen.getByRole('heading', { name: '15% First-Time Learner Discount' })).toBeInTheDocument();
|
||||
expect(screen.getByText(/hours left/s).textContent).toMatch('12 hours left');
|
||||
expect(screen.getByText(/Earn a.*?of completion to showcase on your resumé/s).textContent).toMatch('Earn a verified certificate of completion to showcase on your resumé');
|
||||
expect(screen.getByText(/Unlock your access/s).textContent).toMatch('Unlock your access to all course activities, including graded assignments');
|
||||
expect(screen.getByText(/to course content and materials/s).textContent).toMatch('Full access to course content and materials, even after the course ends');
|
||||
expect(screen.getByText(/Support our.*?at edX/s).textContent).toMatch('Support our mission at edX');
|
||||
expect(screen.getByText(/Upgrade for/).textContent).toMatch('$126.65 ($149)');
|
||||
expect(screen.getByText(/Use code.*?at checkout/s).textContent).toMatch('Use code Welcome15 at checkout');
|
||||
});
|
||||
|
||||
it('renders discount less a week properly', async () => {
|
||||
const accessExpirationDate = new Date(dateNow);
|
||||
accessExpirationDate.setDate(accessExpirationDate.getDate() + 21);
|
||||
const discountExpirationDate = new Date(dateNow);
|
||||
discountExpirationDate.setDate(discountExpirationDate.getDate() + 6);
|
||||
buildAndRender({
|
||||
accessExpiration: {
|
||||
expirationDate: accessExpirationDate.toString(),
|
||||
},
|
||||
contentTypeGatingEnabled: true,
|
||||
offer: {
|
||||
expirationDate: discountExpirationDate.toString(),
|
||||
percentage: 15,
|
||||
code: 'Welcome15',
|
||||
discountedPrice: '$126.65',
|
||||
originalPrice: '$149',
|
||||
upgradeUrl: 'www.exampleUpgradeUrl.com',
|
||||
},
|
||||
});
|
||||
expect(screen.getByRole('heading', { name: '15% First-Time Learner Discount' })).toBeInTheDocument();
|
||||
expect(screen.getByText(/days left/s).textContent).toMatch('6 days left');
|
||||
expect(screen.getByText(/Earn a.*?of completion to showcase on your resumé/s).textContent).toMatch('Earn a verified certificate of completion to showcase on your resumé');
|
||||
expect(screen.getByText(/Unlock your access/s).textContent).toMatch('Unlock your access to all course activities, including graded assignments');
|
||||
expect(screen.getByText(/to course content and materials/s).textContent).toMatch('Full access to course content and materials, even after the course ends');
|
||||
expect(screen.getByText(/Support our.*?at edX/s).textContent).toMatch('Support our mission at edX');
|
||||
expect(screen.getByText(/Upgrade for/).textContent).toMatch('$126.65 ($149)');
|
||||
expect(screen.getByText(/Use code.*?at checkout/s).textContent).toMatch('Use code Welcome15 at checkout');
|
||||
});
|
||||
|
||||
it('renders discount less a week access expiration less than a week properly', async () => {
|
||||
const accessExpirationDate = new Date(dateNow);
|
||||
accessExpirationDate.setDate(accessExpirationDate.getDate() + 5);
|
||||
const discountExpirationDate = new Date(dateNow);
|
||||
discountExpirationDate.setDate(discountExpirationDate.getDate() + 6);
|
||||
buildAndRender({
|
||||
accessExpiration: {
|
||||
expirationDate: accessExpirationDate.toString(),
|
||||
},
|
||||
contentTypeGatingEnabled: true,
|
||||
offer: {
|
||||
expirationDate: discountExpirationDate.toString(),
|
||||
percentage: 15,
|
||||
code: 'Welcome15',
|
||||
discountedPrice: '$126.65',
|
||||
originalPrice: '$149',
|
||||
upgradeUrl: 'www.exampleUpgradeUrl.com',
|
||||
},
|
||||
});
|
||||
expect(screen.getByRole('heading', { name: 'Course Access Expiration' })).toBeInTheDocument();
|
||||
expect(screen.getByText('5 days left')).toBeInTheDocument(); // setting the time to 12 will mean that it's slightly less than 12
|
||||
expect(screen.getByText(/You will lose all access to this course.*?on/s).textContent).toMatch('You will lose all access to this course, including any progress, on April 18.');
|
||||
expect(screen.getByText(/Upgrading your course enables you/s).textContent).toMatch('Upgrading your course enables you to pursue a verified certificate and unlocks numerous features. Learn more about the benefits of upgrading.');
|
||||
expect(screen.getByText(/Upgrade for/).textContent).toMatch('$126.65 ($149)');
|
||||
expect(screen.getByText(/Use code.*?at checkout/s).textContent).toMatch('Use code Welcome15 at checkout');
|
||||
});
|
||||
|
||||
it('renders past access expiration message properly', async () => {
|
||||
const expirationDate = new Date(dateNow);
|
||||
expirationDate.setDate(expirationDate.getDate() - 1);
|
||||
buildAndRender({
|
||||
contentTypeGatingEnabled: true,
|
||||
accessExpiration: {
|
||||
expirationDate: expirationDate.toString(),
|
||||
},
|
||||
});
|
||||
expect(screen.getByRole('heading', { name: 'Course Access Expiration' })).toBeInTheDocument();
|
||||
expect(screen.getByText(/The upgrade deadline/s).textContent).toMatch('The upgrade deadline for this course passed');
|
||||
expect(screen.getByText(/To upgrade/s).textContent).toMatch('To upgrade, enroll in the next available session');
|
||||
expect(screen.getByRole('button', { name: 'View Course Details' })).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('sends course details click info to segment if past access expiration', async () => {
|
||||
const expirationDate = new Date(dateNow);
|
||||
expirationDate.setDate(expirationDate.getDate() - 1);
|
||||
sendTrackEvent.mockClear();
|
||||
buildAndRender({
|
||||
pageName: 'test',
|
||||
contentTypeGatingEnabled: true,
|
||||
accessExpiration: {
|
||||
expirationDate: expirationDate.toString(),
|
||||
},
|
||||
});
|
||||
|
||||
const courseDetailsLink = await waitFor(() => screen.queryByRole('button', { name: 'View Course Details' }));
|
||||
fireEvent.click(courseDetailsLink);
|
||||
expect(sendTrackEvent).toHaveBeenCalledTimes(2);
|
||||
expect(sendTrackEvent).toHaveBeenNthCalledWith(2, 'edx.bi.ecommerce.upgrade_notification.past_expiration.button_clicked', {
|
||||
org_key: 'edX',
|
||||
courserun_key: 'course-v1:edX+DemoX+Demo_Course',
|
||||
linkCategory: 'upgrade_notification',
|
||||
linkName: 'test_course_details',
|
||||
linkType: 'button',
|
||||
pageName: 'test',
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -331,9 +331,13 @@
|
||||
max-width: 640px;
|
||||
margin: 0 auto;
|
||||
|
||||
@media (max-width: -1 + map-get($grid-breakpoints, "sm")) {
|
||||
flex-direction: column;
|
||||
gap: $spacer;
|
||||
}
|
||||
|
||||
.previous-button,
|
||||
.next-button {
|
||||
white-space: nowrap;
|
||||
border-radius: 4px;
|
||||
|
||||
&:focus:before {
|
||||
@@ -343,10 +347,31 @@
|
||||
|
||||
.next-button {
|
||||
flex-basis: 75%;
|
||||
|
||||
@media (max-width: -1 + map-get($grid-breakpoints, "sm")) {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.previous-button {
|
||||
flex-basis: 25%;
|
||||
|
||||
@media (max-width: -1 + map-get($grid-breakpoints, "sm")) {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.top-unit-navigation {
|
||||
max-width: 100%;
|
||||
justify-content: flex-end;
|
||||
|
||||
.next-button,
|
||||
.previous-button {
|
||||
@media (min-width: map-get($grid-breakpoints, "md")) {
|
||||
flex-basis: auto;
|
||||
min-width: 8rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -408,13 +433,10 @@
|
||||
// Import component-specific sass files
|
||||
@import "courseware/course/celebration/CelebrationModal.scss";
|
||||
@import "courseware/course/sidebar/sidebars/notifications/NotificationIcon.scss";
|
||||
@import "courseware/course/sequence/lock-paywall/LockPaywall.scss";
|
||||
@import "shared/streak-celebration/StreakCelebrationModal.scss";
|
||||
@import "courseware/course/content-tools/calculator/calculator.scss";
|
||||
@import "courseware/course/content-tools/contentTools.scss";
|
||||
@import "course-home/dates-tab/timeline/Day.scss";
|
||||
@import "generic/upgrade-notification/UpgradeNotification.scss";
|
||||
@import "generic/upsell-bullets/UpsellBullets.scss";
|
||||
@import "course-home/outline-tab/widgets/ProctoringInfoPanel.scss";
|
||||
@import "course-home/outline-tab/widgets/FlagButton.scss";
|
||||
@import "course-home/progress-tab/course-completion/CompletionDonutChart.scss";
|
||||
|
||||
50
src/plugin-slots/FooterSlot/README.md
Normal file
50
src/plugin-slots/FooterSlot/README.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# Footer Slot
|
||||
|
||||
### Slot ID: `footer_slot`
|
||||
|
||||
## Description
|
||||
|
||||
This slot is used to replace/modify/hide the footer.
|
||||
|
||||
The implementation of the `FooterSlot` component lives in [the `frontend-slot-footer` repository](https://github.com/openedx/frontend-slot-footer/).
|
||||
|
||||
## Example
|
||||
|
||||
The following `env.config.jsx` will replace the default footer.
|
||||
|
||||

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

|
||||
|
||||
```jsx
|
||||
import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
|
||||
|
||||
const config = {
|
||||
pluginSlots: {
|
||||
footer_slot: {
|
||||
plugins: [
|
||||
{
|
||||
// Hide the default footer
|
||||
op: PLUGIN_OPERATIONS.Hide,
|
||||
widgetId: 'default_contents',
|
||||
},
|
||||
{
|
||||
// Insert a custom footer
|
||||
op: PLUGIN_OPERATIONS.Insert,
|
||||
widget: {
|
||||
id: 'custom_footer',
|
||||
type: DIRECT_PLUGIN,
|
||||
RenderWidget: () => (
|
||||
<h1 style={{textAlign: 'center'}}>🦶</h1>
|
||||
),
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default config;
|
||||
```
|
||||
BIN
src/plugin-slots/FooterSlot/images/custom_footer.png
Normal file
BIN
src/plugin-slots/FooterSlot/images/custom_footer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
BIN
src/plugin-slots/FooterSlot/images/default_footer.png
Normal file
BIN
src/plugin-slots/FooterSlot/images/default_footer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.1 KiB |
5
src/plugin-slots/README.md
Normal file
5
src/plugin-slots/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# `frontend-app-learning` Plugin Slots
|
||||
|
||||
* [`footer_slot`](./FooterSlot/)
|
||||
* [`sequence_container_slot`](./SequenceContainerSlot/)
|
||||
* [`unit_title_slot`](./UnitTitleSlot/)
|
||||
45
src/plugin-slots/SequenceContainerSlot/README.md
Normal file
45
src/plugin-slots/SequenceContainerSlot/README.md
Normal file
@@ -0,0 +1,45 @@
|
||||
# Sequence Container Slot
|
||||
|
||||
### Slot ID: `sequence_container_slot`
|
||||
### Props:
|
||||
* `courseId`
|
||||
* `unitId`
|
||||
|
||||
## Description
|
||||
|
||||
This slot is used for adding content after the Sequence content section.
|
||||
|
||||
## Example
|
||||
|
||||
The following `env.config.jsx` will render the `course_id` and `unit_id` of the course as `<p>` elements in a `<div>`.
|
||||
|
||||

|
||||
|
||||
```js
|
||||
import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
|
||||
|
||||
const config = {
|
||||
pluginSlots: {
|
||||
sequence_container_slot: {
|
||||
plugins: [
|
||||
{
|
||||
// Insert custom content after sequence content
|
||||
op: PLUGIN_OPERATIONS.Insert,
|
||||
widget: {
|
||||
id: 'custom_sequence_container_content',
|
||||
type: DIRECT_PLUGIN,
|
||||
RenderWidget: ({courseId, unitId}) => (
|
||||
<div>
|
||||
<p>📚: {courseId}</p>
|
||||
<p>📙: {unitId}</p>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default config;
|
||||
```
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 40 KiB |
23
src/plugin-slots/SequenceContainerSlot/index.jsx
Normal file
23
src/plugin-slots/SequenceContainerSlot/index.jsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PluginSlot } from '@openedx/frontend-plugin-framework';
|
||||
|
||||
const SequenceContainerSlot = ({ courseId, unitId }) => (
|
||||
<PluginSlot
|
||||
id="sequence_container_slot"
|
||||
pluginProps={{
|
||||
courseId,
|
||||
unitId,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
SequenceContainerSlot.propTypes = {
|
||||
courseId: PropTypes.string.isRequired,
|
||||
unitId: PropTypes.string,
|
||||
};
|
||||
|
||||
SequenceContainerSlot.defaultProps = {
|
||||
unitId: null,
|
||||
};
|
||||
|
||||
export default SequenceContainerSlot;
|
||||
47
src/plugin-slots/UnitTitleSlot/README.md
Normal file
47
src/plugin-slots/UnitTitleSlot/README.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# Unit Title Slot
|
||||
|
||||
### Slot ID: `unit_title_slot`
|
||||
### Props:
|
||||
* `courseId`
|
||||
* `unitId`
|
||||
* `unitTitle`
|
||||
|
||||
## Description
|
||||
|
||||
This slot is used for adding content after the Unit title.
|
||||
|
||||
## Example
|
||||
|
||||
The following `env.config.jsx` will render the `course_id`, `unit_id` and `unitTitle` of the course as `<p>` elements.
|
||||
|
||||

|
||||
|
||||
```js
|
||||
import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
|
||||
|
||||
const config = {
|
||||
pluginSlots: {
|
||||
unit_title_slot: {
|
||||
plugins: [
|
||||
{
|
||||
// Insert custom content after unit title
|
||||
op: PLUGIN_OPERATIONS.Insert,
|
||||
widget: {
|
||||
id: 'custom_unit_title_content',
|
||||
type: DIRECT_PLUGIN,
|
||||
RenderWidget: ({courseId, unitId, unitTitle}) => (
|
||||
<>
|
||||
<p>📚: {courseId}</p>
|
||||
<p>📙: {unitId}</p>
|
||||
<p>📙: {unitTitle}</p>
|
||||
</>
|
||||
),
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
export default config;
|
||||
```
|
||||
BIN
src/plugin-slots/UnitTitleSlot/images/post_unit_title.png
Normal file
BIN
src/plugin-slots/UnitTitleSlot/images/post_unit_title.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 48 KiB |
21
src/plugin-slots/UnitTitleSlot/index.jsx
Normal file
21
src/plugin-slots/UnitTitleSlot/index.jsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import { PluginSlot } from '@openedx/frontend-plugin-framework';
|
||||
|
||||
const UnitTitleSlot = ({ courseId, unitId, unitTitle }) => (
|
||||
<PluginSlot
|
||||
id="unit_title_slot"
|
||||
pluginProps={{
|
||||
courseId,
|
||||
unitId,
|
||||
unitTitle,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
UnitTitleSlot.propTypes = {
|
||||
courseId: PropTypes.string.isRequired,
|
||||
unitId: PropTypes.string.isRequired,
|
||||
unitTitle: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default UnitTitleSlot;
|
||||
@@ -29,11 +29,12 @@ import { getCourseOutlineStructure } from './courseware/data/thunks';
|
||||
import { appendBrowserTimezoneToUrl, executeThunk } from './utils';
|
||||
import buildSimpleCourseAndSequenceMetadata from './courseware/data/__factories__/sequenceMetadata.factory';
|
||||
import { buildOutlineFromBlocks } from './courseware/data/__factories__/learningSequencesOutline.factory';
|
||||
import MockedPluginSlot from './tests/MockedPluginSlot';
|
||||
|
||||
jest.mock('@openedx/frontend-plugin-framework', () => ({
|
||||
...jest.requireActual('@openedx/frontend-plugin-framework'),
|
||||
Plugin: () => 'Plugin',
|
||||
PluginSlot: () => 'PluginSlot',
|
||||
PluginSlot: MockedPluginSlot,
|
||||
}));
|
||||
|
||||
jest.mock('@src/generic/plugin-store', () => ({
|
||||
|
||||
@@ -4,9 +4,9 @@ import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
|
||||
import Footer from '@edx/frontend-component-footer';
|
||||
import { Toast } from '@openedx/paragon';
|
||||
import { LearningHeader as Header } from '@edx/frontend-component-header';
|
||||
import FooterSlot from '@openedx/frontend-slot-footer';
|
||||
import PageLoading from '../generic/PageLoading';
|
||||
import { getAccessDeniedRedirectUrl } from '../shared/access';
|
||||
import { useModel } from '../generic/model-store';
|
||||
@@ -80,7 +80,7 @@ const TabPage = ({ intl, ...props }) => {
|
||||
{intl.formatMessage(messages.failure)}
|
||||
</p>
|
||||
)}
|
||||
<Footer />
|
||||
<FooterSlot />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
26
src/tests/MockedPluginSlot.jsx
Normal file
26
src/tests/MockedPluginSlot.jsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const MockedPluginSlot = ({ children, id }) => (
|
||||
<div data-testid={id}>
|
||||
PluginSlot_{id}
|
||||
{ children && <div>{children}</div> }
|
||||
</div>
|
||||
);
|
||||
|
||||
MockedPluginSlot.displayName = 'PluginSlot';
|
||||
|
||||
MockedPluginSlot.propTypes = {
|
||||
children: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(PropTypes.node),
|
||||
PropTypes.node,
|
||||
]),
|
||||
id: PropTypes.string,
|
||||
};
|
||||
|
||||
MockedPluginSlot.defaultProps = {
|
||||
children: undefined,
|
||||
id: undefined,
|
||||
};
|
||||
|
||||
export default MockedPluginSlot;
|
||||
43
src/tests/MockedPluginSlot.test.jsx
Normal file
43
src/tests/MockedPluginSlot.test.jsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import MockedPluginSlot from './MockedPluginSlot';
|
||||
|
||||
describe('MockedPluginSlot', () => {
|
||||
it('renders mock plugin with "PluginSlot" text', () => {
|
||||
render(<MockedPluginSlot id="test_plugin" />);
|
||||
|
||||
const component = screen.getByText('PluginSlot_test_plugin');
|
||||
expect(component).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders as the slot children directly if there is content within', () => {
|
||||
render(
|
||||
<div role="article">
|
||||
<MockedPluginSlot>
|
||||
<q role="note">How much wood could a woodchuck chuck if a woodchuck could chuck wood?</q>
|
||||
</MockedPluginSlot>
|
||||
</div>,
|
||||
);
|
||||
|
||||
const component = screen.getByRole('article');
|
||||
expect(component).toBeInTheDocument();
|
||||
|
||||
// Direct children
|
||||
const quote = component.querySelector(':scope > q');
|
||||
expect(quote.getAttribute('role')).toBe('note');
|
||||
});
|
||||
|
||||
it('renders mock plugin with a data-testid ', () => {
|
||||
render(
|
||||
<MockedPluginSlot id="guybrush">
|
||||
<q role="note">I am selling these fine leather jackets.</q>
|
||||
</MockedPluginSlot>,
|
||||
);
|
||||
|
||||
const component = screen.getByTestId('guybrush');
|
||||
expect(component).toBeInTheDocument();
|
||||
|
||||
const quote = component.querySelector('[role=note]');
|
||||
expect(quote).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user