Compare commits
24 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d46de8fe3 | ||
|
|
8341f17d46 | ||
|
|
d7c3e5a687 | ||
|
|
07b1c5bde1 | ||
|
|
5512faa9b0 | ||
|
|
48c49fe0b2 | ||
|
|
8c7778218b | ||
|
|
0dedbbd589 | ||
|
|
ef0b101fea | ||
|
|
edb22316b8 | ||
|
|
227a97afa1 | ||
|
|
d01486e5f7 | ||
|
|
a58f1eaf19 | ||
|
|
a5024c3fde | ||
|
|
d7be18e717 | ||
|
|
5e405da37e | ||
|
|
901f39f42c | ||
|
|
346a636b76 | ||
|
|
34dcc88880 | ||
|
|
a229c34535 | ||
|
|
5d7b4fecf4 | ||
|
|
f04130a7c6 | ||
|
|
cb7774b325 | ||
|
|
3e4eb21d8c |
5
.github/workflows/ci.yml
vendored
5
.github/workflows/ci.yml
vendored
@@ -9,9 +9,6 @@ on:
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node: [18, 20]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -20,7 +17,7 @@ jobs:
|
||||
- name: Setup Nodejs
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
node-version-file: '.nvmrc'
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Validate package-lock.json changes
|
||||
|
||||
14
catalog-info.yaml
Normal file
14
catalog-info.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
# This file records information about this repo. Its use is described in OEP-55:
|
||||
# https://open-edx-proposals.readthedocs.io/en/latest/processes/oep-0055-proc-project-maintainers.html
|
||||
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: "frontend-component-header"
|
||||
description: "A generic header for the Open edX micro-frontend applications."
|
||||
annotations:
|
||||
openedx.org/arch-interest-groups: ""
|
||||
spec:
|
||||
owner: group:committers-frontend
|
||||
type: "library"
|
||||
lifecycle: "production"
|
||||
@@ -1,8 +0,0 @@
|
||||
# openedx.yaml
|
||||
|
||||
---
|
||||
owner: edx/fedx-team
|
||||
tags:
|
||||
- library
|
||||
- component
|
||||
- react
|
||||
428
package-lock.json
generated
428
package-lock.json
generated
@@ -25,10 +25,10 @@
|
||||
"devDependencies": {
|
||||
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
|
||||
"@edx/browserslist-config": "^1.1.1",
|
||||
"@edx/frontend-platform": "8.1.2",
|
||||
"@edx/frontend-platform": "8.3.1",
|
||||
"@edx/reactifex": "^2.1.1",
|
||||
"@openedx/frontend-build": "14.1.5",
|
||||
"@openedx/paragon": "22.9.0",
|
||||
"@openedx/frontend-build": "14.2.2",
|
||||
"@openedx/paragon": "22.16.0",
|
||||
"@testing-library/dom": "10.4.0",
|
||||
"@testing-library/jest-dom": "5.17.0",
|
||||
"@testing-library/react": "10.4.9",
|
||||
@@ -39,17 +39,18 @@
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"react-redux": "7.2.9",
|
||||
"react-router-dom": "6.27.0",
|
||||
"react-router-dom": "6.28.1",
|
||||
"react-test-renderer": "17.0.2",
|
||||
"redux": "4.2.1",
|
||||
"redux-saga": "1.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@edx/frontend-platform": "^7.0.0 || ^8.0.0",
|
||||
"@openedx/paragon": ">= 21.5.7 < 23.0.0",
|
||||
"@openedx/paragon": "^22.0.0",
|
||||
"prop-types": "^15.5.10",
|
||||
"react": "^16.9.0 || ^17.0.0",
|
||||
"react-dom": "^16.9.0 || ^17.0.0"
|
||||
"react-dom": "^16.9.0 || ^17.0.0",
|
||||
"react-router-dom": "^6.14.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@adobe/css-tools": {
|
||||
@@ -2026,15 +2027,17 @@
|
||||
"integrity": "sha512-Dn9CtpC8fovh++Xi4NF5NJoeR9yU2yXZnV9IujxIyGd/dn0Phq5t6dzJVfupwq09mpDnzJv7egA8Znz/3ljO+w=="
|
||||
},
|
||||
"node_modules/@edx/browserslist-config": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/browserslist-config/-/browserslist-config-1.2.0.tgz",
|
||||
"integrity": "sha512-T1+6P52Yx7SMkmoIr4O0Q3m/DyRdrLTJbv1xVijdRLFEq1hqdafEs+Ln1423U5LSkTePb9AOkEtL1G0RZLFl1w==",
|
||||
"dev": true
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/browserslist-config/-/browserslist-config-1.5.0.tgz",
|
||||
"integrity": "sha512-d2ggwi5j4DOBJOwhWZxBWQSDR0DhT4ke/1PbzRauICdFkuOyax+PsFjK8GUh443K2OaQpy9PGfiCzZ1Yg37AUA==",
|
||||
"dev": true,
|
||||
"license": "AGPL-3.0"
|
||||
},
|
||||
"node_modules/@edx/eslint-config": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/eslint-config/-/eslint-config-4.2.0.tgz",
|
||||
"integrity": "sha512-2wuIw49uyj6gRwS74qJ8WhBU+X2FOP4uot40sthIC4YU9qCM7WJOcOuAhkRPP1FvZKd3UQH3gZM7eJ85xzDBqA==",
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/eslint-config/-/eslint-config-4.3.0.tgz",
|
||||
"integrity": "sha512-4W9wFG4ALr3xocakCsncgJbK67RHfSmDwHDXKHReFtjxl/FRkxhS6qayz189oChqfANieeV3zRCLaq44bLf+/A==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||
"@typescript-eslint/parser": "^5.62.0",
|
||||
@@ -2048,16 +2051,16 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-platform": {
|
||||
"version": "8.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-8.1.2.tgz",
|
||||
"integrity": "sha512-xkSpzoxSp3bC4rrOHkHb/tj5bZQjZrOe5t3/kNzJVqqvziFGRU0fTAhh+Pv0aHboDAS8mAEhqZrJFpdeAFe1Tg==",
|
||||
"version": "8.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-8.3.1.tgz",
|
||||
"integrity": "sha512-wDCCFtbWdxk8N/ExIGd/etyidF9YewaRdyGix2nSTujdfKZU/+2cObRxGkardGHREQDGrvqCPW5tmcSNedAIIg==",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@cospired/i18n-iso-languages": "4.2.0",
|
||||
"@formatjs/intl-pluralrules": "4.3.3",
|
||||
"@formatjs/intl-relativetimeformat": "10.0.1",
|
||||
"axios": "1.6.7",
|
||||
"axios-cache-interceptor": "1.3.2",
|
||||
"axios": "1.8.2",
|
||||
"axios-cache-interceptor": "1.6.2",
|
||||
"form-urlencoded": "4.1.4",
|
||||
"glob": "7.2.3",
|
||||
"history": "4.10.1",
|
||||
@@ -2069,8 +2072,8 @@
|
||||
"lodash.memoize": "4.1.2",
|
||||
"lodash.merge": "4.6.2",
|
||||
"lodash.snakecase": "4.1.1",
|
||||
"pubsub-js": "1.9.4",
|
||||
"react-intl": "6.7.0",
|
||||
"pubsub-js": "1.9.5",
|
||||
"react-intl": "6.8.9",
|
||||
"universal-cookie": "4.0.4"
|
||||
},
|
||||
"bin": {
|
||||
@@ -2079,26 +2082,15 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@openedx/frontend-build": ">= 14.0.0",
|
||||
"@openedx/paragon": ">= 21.5.7 < 23.0.0",
|
||||
"@openedx/paragon": ">= 21.5.7 < 24.0.0",
|
||||
"prop-types": ">=15.7.2 <16.0.0",
|
||||
"react": "^16.9.0 || ^17.0.0",
|
||||
"react-dom": "^16.9.0 || ^17.0.0",
|
||||
"react": "^16.9.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0",
|
||||
"react-redux": "^7.1.1 || ^8.1.1",
|
||||
"react-router-dom": "^6.0.0",
|
||||
"redux": "^4.0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-platform/node_modules/axios": {
|
||||
"version": "1.6.7",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz",
|
||||
"integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.4",
|
||||
"form-data": "^4.0.0",
|
||||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/new-relic-source-map-webpack-plugin": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/new-relic-source-map-webpack-plugin/-/new-relic-source-map-webpack-plugin-2.1.0.tgz",
|
||||
@@ -2298,11 +2290,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/fast-memoize": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.0.tgz",
|
||||
"integrity": "sha512-hnk/nY8FyrL5YxwP9e4r9dqeM6cAbo8PeU9UjyXojZMNvVad2Z06FAVHyR3Ecw6fza+0GH7vdJgiKIVXTMbSBA==",
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.3.tgz",
|
||||
"integrity": "sha512-3jeJ+HyOfu8osl3GNSL4vVHUuWFXR03Iz9jjgI7RwjG6ysu/Ymdr0JRCPHfF5yGbTE6JCrd63EpvX1/WybYRbA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/icu-messageformat-parser": {
|
||||
@@ -2359,17 +2352,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/intl": {
|
||||
"version": "2.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.10.5.tgz",
|
||||
"integrity": "sha512-f9qPNNgLrh2KvoFvHGIfcPTmNGbyy7lyyV4/P6JioDqtTE7Akdmgt+ZzVndr+yMLZnssUShyTMXxM/6aV9eVuQ==",
|
||||
"version": "2.10.15",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.10.15.tgz",
|
||||
"integrity": "sha512-i6+xVqT+6KCz7nBfk4ybMXmbKO36tKvbMKtgFz9KV+8idYFyFbfwKooYk8kGjyA5+T5f1kEPQM5IDLXucTAQ9g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": "2.0.0",
|
||||
"@formatjs/fast-memoize": "2.2.0",
|
||||
"@formatjs/icu-messageformat-parser": "2.7.8",
|
||||
"@formatjs/intl-displaynames": "6.6.8",
|
||||
"@formatjs/intl-listformat": "7.5.7",
|
||||
"intl-messageformat": "10.5.14",
|
||||
"tslib": "^2.4.0"
|
||||
"@formatjs/ecma402-abstract": "2.2.4",
|
||||
"@formatjs/fast-memoize": "2.2.3",
|
||||
"@formatjs/icu-messageformat-parser": "2.9.4",
|
||||
"@formatjs/intl-displaynames": "6.8.5",
|
||||
"@formatjs/intl-listformat": "7.7.5",
|
||||
"intl-messageformat": "10.7.7",
|
||||
"tslib": "2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^4.7 || 5"
|
||||
@@ -2381,57 +2375,65 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/intl-displaynames": {
|
||||
"version": "6.6.8",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-6.6.8.tgz",
|
||||
"integrity": "sha512-Lgx6n5KxN16B3Pb05z3NLEBQkGoXnGjkTBNCZI+Cn17YjHJ3fhCeEJJUqRlIZmJdmaXQhjcQVDp6WIiNeRYT5g==",
|
||||
"version": "6.8.5",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-6.8.5.tgz",
|
||||
"integrity": "sha512-85b+GdAKCsleS6cqVxf/Aw/uBd+20EM0wDpgaxzHo3RIR3bxF4xCJqH/Grbzx8CXurTgDDZHPdPdwJC+May41w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": "2.0.0",
|
||||
"@formatjs/intl-localematcher": "0.5.4",
|
||||
"tslib": "^2.4.0"
|
||||
"@formatjs/ecma402-abstract": "2.2.4",
|
||||
"@formatjs/intl-localematcher": "0.5.8",
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/intl-displaynames/node_modules/@formatjs/ecma402-abstract": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz",
|
||||
"integrity": "sha512-rRqXOqdFmk7RYvj4khklyqzcfQl9vEL/usogncBHRZfZBDOwMGuSRNFl02fu5KGHXdbinju+YXyuR+Nk8xlr/g==",
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.4.tgz",
|
||||
"integrity": "sha512-lFyiQDVvSbQOpU+WFd//ILolGj4UgA/qXrKeZxdV14uKiAUiPAtX6XAn7WBCRi7Mx6I7EybM9E5yYn4BIpZWYg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/intl-localematcher": "0.5.4",
|
||||
"tslib": "^2.4.0"
|
||||
"@formatjs/fast-memoize": "2.2.3",
|
||||
"@formatjs/intl-localematcher": "0.5.8",
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/intl-displaynames/node_modules/@formatjs/intl-localematcher": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz",
|
||||
"integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==",
|
||||
"version": "0.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.8.tgz",
|
||||
"integrity": "sha512-I+WDNWWJFZie+jkfkiK5Mp4hEDyRSEvmyfYadflOno/mmKJKcB17fEpEH0oJu/OWhhCJ8kJBDz2YMd/6cDl7Mg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/intl-listformat": {
|
||||
"version": "7.5.7",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.5.7.tgz",
|
||||
"integrity": "sha512-MG2TSChQJQT9f7Rlv+eXwUFiG24mKSzmF144PLb8m8OixyXqn4+YWU+5wZracZGCgVTVmx8viCf7IH3QXoiB2g==",
|
||||
"version": "7.7.5",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-7.7.5.tgz",
|
||||
"integrity": "sha512-Wzes10SMNeYgnxYiKsda4rnHP3Q3II4XT2tZyOgnH5fWuHDtIkceuWlRQNsvrI3uiwP4hLqp2XdQTCsfkhXulg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": "2.0.0",
|
||||
"@formatjs/intl-localematcher": "0.5.4",
|
||||
"tslib": "^2.4.0"
|
||||
"@formatjs/ecma402-abstract": "2.2.4",
|
||||
"@formatjs/intl-localematcher": "0.5.8",
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/intl-listformat/node_modules/@formatjs/ecma402-abstract": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz",
|
||||
"integrity": "sha512-rRqXOqdFmk7RYvj4khklyqzcfQl9vEL/usogncBHRZfZBDOwMGuSRNFl02fu5KGHXdbinju+YXyuR+Nk8xlr/g==",
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.4.tgz",
|
||||
"integrity": "sha512-lFyiQDVvSbQOpU+WFd//ILolGj4UgA/qXrKeZxdV14uKiAUiPAtX6XAn7WBCRi7Mx6I7EybM9E5yYn4BIpZWYg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/intl-localematcher": "0.5.4",
|
||||
"tslib": "^2.4.0"
|
||||
"@formatjs/fast-memoize": "2.2.3",
|
||||
"@formatjs/intl-localematcher": "0.5.8",
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/intl-listformat/node_modules/@formatjs/intl-localematcher": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz",
|
||||
"integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==",
|
||||
"version": "0.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.8.tgz",
|
||||
"integrity": "sha512-I+WDNWWJFZie+jkfkiK5Mp4hEDyRSEvmyfYadflOno/mmKJKcB17fEpEH0oJu/OWhhCJ8kJBDz2YMd/6cDl7Mg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/intl-localematcher": {
|
||||
@@ -2463,20 +2465,44 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/intl/node_modules/@formatjs/ecma402-abstract": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz",
|
||||
"integrity": "sha512-rRqXOqdFmk7RYvj4khklyqzcfQl9vEL/usogncBHRZfZBDOwMGuSRNFl02fu5KGHXdbinju+YXyuR+Nk8xlr/g==",
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.4.tgz",
|
||||
"integrity": "sha512-lFyiQDVvSbQOpU+WFd//ILolGj4UgA/qXrKeZxdV14uKiAUiPAtX6XAn7WBCRi7Mx6I7EybM9E5yYn4BIpZWYg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/intl-localematcher": "0.5.4",
|
||||
"tslib": "^2.4.0"
|
||||
"@formatjs/fast-memoize": "2.2.3",
|
||||
"@formatjs/intl-localematcher": "0.5.8",
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/intl/node_modules/@formatjs/icu-messageformat-parser": {
|
||||
"version": "2.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.9.4.tgz",
|
||||
"integrity": "sha512-Tbvp5a9IWuxUcpWNIW6GlMQYEc4rwNHR259uUFoKWNN1jM9obf9Ul0e+7r7MvFOBNcN+13K7NuKCKqQiAn1QEg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": "2.2.4",
|
||||
"@formatjs/icu-skeleton-parser": "1.8.8",
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/intl/node_modules/@formatjs/icu-skeleton-parser": {
|
||||
"version": "1.8.8",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.8.tgz",
|
||||
"integrity": "sha512-vHwK3piXwamFcx5YQdCdJxUQ1WdTl6ANclt5xba5zLGDv5Bsur7qz8AD7BevaKxITwpgDeU0u8My3AIibW9ywA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": "2.2.4",
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/intl/node_modules/@formatjs/intl-localematcher": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz",
|
||||
"integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==",
|
||||
"version": "0.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.8.tgz",
|
||||
"integrity": "sha512-I+WDNWWJFZie+jkfkiK5Mp4hEDyRSEvmyfYadflOno/mmKJKcB17fEpEH0oJu/OWhhCJ8kJBDz2YMd/6cDl7Mg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/@formatjs/ts-transformer": {
|
||||
@@ -3184,9 +3210,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-build": {
|
||||
"version": "14.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@openedx/frontend-build/-/frontend-build-14.1.5.tgz",
|
||||
"integrity": "sha512-QEdl55jNitdQL7RDAuX/EgfxsyBeEZfW3fc9Df4Py5KY6NKjRE7wNLeBMxYCFagEgXwaR1Btiw5NxzByAdlnfg==",
|
||||
"version": "14.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@openedx/frontend-build/-/frontend-build-14.2.2.tgz",
|
||||
"integrity": "sha512-RLhRoYE8+9A4YknEZyZwLreXeUV4u+QKQXLFf07H8YkW2U6A+8f/ANMDb6Vqw4NsQ/s6eg8SjzpWKwX1FO7qwg==",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@babel/cli": "7.24.8",
|
||||
@@ -3197,7 +3223,7 @@
|
||||
"@babel/plugin-syntax-dynamic-import": "7.8.3",
|
||||
"@babel/preset-env": "7.24.8",
|
||||
"@babel/preset-react": "7.24.7",
|
||||
"@edx/eslint-config": "4.2.0",
|
||||
"@edx/eslint-config": "^4.3.0",
|
||||
"@edx/new-relic-source-map-webpack-plugin": "2.1.0",
|
||||
"@edx/typescript-config": "1.1.0",
|
||||
"@formatjs/cli": "^6.0.3",
|
||||
@@ -3229,14 +3255,14 @@
|
||||
"eslint-plugin-react-hooks": "4.6.0",
|
||||
"express": "^4.18.2",
|
||||
"file-loader": "6.2.0",
|
||||
"html-webpack-plugin": "5.6.0",
|
||||
"html-webpack-plugin": "5.6.3",
|
||||
"identity-obj-proxy": "3.0.0",
|
||||
"image-minimizer-webpack-plugin": "3.8.3",
|
||||
"jest": "29.6.1",
|
||||
"jest-environment-jsdom": "29.6.1",
|
||||
"mini-css-extract-plugin": "1.6.2",
|
||||
"parse5": "7.1.2",
|
||||
"postcss": "8.4.47",
|
||||
"postcss": "8.4.49",
|
||||
"postcss-custom-media": "10.0.8",
|
||||
"postcss-loader": "7.3.4",
|
||||
"postcss-rtlcss": "5.1.2",
|
||||
@@ -3319,9 +3345,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@openedx/frontend-plugin-framework/-/frontend-plugin-framework-1.3.0.tgz",
|
||||
"integrity": "sha512-qLtX/4HIuWXiIhBdtBuL6mPVbV2un0rsFYx3I5+3tIUf7+T7WRq81a6JHU5QGyAmZy9dfiv7QwbqwiEQOVXVuQ==",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@openedx/frontend-plugin-framework/-/frontend-plugin-framework-1.4.1.tgz",
|
||||
"integrity": "sha512-8lVvq+kqb4CsPtD2CIf5nL+Ded6r+dTM/0DIwxCuoUTh4i5aCBwPY3gnKsfa1OS9IEJjeSgiMBieH8WRqUiixw==",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
|
||||
"classnames": "^2.3.2",
|
||||
@@ -3340,9 +3367,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/paragon": {
|
||||
"version": "22.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@openedx/paragon/-/paragon-22.9.0.tgz",
|
||||
"integrity": "sha512-r5xD+z64U3phkgT4ooUQaxE/4Rv0D91tpS3kA+mLfOT1vMD8jXIjDZp+/k4BEw4yqWQ8Eyb//ar8xiwL/ugojQ==",
|
||||
"version": "22.16.0",
|
||||
"resolved": "https://registry.npmjs.org/@openedx/paragon/-/paragon-22.16.0.tgz",
|
||||
"integrity": "sha512-6dhspna6rMJexeasq5acrkD+NMHOlVOt/RdraXg87Rqw8iR4XWxD8T0wY21DlYysPRCOZJVMXdc1jsjdI4MjXg==",
|
||||
"license": "Apache-2.0",
|
||||
"workspaces": [
|
||||
"example",
|
||||
@@ -3386,8 +3413,8 @@
|
||||
"paragon": "bin/paragon-scripts.js"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.6 || ^17.0.0",
|
||||
"react-dom": "^16.8.6 || ^17.0.0",
|
||||
"react": "^16.8.6 || ^17 || ^18",
|
||||
"react-dom": "^16.8.6 || ^17 || ^18",
|
||||
"react-intl": "^5.25.1 || ^6.4.0"
|
||||
}
|
||||
},
|
||||
@@ -3563,9 +3590,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@remix-run/router": {
|
||||
"version": "1.20.0",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.20.0.tgz",
|
||||
"integrity": "sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg==",
|
||||
"version": "1.21.0",
|
||||
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.21.0.tgz",
|
||||
"integrity": "sha512-xfSkCAchbdG5PnbrKqFWwia4Bi61nH+wm8wLEqfHDyp7Y3dZzgqS2itV8i4gAq9pC2HsTpwyBC6Ds8VHZ96JlA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@@ -5343,10 +5370,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.7.7",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz",
|
||||
"integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==",
|
||||
"peer": true,
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.8.2.tgz",
|
||||
"integrity": "sha512-ls4GYBm5aig9vWx8AWDSGLpnpDQRtWAfrjU+EuytuODrFBkqesN2RkOQCBzrA1RQNHw1SmRMSDDDSwzNAYQ6Rg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.0",
|
||||
@@ -5354,13 +5381,14 @@
|
||||
}
|
||||
},
|
||||
"node_modules/axios-cache-interceptor": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/axios-cache-interceptor/-/axios-cache-interceptor-1.3.2.tgz",
|
||||
"integrity": "sha512-FNy4/IKvFYVswpPS09j3H9OUzcXSuxQ93wYxCKnogHbjCRE9nDQ/lukgjyuJqMIk3Yao51qQI/zPbMRNQu4JJw==",
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/axios-cache-interceptor/-/axios-cache-interceptor-1.6.2.tgz",
|
||||
"integrity": "sha512-YLbAODIHZZIcD4b3WYFVQOa5W2TY/WnJ6sBHqAg6Z+hx+RVj8/OcjQyRopO6awn7/kOkGL5X9TP16AucnlJ/lw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cache-parser": "^1.2.4",
|
||||
"fast-defer": "^1.1.7",
|
||||
"object-code": "^1.3.0"
|
||||
"cache-parser": "1.2.5",
|
||||
"fast-defer": "1.1.8",
|
||||
"object-code": "1.3.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
@@ -9464,9 +9492,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/html-webpack-plugin": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.0.tgz",
|
||||
"integrity": "sha512-iwaY4wzbe48AfKLZ/Cc8k0L+FKG6oSNRaZ8x5A/T/IVDGyXcbHncM9TdDa93wn0FsSm82FhTKW7f3vS61thXAw==",
|
||||
"version": "5.6.3",
|
||||
"resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.3.tgz",
|
||||
"integrity": "sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/html-minifier-terser": "^6.0.0",
|
||||
"html-minifier-terser": "^6.0.2",
|
||||
@@ -9904,31 +9933,56 @@
|
||||
}
|
||||
},
|
||||
"node_modules/intl-messageformat": {
|
||||
"version": "10.5.14",
|
||||
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.5.14.tgz",
|
||||
"integrity": "sha512-IjC6sI0X7YRjjyVH9aUgdftcmZK7WXdHeil4KwbjDnRWjnVitKpAx3rr6t6di1joFp5188VqKcobOPA6mCLG/w==",
|
||||
"version": "10.7.7",
|
||||
"resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-10.7.7.tgz",
|
||||
"integrity": "sha512-F134jIoeYMro/3I0h08D0Yt4N9o9pjddU/4IIxMMURqbAtI2wu70X8hvG1V48W49zXHXv3RKSF/po+0fDfsGjA==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": "2.0.0",
|
||||
"@formatjs/fast-memoize": "2.2.0",
|
||||
"@formatjs/icu-messageformat-parser": "2.7.8",
|
||||
"tslib": "^2.4.0"
|
||||
"@formatjs/ecma402-abstract": "2.2.4",
|
||||
"@formatjs/fast-memoize": "2.2.3",
|
||||
"@formatjs/icu-messageformat-parser": "2.9.4",
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz",
|
||||
"integrity": "sha512-rRqXOqdFmk7RYvj4khklyqzcfQl9vEL/usogncBHRZfZBDOwMGuSRNFl02fu5KGHXdbinju+YXyuR+Nk8xlr/g==",
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.4.tgz",
|
||||
"integrity": "sha512-lFyiQDVvSbQOpU+WFd//ILolGj4UgA/qXrKeZxdV14uKiAUiPAtX6XAn7WBCRi7Mx6I7EybM9E5yYn4BIpZWYg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/intl-localematcher": "0.5.4",
|
||||
"tslib": "^2.4.0"
|
||||
"@formatjs/fast-memoize": "2.2.3",
|
||||
"@formatjs/intl-localematcher": "0.5.8",
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/intl-messageformat/node_modules/@formatjs/icu-messageformat-parser": {
|
||||
"version": "2.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.9.4.tgz",
|
||||
"integrity": "sha512-Tbvp5a9IWuxUcpWNIW6GlMQYEc4rwNHR259uUFoKWNN1jM9obf9Ul0e+7r7MvFOBNcN+13K7NuKCKqQiAn1QEg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": "2.2.4",
|
||||
"@formatjs/icu-skeleton-parser": "1.8.8",
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/intl-messageformat/node_modules/@formatjs/icu-skeleton-parser": {
|
||||
"version": "1.8.8",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.8.tgz",
|
||||
"integrity": "sha512-vHwK3piXwamFcx5YQdCdJxUQ1WdTl6ANclt5xba5zLGDv5Bsur7qz8AD7BevaKxITwpgDeU0u8My3AIibW9ywA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": "2.2.4",
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/intl-messageformat/node_modules/@formatjs/intl-localematcher": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz",
|
||||
"integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==",
|
||||
"version": "0.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.8.tgz",
|
||||
"integrity": "sha512-I+WDNWWJFZie+jkfkiK5Mp4hEDyRSEvmyfYadflOno/mmKJKcB17fEpEH0oJu/OWhhCJ8kJBDz2YMd/6cDl7Mg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/invariant": {
|
||||
@@ -12627,9 +12681,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/picocolors": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
|
||||
"integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw=="
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
|
||||
"integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/picomatch": {
|
||||
"version": "2.3.1",
|
||||
@@ -12854,9 +12909,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.47",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
|
||||
"integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
|
||||
"version": "8.4.49",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
|
||||
"integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -12871,9 +12926,10 @@
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.7",
|
||||
"picocolors": "^1.1.0",
|
||||
"picocolors": "^1.1.1",
|
||||
"source-map-js": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
@@ -13571,9 +13627,10 @@
|
||||
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
|
||||
},
|
||||
"node_modules/pubsub-js": {
|
||||
"version": "1.9.4",
|
||||
"resolved": "https://registry.npmjs.org/pubsub-js/-/pubsub-js-1.9.4.tgz",
|
||||
"integrity": "sha512-hJYpaDvPH4w8ZX/0Fdf9ma1AwRgU353GfbaVfPjfJQf1KxZ2iHaHl3fAUw1qlJIR5dr4F3RzjGaWohYUEyoh7A=="
|
||||
"version": "1.9.5",
|
||||
"resolved": "https://registry.npmjs.org/pubsub-js/-/pubsub-js-1.9.5.tgz",
|
||||
"integrity": "sha512-5MZ0I9i5JWVO7SizvOviKvZU2qaBbl2KQX150FAA+fJBwYpwOUId7aNygURWSdPzlsA/xZ/InUKXqBbzM0czTA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pump": {
|
||||
"version": "3.0.2",
|
||||
@@ -14038,20 +14095,21 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-intl": {
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.7.0.tgz",
|
||||
"integrity": "sha512-f5QhjuKb+WEqiAbL5hDqUs2+sSRkF0vxkTbJ4A8ompt55XTyOHcrDlCXGq4o73ywFFrpgz+78C9IXegSLlya2A==",
|
||||
"version": "6.8.9",
|
||||
"resolved": "https://registry.npmjs.org/react-intl/-/react-intl-6.8.9.tgz",
|
||||
"integrity": "sha512-TUfj5E7lyUDvz/GtovC9OMh441kBr08rtIbgh3p0R8iF3hVY+V2W9Am7rb8BpJ/29BH1utJOqOOhmvEVh3GfZg==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": "2.0.0",
|
||||
"@formatjs/icu-messageformat-parser": "2.7.8",
|
||||
"@formatjs/intl": "2.10.5",
|
||||
"@formatjs/intl-displaynames": "6.6.8",
|
||||
"@formatjs/intl-listformat": "7.5.7",
|
||||
"@types/hoist-non-react-statics": "^3.3.1",
|
||||
"@formatjs/ecma402-abstract": "2.2.4",
|
||||
"@formatjs/icu-messageformat-parser": "2.9.4",
|
||||
"@formatjs/intl": "2.10.15",
|
||||
"@formatjs/intl-displaynames": "6.8.5",
|
||||
"@formatjs/intl-listformat": "7.7.5",
|
||||
"@types/hoist-non-react-statics": "3",
|
||||
"@types/react": "16 || 17 || 18",
|
||||
"hoist-non-react-statics": "^3.3.2",
|
||||
"intl-messageformat": "10.5.14",
|
||||
"tslib": "^2.4.0"
|
||||
"hoist-non-react-statics": "3",
|
||||
"intl-messageformat": "10.7.7",
|
||||
"tslib": "2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.6.0 || 17 || 18",
|
||||
@@ -14064,20 +14122,44 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-intl/node_modules/@formatjs/ecma402-abstract": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.0.0.tgz",
|
||||
"integrity": "sha512-rRqXOqdFmk7RYvj4khklyqzcfQl9vEL/usogncBHRZfZBDOwMGuSRNFl02fu5KGHXdbinju+YXyuR+Nk8xlr/g==",
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.4.tgz",
|
||||
"integrity": "sha512-lFyiQDVvSbQOpU+WFd//ILolGj4UgA/qXrKeZxdV14uKiAUiPAtX6XAn7WBCRi7Mx6I7EybM9E5yYn4BIpZWYg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/intl-localematcher": "0.5.4",
|
||||
"tslib": "^2.4.0"
|
||||
"@formatjs/fast-memoize": "2.2.3",
|
||||
"@formatjs/intl-localematcher": "0.5.8",
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/react-intl/node_modules/@formatjs/icu-messageformat-parser": {
|
||||
"version": "2.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.9.4.tgz",
|
||||
"integrity": "sha512-Tbvp5a9IWuxUcpWNIW6GlMQYEc4rwNHR259uUFoKWNN1jM9obf9Ul0e+7r7MvFOBNcN+13K7NuKCKqQiAn1QEg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": "2.2.4",
|
||||
"@formatjs/icu-skeleton-parser": "1.8.8",
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/react-intl/node_modules/@formatjs/icu-skeleton-parser": {
|
||||
"version": "1.8.8",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.8.tgz",
|
||||
"integrity": "sha512-vHwK3piXwamFcx5YQdCdJxUQ1WdTl6ANclt5xba5zLGDv5Bsur7qz8AD7BevaKxITwpgDeU0u8My3AIibW9ywA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@formatjs/ecma402-abstract": "2.2.4",
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/react-intl/node_modules/@formatjs/intl-localematcher": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz",
|
||||
"integrity": "sha512-zTwEpWOzZ2CiKcB93BLngUX59hQkuZjT2+SAQEscSm52peDW/getsawMcWF1rGRpMCX6D7nSJA3CzJ8gn13N/g==",
|
||||
"version": "0.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.8.tgz",
|
||||
"integrity": "sha512-I+WDNWWJFZie+jkfkiK5Mp4hEDyRSEvmyfYadflOno/mmKJKcB17fEpEH0oJu/OWhhCJ8kJBDz2YMd/6cDl7Mg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"tslib": "^2.4.0"
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
@@ -14231,12 +14313,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router": {
|
||||
"version": "6.27.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.27.0.tgz",
|
||||
"integrity": "sha512-YA+HGZXz4jaAkVoYBE98VQl+nVzI+cVI2Oj/06F5ZM+0u3TgedN9Y9kmMRo2mnkSK2nCpNQn0DVob4HCsY/WLw==",
|
||||
"version": "6.28.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.28.1.tgz",
|
||||
"integrity": "sha512-2omQTA3rkMljmrvvo6WtewGdVh45SpL9hGiCI9uUrwGGfNFDIvGK4gYJsKlJoNVi6AQZcopSCballL+QGOm7fA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.20.0"
|
||||
"@remix-run/router": "1.21.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
@@ -14246,13 +14328,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-router-dom": {
|
||||
"version": "6.27.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.27.0.tgz",
|
||||
"integrity": "sha512-+bvtFWMC0DgAFrfKXKG9Fc+BcXWRUO1aJIihbB79xaeq0v5UzfvnM5houGUm1Y461WVRcgAQ+Clh5rdb1eCx4g==",
|
||||
"version": "6.28.1",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.28.1.tgz",
|
||||
"integrity": "sha512-YraE27C/RdjcZwl5UCqF/ffXnZDxpJdk9Q6jw38SZHjXs7NNdpViq2l2c7fO7+4uWaEfcwfGCv3RSg4e1By/fQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.20.0",
|
||||
"react-router": "6.27.0"
|
||||
"@remix-run/router": "1.21.0",
|
||||
"react-router": "6.28.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
|
||||
13
package.json
13
package.json
@@ -35,10 +35,10 @@
|
||||
"devDependencies": {
|
||||
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
|
||||
"@edx/browserslist-config": "^1.1.1",
|
||||
"@edx/frontend-platform": "8.1.2",
|
||||
"@edx/frontend-platform": "8.3.1",
|
||||
"@edx/reactifex": "^2.1.1",
|
||||
"@openedx/frontend-build": "14.1.5",
|
||||
"@openedx/paragon": "22.9.0",
|
||||
"@openedx/frontend-build": "14.2.2",
|
||||
"@openedx/paragon": "22.16.0",
|
||||
"@testing-library/dom": "10.4.0",
|
||||
"@testing-library/jest-dom": "5.17.0",
|
||||
"@testing-library/react": "10.4.9",
|
||||
@@ -49,7 +49,7 @@
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"react-redux": "7.2.9",
|
||||
"react-router-dom": "6.27.0",
|
||||
"react-router-dom": "6.28.1",
|
||||
"react-test-renderer": "17.0.2",
|
||||
"redux": "4.2.1",
|
||||
"redux-saga": "1.3.0"
|
||||
@@ -70,9 +70,10 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@edx/frontend-platform": "^7.0.0 || ^8.0.0",
|
||||
"@openedx/paragon": ">= 21.5.7 < 23.0.0",
|
||||
"@openedx/paragon": "^22.0.0",
|
||||
"prop-types": "^15.5.10",
|
||||
"react": "^16.9.0 || ^17.0.0",
|
||||
"react-dom": "^16.9.0 || ^17.0.0"
|
||||
"react-dom": "^16.9.0 || ^17.0.0",
|
||||
"react-router-dom": "^6.14.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,11 @@ const CourseInfoSlot = ({
|
||||
slotOptions={{
|
||||
mergeProps: true,
|
||||
}}
|
||||
pluginProps={{
|
||||
courseOrg,
|
||||
courseNumber,
|
||||
courseTitle,
|
||||
}}
|
||||
>
|
||||
<LearningHeaderCourseInfo
|
||||
courseOrg={courseOrg}
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const BrandNav = ({
|
||||
studioBaseUrl,
|
||||
logo,
|
||||
logoAltText,
|
||||
}) => (
|
||||
<a href={studioBaseUrl}>
|
||||
<Link to={studioBaseUrl}>
|
||||
<img
|
||||
src={logo}
|
||||
alt={logoAltText}
|
||||
className="d-block logo"
|
||||
/>
|
||||
</a>
|
||||
</Link>
|
||||
);
|
||||
|
||||
BrandNav.propTypes = {
|
||||
|
||||
40
src/studio-header/BrandNav.test.jsx
Normal file
40
src/studio-header/BrandNav.test.jsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
import BrandNav from './BrandNav';
|
||||
|
||||
const studioBaseUrl = 'https://example.com/';
|
||||
const logo = 'logo.png';
|
||||
const logoAltText = 'Example Logo';
|
||||
|
||||
const RootWrapper = () => (
|
||||
<MemoryRouter>
|
||||
<BrandNav
|
||||
studioBaseUrl={studioBaseUrl}
|
||||
logo={logo}
|
||||
logoAltText={logoAltText}
|
||||
/>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
describe('BrandNav Component', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders the logo with the correct alt text', () => {
|
||||
render(<RootWrapper />);
|
||||
|
||||
const img = screen.getByAltText(logoAltText);
|
||||
expect(img).toHaveAttribute('src', logo);
|
||||
});
|
||||
|
||||
it('displays a link that navigates to studioBaseUrl', () => {
|
||||
render(<RootWrapper />);
|
||||
|
||||
const link = screen.getByRole('link');
|
||||
expect(link.href).toBe(studioBaseUrl);
|
||||
});
|
||||
});
|
||||
@@ -5,6 +5,8 @@ import {
|
||||
OverlayTrigger,
|
||||
Tooltip,
|
||||
} from '@openedx/paragon';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import messages from './messages';
|
||||
|
||||
const CourseLockUp = ({
|
||||
@@ -23,15 +25,15 @@ const CourseLockUp = ({
|
||||
</Tooltip>
|
||||
)}
|
||||
>
|
||||
<a
|
||||
<Link
|
||||
className="course-title-lockup mr-2"
|
||||
href={outlineLink}
|
||||
to={outlineLink}
|
||||
aria-label={intl.formatMessage(messages['header.label.courseOutline'])}
|
||||
data-testid="course-lock-up-block"
|
||||
>
|
||||
<span className="d-block small m-0 text-gray-800" data-testid="course-org-number">{org} {number}</span>
|
||||
<span className="d-block m-0 font-weight-bold text-gray-800" data-testid="course-title">{title}</span>
|
||||
</a>
|
||||
</Link>
|
||||
</OverlayTrigger>
|
||||
);
|
||||
|
||||
|
||||
58
src/studio-header/CourseLockUp.test.jsx
Normal file
58
src/studio-header/CourseLockUp.test.jsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import React from 'react';
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
import { IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
import CourseLockUp from './CourseLockUp';
|
||||
import messages from './messages';
|
||||
|
||||
const mockProps = {
|
||||
number: '101',
|
||||
org: 'EDX',
|
||||
title: 'Course Title',
|
||||
outlineLink: 'https://example.com/course-outline',
|
||||
};
|
||||
|
||||
const RootWrapper = (props) => (
|
||||
<MemoryRouter>
|
||||
<IntlProvider locale="en" messages={messages}>
|
||||
<CourseLockUp {...props} />
|
||||
</IntlProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
describe('CourseLockUp Component', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders course org, number, and title', () => {
|
||||
render(<RootWrapper {...mockProps} />);
|
||||
|
||||
const courseOrgNumber = screen.getByTestId('course-org-number');
|
||||
const courseTitle = screen.getByTestId('course-title');
|
||||
|
||||
expect(courseOrgNumber).toBeInTheDocument();
|
||||
expect(courseOrgNumber).toHaveTextContent(`${mockProps.org} ${mockProps.number}`);
|
||||
expect(courseTitle).toBeInTheDocument();
|
||||
expect(courseTitle).toHaveTextContent(mockProps.title);
|
||||
});
|
||||
|
||||
it('renders the link with correct aria-label', () => {
|
||||
render(<RootWrapper {...mockProps} />);
|
||||
|
||||
const link = screen.getByTestId('course-lock-up-block');
|
||||
expect(link).toHaveAttribute(
|
||||
'aria-label',
|
||||
messages['header.label.courseOutline'].defaultMessage,
|
||||
);
|
||||
});
|
||||
|
||||
it('navigates to an absolute URL when clicked', () => {
|
||||
render(<RootWrapper {...mockProps} />);
|
||||
|
||||
const link = screen.getByTestId('course-lock-up-block');
|
||||
expect(link.href).toBe(mockProps.outlineLink);
|
||||
});
|
||||
});
|
||||
@@ -103,7 +103,12 @@ const HeaderBody = ({
|
||||
{mainMenuDropdowns.map(dropdown => {
|
||||
const { id, buttonTitle, items } = dropdown;
|
||||
return (
|
||||
<NavDropdownMenu key={id} {...{ id, buttonTitle, items }} />
|
||||
<NavDropdownMenu
|
||||
key={id}
|
||||
{...{
|
||||
id, buttonTitle, items,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Nav>
|
||||
|
||||
102
src/studio-header/HeaderBody.test.jsx
Normal file
102
src/studio-header/HeaderBody.test.jsx
Normal file
@@ -0,0 +1,102 @@
|
||||
import React from 'react';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
import { IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
import HeaderBody from './HeaderBody';
|
||||
import messages from './messages';
|
||||
|
||||
const mockOnNavigate = jest.fn();
|
||||
const mockSearchButtonAction = jest.fn();
|
||||
const mockToggleModalPopup = jest.fn();
|
||||
const mockSetModalPopupTarget = jest.fn();
|
||||
|
||||
const defaultProps = {
|
||||
studioBaseUrl: 'https://example.com',
|
||||
logoutUrl: 'https://example.com/logout',
|
||||
onNavigate: mockOnNavigate,
|
||||
setModalPopupTarget: mockSetModalPopupTarget,
|
||||
toggleModalPopup: mockToggleModalPopup,
|
||||
searchButtonAction: mockSearchButtonAction,
|
||||
username: 'testuser',
|
||||
authenticatedUserAvatar: 'avatar.png',
|
||||
isAdmin: true,
|
||||
isMobile: false,
|
||||
isHiddenMainMenu: false,
|
||||
mainMenuDropdowns: [],
|
||||
logo: 'logo.png',
|
||||
logoAltText: 'Test Logo',
|
||||
number: '101',
|
||||
org: 'EDX',
|
||||
title: 'Test Course',
|
||||
outlineLink: '/courses/edx/course-101',
|
||||
};
|
||||
|
||||
const RootWrapper = (props) => (
|
||||
<MemoryRouter>
|
||||
<IntlProvider locale="en" messages={messages}>
|
||||
<HeaderBody {...props} />
|
||||
</IntlProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
describe('HeaderBody Component', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('renders the logo and brand navigation', () => {
|
||||
render(<RootWrapper {...defaultProps} />);
|
||||
|
||||
const logoImage = screen.getByAltText(defaultProps.logoAltText);
|
||||
expect(logoImage).toBeInTheDocument();
|
||||
expect(logoImage).toHaveAttribute('src', defaultProps.logo);
|
||||
});
|
||||
|
||||
it('renders course lockup information', () => {
|
||||
render(<RootWrapper {...defaultProps} />);
|
||||
|
||||
const courseTitle = screen.getByText(defaultProps.title);
|
||||
const courseOrgNumber = screen.getByText(`${defaultProps.org} ${defaultProps.number}`);
|
||||
|
||||
expect(courseTitle).toBeInTheDocument();
|
||||
expect(courseOrgNumber).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders a course lock-up link with the correct outline URL', () => {
|
||||
render(<RootWrapper {...defaultProps} />);
|
||||
|
||||
const courseLockUpLink = screen.getByTestId('course-lock-up-block');
|
||||
expect(courseLockUpLink.getAttribute('href')).toBe(defaultProps.outlineLink);
|
||||
});
|
||||
|
||||
it('displays search button and triggers searchButtonAction on click', () => {
|
||||
render(<RootWrapper {...defaultProps} />);
|
||||
|
||||
const searchButton = screen.getByLabelText(messages['header.label.search.nav'].defaultMessage);
|
||||
expect(searchButton).toBeInTheDocument();
|
||||
|
||||
fireEvent.click(searchButton);
|
||||
expect(mockSearchButtonAction).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('displays user menu with username and avatar', () => {
|
||||
render(<RootWrapper {...defaultProps} />);
|
||||
|
||||
const userMenu = screen.getByText(defaultProps.username);
|
||||
const avatarImage = screen.getByAltText(defaultProps.username);
|
||||
|
||||
expect(userMenu).toBeInTheDocument();
|
||||
expect(avatarImage).toHaveAttribute('src', defaultProps.authenticatedUserAvatar);
|
||||
});
|
||||
|
||||
it('toggles mobile menu popup when button is clicked in mobile view', () => {
|
||||
render(<RootWrapper {...defaultProps} isMobile isModalPopupOpen={false} />);
|
||||
|
||||
const menuButton = screen.getByTestId('mobile-menu-button');
|
||||
fireEvent.click(menuButton);
|
||||
|
||||
expect(mockToggleModalPopup).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -1,10 +1,9 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Collapsible } from '@openedx/paragon';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const MobileMenu = ({
|
||||
mainMenuDropdowns,
|
||||
}) => (
|
||||
const MobileMenu = ({ mainMenuDropdowns }) => (
|
||||
<div
|
||||
className="ml-4 p-2 bg-light-100 border border-gray-200 small rounded"
|
||||
data-testid="mobile-menu"
|
||||
@@ -21,9 +20,9 @@ const MobileMenu = ({
|
||||
<ul className="p-0" style={{ listStyleType: 'none' }}>
|
||||
{items.map(item => (
|
||||
<li className="mobile-menu-item">
|
||||
<a href={item.href}>
|
||||
<Link to={item.href}>
|
||||
{item.title}
|
||||
</a>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
81
src/studio-header/MobileMenu.test.jsx
Normal file
81
src/studio-header/MobileMenu.test.jsx
Normal file
@@ -0,0 +1,81 @@
|
||||
import React from 'react';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
import MobileMenu from './MobileMenu';
|
||||
|
||||
const mockOnNavigate = jest.fn();
|
||||
|
||||
const defaultProps = {
|
||||
mainMenuDropdowns: [
|
||||
{
|
||||
id: 'menu1',
|
||||
buttonTitle: 'Menu 1',
|
||||
items: [
|
||||
{ href: '/menu1/item1', title: 'Item 1' },
|
||||
{ href: '/menu1/item2', title: 'Item 2' },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'menu2',
|
||||
buttonTitle: 'Menu 2',
|
||||
items: [
|
||||
{ href: 'https://external-link.com', title: 'External Link' },
|
||||
],
|
||||
},
|
||||
],
|
||||
onNavigate: mockOnNavigate,
|
||||
};
|
||||
|
||||
const RootWrapper = (props) => (
|
||||
<MemoryRouter>
|
||||
<MobileMenu {...props} />
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
describe('MobileMenu Component', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test('renders the mobile menu with dropdowns and items', () => {
|
||||
render(<RootWrapper {...defaultProps} />);
|
||||
|
||||
const menu1Title = screen.getByText('Menu 1');
|
||||
const menu2Title = screen.getByText('Menu 2');
|
||||
|
||||
expect(menu1Title).toBeInTheDocument();
|
||||
expect(menu2Title).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('navigates to internal URL when item is clicked', () => {
|
||||
render(<RootWrapper {...defaultProps} />);
|
||||
|
||||
const menu1Title = screen.getByText(defaultProps.mainMenuDropdowns[0].buttonTitle);
|
||||
fireEvent.click(menu1Title);
|
||||
|
||||
const menuItem = screen.getByText(defaultProps.mainMenuDropdowns[0].items[0].title);
|
||||
expect(menuItem.getAttribute('href')).toBe(defaultProps.mainMenuDropdowns[0].items[0].href);
|
||||
});
|
||||
|
||||
test('navigates to an external URL when external link is clicked', () => {
|
||||
render(<RootWrapper {...defaultProps} />);
|
||||
|
||||
const menu2Title = screen.getByText(defaultProps.mainMenuDropdowns[1].buttonTitle);
|
||||
fireEvent.click(menu2Title);
|
||||
|
||||
const externalLink = screen.getByText(defaultProps.mainMenuDropdowns[1].items[0].title);
|
||||
expect(externalLink.getAttribute('href')).toBe(defaultProps.mainMenuDropdowns[1].items[0].href);
|
||||
});
|
||||
|
||||
test('renders empty state when there are no dropdowns', () => {
|
||||
render(<RootWrapper mainMenuDropdowns={[]} onNavigate={mockOnNavigate} />);
|
||||
|
||||
const mobileMenu = screen.getByTestId('mobile-menu');
|
||||
expect(mobileMenu).toBeInTheDocument();
|
||||
|
||||
const menuItems = screen.queryAllByRole('listitem');
|
||||
expect(menuItems.length).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
Dropdown,
|
||||
DropdownButton,
|
||||
} from '@openedx/paragon';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const NavDropdownMenu = ({
|
||||
id,
|
||||
@@ -18,8 +19,9 @@ const NavDropdownMenu = ({
|
||||
>
|
||||
{items.map(item => (
|
||||
<Dropdown.Item
|
||||
as={Link}
|
||||
key={`${item.title}-dropdown-item`}
|
||||
href={item.href}
|
||||
to={item.href}
|
||||
className="small"
|
||||
>
|
||||
{item.title}
|
||||
@@ -32,8 +34,8 @@ NavDropdownMenu.propTypes = {
|
||||
id: PropTypes.string.isRequired,
|
||||
buttonTitle: PropTypes.node.isRequired,
|
||||
items: PropTypes.arrayOf(PropTypes.shape({
|
||||
href: PropTypes.string,
|
||||
title: PropTypes.node,
|
||||
href: PropTypes.string.isRequired,
|
||||
title: PropTypes.node.isRequired,
|
||||
})).isRequired,
|
||||
};
|
||||
|
||||
|
||||
67
src/studio-header/NavDropdownMenu.test.jsx
Normal file
67
src/studio-header/NavDropdownMenu.test.jsx
Normal file
@@ -0,0 +1,67 @@
|
||||
import React from 'react';
|
||||
import { render, screen, fireEvent } from '@testing-library/react';
|
||||
import '@testing-library/jest-dom/extend-expect';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
import NavDropdownMenu from './NavDropdownMenu';
|
||||
|
||||
const defaultProps = {
|
||||
id: 'menu-id',
|
||||
buttonTitle: 'Menu',
|
||||
items: [
|
||||
{ href: '/item1', title: 'Item 1' },
|
||||
{ href: 'https://external.com', title: 'External Link' },
|
||||
],
|
||||
};
|
||||
|
||||
const RootWrapper = (props) => (
|
||||
<MemoryRouter>
|
||||
<NavDropdownMenu {...props} />
|
||||
</MemoryRouter>
|
||||
);
|
||||
|
||||
describe('NavDropdownMenu Component', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test('renders the dropdown button with correct title', () => {
|
||||
render(<NavDropdownMenu {...defaultProps} />);
|
||||
|
||||
const dropdownButton = screen.getByRole('button', { name: defaultProps.buttonTitle });
|
||||
expect(dropdownButton).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('renders all dropdown items', () => {
|
||||
render(<RootWrapper {...defaultProps} />);
|
||||
|
||||
const dropdownButton = screen.getByRole('button', { name: defaultProps.buttonTitle });
|
||||
fireEvent.click(dropdownButton);
|
||||
|
||||
const item1 = screen.getByText(defaultProps.items[0].title);
|
||||
const externalLink = screen.getByText(defaultProps.items[1].title);
|
||||
|
||||
expect(item1).toBeInTheDocument();
|
||||
expect(externalLink).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test('calls onNavigate with the correct URL for internal link', () => {
|
||||
render(<RootWrapper {...defaultProps} />);
|
||||
|
||||
const dropdownButton = screen.getByRole('button', { name: defaultProps.buttonTitle });
|
||||
fireEvent.click(dropdownButton);
|
||||
|
||||
const item1 = screen.getByText(defaultProps.items[0].title);
|
||||
expect(item1.getAttribute('href')).toBe(defaultProps.items[0].href);
|
||||
});
|
||||
|
||||
test('navigates to external URL when external link is clicked', () => {
|
||||
render(<RootWrapper {...defaultProps} />);
|
||||
|
||||
const dropdownButton = screen.getByRole('button', { name: defaultProps.buttonTitle });
|
||||
fireEvent.click(dropdownButton);
|
||||
|
||||
const externalLink = screen.getByText(defaultProps.items[1].title);
|
||||
expect(externalLink.getAttribute('href')).toBe(defaultProps.items[1].href);
|
||||
});
|
||||
});
|
||||
@@ -16,7 +16,8 @@ ensureConfig([
|
||||
], 'Studio Header component');
|
||||
|
||||
const StudioHeader = ({
|
||||
number, org, title, containerProps, isHiddenMainMenu, mainMenuDropdowns, outlineLink, searchButtonAction,
|
||||
number, org, title, containerProps, isHiddenMainMenu, mainMenuDropdowns,
|
||||
outlineLink, searchButtonAction, isNewHomePage,
|
||||
}) => {
|
||||
const { authenticatedUser, config } = useContext(AppContext);
|
||||
const props = {
|
||||
@@ -29,7 +30,7 @@ const StudioHeader = ({
|
||||
username: authenticatedUser?.username,
|
||||
isAdmin: authenticatedUser?.administrator,
|
||||
authenticatedUserAvatar: authenticatedUser?.avatar,
|
||||
studioBaseUrl: config.STUDIO_BASE_URL,
|
||||
studioBaseUrl: isNewHomePage ? '/home' : config.STUDIO_BASE_URL,
|
||||
logoutUrl: config.LOGOUT_URL,
|
||||
isHiddenMainMenu,
|
||||
mainMenuDropdowns,
|
||||
@@ -66,6 +67,7 @@ StudioHeader.propTypes = {
|
||||
})),
|
||||
outlineLink: PropTypes.string,
|
||||
searchButtonAction: PropTypes.func,
|
||||
isNewHomePage: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
StudioHeader.defaultProps = {
|
||||
|
||||
@@ -29,6 +29,8 @@ $white: #FFFFFF;
|
||||
}
|
||||
|
||||
.course-title-lockup {
|
||||
overflow: hidden;
|
||||
|
||||
@media only screen and (min-width: 769px) {
|
||||
padding: .5rem;
|
||||
padding-right: $spacer;
|
||||
@@ -36,8 +38,6 @@ $white: #FFFFFF;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
span {
|
||||
color: #333333;
|
||||
white-space: nowrap;
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
import { AppContext } from '@edx/frontend-platform/react';
|
||||
import { IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import { Context as ResponsiveContext } from 'react-responsive';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
import StudioHeader from './StudioHeader';
|
||||
import messages from './messages';
|
||||
@@ -40,15 +41,17 @@ const RootWrapper = ({
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line react/jsx-no-constructed-context-values, react/prop-types
|
||||
<IntlProvider locale="en">
|
||||
<AppContext.Provider value={appContextValue}>
|
||||
<ResponsiveContext.Provider value={responsiveContextValue}>
|
||||
<StudioHeader
|
||||
{...props}
|
||||
/>
|
||||
</ResponsiveContext.Provider>
|
||||
</AppContext.Provider>
|
||||
</IntlProvider>
|
||||
<MemoryRouter>
|
||||
<IntlProvider locale="en">
|
||||
<AppContext.Provider value={appContextValue}>
|
||||
<ResponsiveContext.Provider value={responsiveContextValue}>
|
||||
<StudioHeader
|
||||
{...props}
|
||||
/>
|
||||
</ResponsiveContext.Provider>
|
||||
</AppContext.Provider>
|
||||
</IntlProvider>
|
||||
</MemoryRouter>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -70,6 +73,7 @@ const props = {
|
||||
],
|
||||
outlineLink: 'tEsTLInK',
|
||||
searchButtonAction: null,
|
||||
isNewHomePage: true,
|
||||
};
|
||||
|
||||
describe('Header', () => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import messages from './messages';
|
||||
|
||||
const getUserMenuItems = ({
|
||||
@@ -21,7 +22,7 @@ const getUserMenuItems = ({
|
||||
href: `${studioBaseUrl}`,
|
||||
title: intl.formatMessage(messages['header.user.menu.studio']),
|
||||
}, {
|
||||
href: `${studioBaseUrl}/maintenance`,
|
||||
href: `${getConfig().STUDIO_BASE_URL}/maintenance`,
|
||||
title: intl.formatMessage(messages['header.user.menu.maintenance']),
|
||||
}, {
|
||||
href: `${logoutUrl}`,
|
||||
|
||||
Reference in New Issue
Block a user