Compare commits

..

198 Commits

Author SHA1 Message Date
Brian Smith
c1d143ace2 fix: update frontend-platform peer dependency (#459)
This updates the `frontend-platform` peer dependency to require a version that has paragon in the `openedx` scope as a peer dependency, as opposed to the `edx` scope
2024-01-18 12:59:14 -05:00
Brian Smith
54a879aec2 chore!: move paragon to peer dependency using @openedx scope (#456)
BREAKING CHANGE: consuming applications must now provide `paragon` from the `@openedx` scope
2024-01-16 11:06:27 -05:00
renovate[bot]
b5b37f1d64 chore(deps): update dependency @testing-library/dom to v9.3.4 2024-01-15 13:19:09 +00:00
renovate[bot]
36be99ace0 chore(deps): update dependency @openedx/frontend-build to v13.0.27 2024-01-15 11:42:22 +00:00
dependabot[bot]
222bc19bd0 chore(deps): bump follow-redirects from 1.15.2 to 1.15.4 (#454)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.2 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.2...v1.15.4)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-11 17:14:11 +05:00
renovate[bot]
3d827e64ea chore(deps): update dependency redux-saga to v1.3.0 (#453)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-08 15:40:28 +05:00
renovate[bot]
8a247abd6a fix(deps): update dependency @openedx/paragon to v21.13.0 2024-01-01 12:25:49 +00:00
renovate[bot]
a6943fbaeb chore(deps): update dependency @edx/frontend-platform to v6.2.0 2024-01-01 10:17:53 +00:00
Mashal Malik
19292cd5b6 refactor: replace @edx/paragon with @openedx/paragon (#446)
* refactor: replace @edx/paragon with @openedx/paragon

* fix: replaced frontend-build to @openedx to fix jest issues

* refactor: removed old unused package

---------

Co-authored-by: Abdullah Waheed <abdullah.waheed@arbisoft.com>
2024-01-01 13:08:15 +05:00
Syed Ali Abbas Zaidi
b6374a5c05 feat: migrate enzyme with RTL (#439)
Co-authored-by: Abdullah Waheed <abdullah.waheed@arbisoft.com>
2023-12-25 16:23:33 +05:00
renovate[bot]
f6a4036b49 fix(deps): update font awesome to v6.5.1 2023-12-25 09:29:05 +00:00
renovate[bot]
12a845ad33 chore(deps): update dependency react-router-dom to v6.21.1 2023-12-25 09:07:12 +00:00
renovate[bot]
a4a7456726 chore(deps): update dependency react-router-dom to v6.21.0 2023-12-18 13:48:34 +00:00
renovate[bot]
6fdf73fed3 chore(deps): update dependency @edx/frontend-build to v13.0.14 2023-12-18 10:46:30 +00:00
renovate[bot]
32b8079744 chore(deps): update dependency @edx/frontend-platform to v6.1.2 2023-12-11 12:36:07 +00:00
renovate[bot]
b9f7fe74c8 chore(deps): update dependency @edx/frontend-build to v13.0.12 2023-12-11 10:39:07 +00:00
dependabot[bot]
5a259a76df chore(deps-dev): bump @adobe/css-tools from 4.3.1 to 4.3.2 (#440)
Bumps [@adobe/css-tools](https://github.com/adobe/css-tools) from 4.3.1 to 4.3.2.
- [Changelog](https://github.com/adobe/css-tools/blob/main/History.md)
- [Commits](https://github.com/adobe/css-tools/commits)

---
updated-dependencies:
- dependency-name: "@adobe/css-tools"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-06 13:10:03 +05:00
Jenkins
d2ad5ee2a4 chore(i18n): update translations 2023-12-03 15:31:05 -05:00
renovate[bot]
67fefe814b chore(deps): update dependency react-router-dom to v6.20.0 2023-11-27 11:37:21 +00:00
renovate[bot]
9a5b1fa5e7 chore(deps): update dependency react-router-dom to v6.19.0 2023-11-20 13:25:44 +00:00
renovate[bot]
55f21aeeaa chore(deps): update dependency @edx/frontend-build to v13.0.8 2023-11-20 09:19:45 +00:00
Jenkins
fc24e61a1c chore(i18n): update translations 2023-11-19 15:31:00 -05:00
Kristin Aoki
bace8286fd fix: studio dropdown hover styles (#433) 2023-11-16 11:18:00 -05:00
renovate[bot]
665653e9a5 chore(deps): update actions/setup-node action to v4 (#429)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-11-16 11:46:31 +05:00
Bilal Qamar
0cc2282c44 feat: bumped frontend-platform version (#432) 2023-11-15 17:21:06 +05:00
Bilal Qamar
455ffd345c Revert "chore: bumped frontend-platform version (#430)" (#431)
This reverts commit dde02a0739.
2023-11-15 17:11:01 +05:00
Bilal Qamar
dde02a0739 chore: bumped frontend-platform version (#430) 2023-11-15 16:51:34 +05:00
renovate[bot]
81cb72f10b chore(deps): update dependency @edx/frontend-build to v13.0.5 2023-11-13 10:40:46 +00:00
Kristin Aoki
e285a91408 fix: admin home link in user menu (#427) 2023-11-07 12:17:41 -05:00
Kristin Aoki
50142adb85 Merge pull request #426 from openedx/KristinAoki/fix-broken-studio-home-link
fix: broken studio home link
2023-11-06 10:17:33 -05:00
KristinAoki
7281804fbd fix: broken studio home link 2023-11-06 09:13:01 -05:00
renovate[bot]
b446534992 chore(deps): update dependency react-router-dom to v6.18.0 2023-11-06 12:07:03 +00:00
renovate[bot]
3adc305aec chore(deps): update dependency @edx/frontend-build to v13.0.4 2023-11-06 11:36:45 +00:00
Jenkins
65446ce9c3 chore(i18n): update translations 2023-11-05 15:32:11 -05:00
renovate[bot]
f3637b5624 fix(deps): update dependency axios-mock-adapter to v1.22.0 2023-10-30 12:47:59 +00:00
Muhammad Abdullah Waheed
3c1d2152aa feat: babel-plugin-react-intl to babel-plugin-formatjs migration (#404)
* feat: babel-plugin-react-intl to babel-plugin-formatjs migration

* fix: upgraded frontend-build to fix security issue

* fix: upgraded frontend-build to fix security issue

* fix: upgraded frontend build again

* refactor: upgraded to latest frontend-build and resolved vulnerabilities
2023-10-30 15:17:10 +05:00
renovate[bot]
48b22ea41e chore(deps): update dependency react-router-dom to v6.17.0 2023-10-30 10:09:57 +00:00
renovate[bot]
ed76e40862 fix(deps): update dependency @edx/paragon to v21.5.6 2023-10-30 07:33:58 +00:00
Bilal Qamar
d5b07cc38f chore: bumped frontend-platform version (#417) 2023-10-24 13:54:00 +05:00
Feanil Patel
b9509bb890 chore: Update to the new version of brand-openedx in the new scope. (#414)
Part of https://github.com/openedx/axim-engineering/issues/23

This updates the `@edx/brand` alias to point to the `brand-openedx` package at
the `openedx` scope. This does not impact imports because this package is used
via an alias.
2023-10-20 17:18:23 -04:00
dependabot[bot]
c87a1049c8 chore(deps-dev): bump @babel/traverse from 7.22.5 to 7.23.2 (#413)
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.22.5 to 7.23.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)

---
updated-dependencies:
- dependency-name: "@babel/traverse"
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-18 16:52:03 +05:00
Bilal Qamar
e0c22e781d feat: bumped frontend-platform to v6 (#412) 2023-10-18 16:05:45 +05:00
renovate[bot]
f1605d1f27 fix(deps): update dependency @edx/paragon to v21.5.3 2023-10-16 13:15:31 +00:00
renovate[bot]
5d7826c26c chore(deps): update dependency @edx/frontend-platform to v5.5.4 2023-10-16 09:30:30 +00:00
renovate[bot]
5d075b0cdc chore(deps): update actions/checkout action to v4 (#395)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-10-12 12:10:30 +05:00
renovate[bot]
1a86685f11 fix(deps): update dependency @edx/paragon to v21.3.1 2023-10-09 11:14:33 +00:00
renovate[bot]
e31597509c chore(deps): update dependency @edx/frontend-platform to v5.5.1 2023-10-09 11:11:49 +00:00
Jenkins
c79c137fd6 chore(i18n): update translations 2023-10-08 16:31:21 -04:00
Mashal Malik
76f735ed39 refactor: updated README file to reflect template changes (#380)
* refactor: update README File

* refactor: update README file

* refactor: update readMe file

* refactor: update readMe file

* refactor: update readMe file

* refactor: update readMe file

* refactor: update readMe file

* refactor: update readMe file

* refactor: update readMe file

* refactor: update readMe file

* refactor: update readMe file

* refactor: update readMe file

* refactor: update readMe file

* refactor: update readMe file

* refactor: update readMe file

* refactor: update readMe file

* refactor: update readMe file

* refactor: replace npm install with npm ci
2023-10-04 12:29:28 +05:00
Kristin Aoki
2298791aeb feat: update studio header to be more accessible 2023-10-02 11:37:48 -04:00
KristinAoki
9f48ccc66b feat: update studio header to be more accessible 2023-10-02 10:05:22 -04:00
Mashal Malik
398479d2e7 refactor: add @openedx in renovate automate configuration (#400) 2023-10-02 18:58:01 +05:00
renovate[bot]
3d6d815373 chore(deps): update dependency react-router-dom to v6.16.0 2023-10-02 12:47:21 +00:00
renovate[bot]
287bf50a46 chore(deps): update dependency @edx/frontend-build to v12.9.17 2023-10-02 10:28:13 +00:00
renovate[bot]
677e872320 chore(deps): update dependency jest to v29.7.0 2023-09-25 13:14:25 +00:00
renovate[bot]
d8ecfd6fa3 chore(deps): update dependency @edx/frontend-platform to v5.4.0 2023-09-25 10:21:00 +00:00
renovate[bot]
9a6074868b fix(deps): update dependency @edx/paragon to v21 (#393)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-09-25 11:31:13 +05:00
renovate[bot]
077ecf38a4 chore(deps): update dependency @testing-library/dom to v9.3.3 2023-09-18 12:39:04 +00:00
renovate[bot]
7d6aa276ec chore(deps): update dependency @edx/frontend-platform to v5.3.2 2023-09-18 11:31:49 +00:00
renovate[bot]
115e69fad3 chore(deps): update dependency @edx/frontend-platform to v5.3.1 2023-09-07 16:42:05 +00:00
Mashal Malik
3e04f76c81 refactor: update lock file version (#391) 2023-08-31 11:29:46 +05:00
renovate[bot]
83eeb88eab chore(deps): update dependency jest to v29.6.4 2023-08-28 06:07:32 +00:00
renovate[bot]
a05570ed37 chore(deps): update dependency react-router-dom to v6.15.0 2023-08-21 09:53:30 +00:00
renovate[bot]
058a846978 chore(deps): update dependency @edx/frontend-build to v12.9.10 2023-08-21 08:26:22 +00:00
renovate[bot]
04cd95824e chore(deps): update dependency @edx/frontend-platform to v5.1.0 2023-08-18 20:03:31 +00:00
Syed Ali Abbas Zaidi
678b95f60d feat: upgrade react router to v6 (#316)
* feat: upgrade react router to v6

* chore: update peer deps
2023-08-16 16:46:49 +05:00
renovate[bot]
599f513624 fix(deps): update dependency @edx/paragon to v20.46.2 2023-08-14 10:18:32 +00:00
renovate[bot]
e8ce471f6f fix(deps): update font awesome to v6.4.2 2023-08-14 06:15:41 +00:00
renovate[bot]
4e727748af chore(deps): update dependency @edx/frontend-platform to v4.6.1 2023-08-07 10:37:18 +00:00
renovate[bot]
79c5bbff68 chore(deps): update dependency @edx/frontend-build to v12.9.4 2023-08-07 08:25:09 +00:00
Jenkins
a95b600a00 chore(i18n): update translations 2023-08-06 16:31:03 -04:00
Awais Ansari
464e952a1e Merge pull request #379 from openedx/aansari/INF-993
feat: deprecate notification tray in learning header
2023-08-02 14:35:38 +05:00
Awais Ansari
018ca18a4e feat: deprecate notification tray in learning header 2023-08-01 18:50:12 +05:00
renovate[bot]
4b89e7561f chore(deps): update dependency jest to v29.6.2 2023-07-31 09:53:25 +00:00
renovate[bot]
da08e721c2 chore(deps): update dependency @edx/frontend-build to v12.9.3 2023-07-31 07:34:58 +00:00
renovate[bot]
d037e96a0c chore(deps): update dependency @testing-library/jest-dom to v5.17.0 2023-07-24 12:16:59 +00:00
renovate[bot]
050dd30d8f chore(deps): update dependency @edx/frontend-build to v12.9.2 2023-07-24 09:55:32 +00:00
renovate[bot]
3dd41030d3 fix(deps): update dependency @edx/paragon to v20.45.5 2023-07-24 06:29:20 +00:00
Awais Ansari
27228f093d Merge pull request #370 from openedx/Ayesha/INF-951
fix: clicking on notification redirecting to correct URL
2023-07-14 13:04:49 +05:00
Muhammad Adeel Tajamul
83f3241e26 fix: course name was not visible in notification (#371) 2023-07-14 08:53:02 +05:00
ayeshoali
e153aefc13 fix: clicking on notification redirecting to correct URL 2023-07-13 15:48:35 +05:00
Bilal Qamar
936c8714b7 fix: updated frontend-build to resolve word-wrap ReDoS vulnerability (#368) 2023-07-11 17:31:29 +05:00
renovate[bot]
6b18f28933 chore(deps): update dependency jest to v29.6.1 2023-07-10 10:41:00 +00:00
ayesha waris
0ce451cfd2 feat: integrated notifications tray with backend apis (#363)
* feat: integrated notifications tray with backend apis

* test: updates test cases

* refactor: loader added and resolves minor nits

* test: fixes test cases related to pagination

* refactor: moved pagination to normalised data
2023-07-10 13:18:44 +05:00
renovate[bot]
42e831a693 chore(deps): update dependency @edx/frontend-build to v12.8.60 2023-07-10 07:38:50 +00:00
Jenkins
9af7eaf587 chore(i18n): update translations 2023-07-09 16:30:57 -04:00
Bilal Qamar
3fbbde1044 fix: updated frontend-build to bump eslint version (#364) 2023-07-06 18:14:00 +05:00
Bilal Qamar
c3f0282be4 feat: update react & react-dom to v17 (#346)
* feat: update react & react-dom to v17

* build: update paragon version

* build: update lock file

* refactor: updated frontend-platform

---------

Co-authored-by: mashal-m <mashal.malik@arbisoft.com>
2023-07-05 12:04:53 +05:00
sundasnoreen12
ceb9c13542 Merge pull request #360 from openedx/sundas/INF-917
test: added test cases for UI components
2023-07-03 23:20:05 -07:00
SundasNoreen
7229bff3ff refactor: added mock funtion for notification count api 2023-07-03 16:30:03 +05:00
renovate[bot]
0695d4f400 fix(deps): update font awesome to v6.4.0 2023-07-03 08:13:31 +00:00
SundasNoreen
de8783c708 refactor: fixed review points 2023-06-27 16:28:58 +05:00
renovate[bot]
e2540bc3a0 fix(deps): update dependency @edx/paragon to v20.45.0 2023-06-26 19:55:23 +00:00
renovate[bot]
ffb5a765e2 chore(deps): update dependency @edx/reactifex to v2.2.0 2023-06-26 14:58:49 +00:00
renovate[bot]
952e543217 fix(deps): update dependency axios-mock-adapter to v1.21.5 2023-06-26 11:48:04 +00:00
SundasNoreen
0e6a272f2b test: added test cases for UI components 2023-06-26 13:39:09 +05:00
renovate[bot]
45a1da9f5e chore(deps): update dependency @edx/frontend-build to v12.8.57 2023-06-26 06:43:40 +00:00
sundasnoreen12
022515d1d2 Merge pull request #355 from openedx/sundas/INF-903
feat: binded show notification tray status with the backend api
2023-06-20 01:59:04 -07:00
ayeshoali
2d737aae7f refactor: fixed data updation in redux 2023-06-20 13:31:15 +05:00
SundasNoreen
4c4db14eac feat: binded show notification tray status with the backend api 2023-06-19 18:00:06 +05:00
sundasnoreen12
911cea6a0e Merge pull request #350 from openedx/sundas/INF-878
test: added redux, selector and api cases
2023-06-19 05:10:57 -07:00
SundasNoreen
a52ddfd9bd refactor: changed api url 2023-06-19 16:58:32 +05:00
SundasNoreen
8175ba897a test: added failed and denied test cases of redux 2023-06-19 16:04:55 +05:00
renovate[bot]
cfda72b2e2 chore(deps): update dependency @testing-library/dom to v9.3.1 2023-06-19 10:46:30 +00:00
SundasNoreen
4483a734bc refactor: fixed issues of review 2023-06-19 15:33:47 +05:00
renovate[bot]
db1903cdce chore(deps): update dependency @edx/frontend-build to v12.8.54 2023-06-19 08:12:52 +00:00
Jenkins
71851b13a6 chore(i18n): update translations 2023-06-18 16:30:53 -04:00
SundasNoreen
6efa31092d test: added redux, selector and api cases 2023-06-15 17:27:11 +05:00
SundasNoreen
c3541a3d79 test: added notification redux test cases 2023-06-15 13:30:38 +05:00
sundasnoreen12
dad01fcd78 Merge pull request #340 from openedx/sundas/INF-820
feat: added notification UI
2023-06-15 01:12:31 -07:00
ayeshoali
30e6eed60d refactor: fixes extra spaces in index.scss 2023-06-15 12:39:23 +05:00
renovate[bot]
de69ed3dd9 fix(deps): update dependency @edx/paragon to v20.44.0 2023-06-12 14:13:06 +00:00
renovate[bot]
1d55df323f chore(deps): update dependency @edx/frontend-build to v12.8.51 2023-06-12 11:46:29 +00:00
SundasNoreen
4e718f85de refactor: fixed all review points 2023-06-12 12:56:15 +05:00
SundasNoreen
a211547a1d refactor: removed backend api calls 2023-06-08 21:08:54 +05:00
Awais Ansari
784e9afccf fix: add appName param in getNotifications function 2023-06-06 15:16:06 +05:00
SundasNoreen
4b23d8c4e4 fix: lint issue 2023-06-06 13:15:12 +05:00
ayeshoali
6d02e63d08 fix: fixes lint errors 2023-06-06 13:04:20 +05:00
ayeshoali
b1feed2443 fix: fixes UI according to figma 2023-06-06 12:48:10 +05:00
SundasNoreen
cabf4e3f27 refactor: fixed code refactor and added new slices and selector 2023-06-05 19:55:46 +05:00
Awais Ansari
78a40d47c1 refactor: code and style modifications 2023-06-05 17:52:52 +05:00
renovate[bot]
c7178afe6b chore(deps): update dependency @edx/frontend-build to v12.8.40 2023-06-05 10:10:38 +00:00
Awais Ansari
18a6840037 fix: Ui modifications 2023-06-05 14:51:30 +05:00
renovate[bot]
583a487c38 chore(deps): update dependency @edx/frontend-platform to v4.5.1 2023-06-05 08:50:33 +00:00
SundasNoreen
3276496523 feat: added redux store implementation 2023-06-05 12:15:41 +05:00
Awais Ansari
7ab55175b5 fix: redux structure updates 2023-06-01 19:57:45 +05:00
SundasNoreen
72e82005c0 feat: added notification APIs 2023-06-01 13:07:53 +05:00
SundasNoreen
c4df727178 refactor: refactor components 2023-05-31 09:28:45 +05:00
renovate[bot]
1f6766175d chore(deps): update dependency @edx/frontend-platform to v4.5.0 2023-05-29 09:37:10 +00:00
renovate[bot]
2ac8988a9b chore(deps): update dependency @edx/frontend-build to v12.8.38 2023-05-29 07:50:58 +00:00
Awais Ansari
642be093c7 fix: bell icon design change 2023-05-26 20:52:18 +05:00
SundasNoreen
86939a2559 refactor: added notification icon in learning header 2023-05-26 17:07:05 +05:00
SundasNoreen
8ed18f3d69 refactor: UI refactor based on figma 2023-05-25 22:41:32 +05:00
SundasNoreen
061746da9f refactor: used paragon icons and updated css 2023-05-24 15:14:41 +05:00
SundasNoreen
de77aa5f0c feat: notification tray closes when clicked outside 2023-05-23 12:16:22 +05:00
SundasNoreen
7034d10536 refactor: fixed snapshot and store structure in header test file 2023-05-23 10:40:43 +05:00
SundasNoreen
4ce7311809 refactor: removed unused states 2023-05-22 15:17:04 +05:00
SundasNoreen
e76f5b6937 feat: added add more notification button functionality 2023-05-22 15:05:26 +05:00
SundasNoreen
f8fc794458 feat: added notification tray 2023-05-22 14:59:24 +05:00
sundasnoreen12
a5069edd94 feat: added notification UI 2023-05-15 16:36:41 +05:00
renovate[bot]
2543926c95 fix(deps): update dependency @edx/paragon to v20.36.0 2023-05-15 07:37:37 +00:00
renovate[bot]
c5eb43a2a5 chore(deps): update dependency @testing-library/dom to v9.3.0 2023-05-15 07:33:45 +00:00
renovate[bot]
256fa5c9d8 fix(deps): update dependency @edx/paragon to v20.34.0 2023-05-08 10:40:34 +00:00
renovate[bot]
267cce9f89 chore(deps): update dependency @edx/frontend-build to v12.8.27 2023-05-08 10:37:16 +00:00
renovate[bot]
4f59c80a12 fix(deps): update dependency @edx/paragon to v20.32.3 2023-05-01 07:58:51 +00:00
renovate[bot]
59afd596ab chore(deps): update dependency @edx/frontend-build to v12.8.16 2023-05-01 07:55:36 +00:00
Jenkins
0c83268163 chore(i18n): update translations 2023-04-23 16:30:51 -04:00
Bilal Qamar
c5f5fa9281 feat!: upgraded to node v18, added .nvmrc and updated workflows (#332)
BREAKING CHANGE: Ending support of @edx/frontend-platform v2 and v3 and now only support v4
2023-04-20 19:10:05 +05:00
Muhammad Abdullah Waheed
e247aee372 fix: updated readme for installing dependencies (#331) 2023-04-20 17:36:10 +05:00
Bilal Qamar
f6ae5a4bdd refactor: reverted in favor of major release due to updated platform peer dependencies (#330) 2023-04-20 16:52:44 +05:00
Brian Smith
057d16d3c1 fix: release frontend-platform version update 2023-04-19 13:04:58 -04:00
Omar Al-Ithawi
93bb38d0bd chore(deps): update frontend-platform version (#329)
gets the intl-imports.js script for FC-0012 OEP-58.
2023-04-13 16:56:46 -04:00
renovate[bot]
01405eaff9 fix(deps): update dependency @edx/paragon to v20.30.1 2023-04-10 14:48:47 +00:00
renovate[bot]
59fa6e2a35 chore(deps): update dependency @edx/frontend-build to v12.8.6 2023-04-10 11:18:27 +00:00
Mashal Malik
9fc3a0e835 refactor: update peer dependency for react and react-dom (#326)
* refactor: remove react16 from peer dependency

* build: update lock file

* fix: fix lint issue

* refactor: add 17 support of react in peerDep
2023-04-05 16:47:15 +05:00
renovate[bot]
22e157adf6 fix(deps): update dependency @edx/paragon to v20.29.0 2023-04-03 12:46:02 +00:00
renovate[bot]
6b81f69eba chore(deps): update dependency @testing-library/dom to v9.2.0 2023-04-03 10:35:08 +00:00
Bilal Qamar
cefa84006c fix: reverted semantic release to v16 (#321) 2023-03-30 11:04:45 +05:00
Muhammad Abdullah Waheed
a54309dd63 fix: updated version of semantic-release-action (#322) 2023-03-29 23:33:58 +05:00
Bilal Qamar
aeb0fd2be7 feat: upgraded to node v18, added .nvmrc and updated workflows (#314)
* feat: upgraded to node v18, added .nvmrc and updated workflows

* refactor: upgraded frontend-build & frontend-platform, updated workflows

* refactor: moved platform from devDependencies
2023-03-29 16:44:29 +05:00
renovate[bot]
26eb2bb4c7 chore(deps): update dependency @edx/frontend-platform to v3.6.0 2023-03-27 10:18:25 +00:00
renovate[bot]
8083079954 chore(deps): update dependency @edx/frontend-build to v12.7.0 2023-03-27 10:15:17 +00:00
renovate[bot]
d4fc8489ea fix(deps): update dependency @edx/paragon to v20.28.5 2023-03-20 11:56:19 +00:00
renovate[bot]
4020a81bd4 chore(deps): update dependency redux-saga to v1.2.3 2023-03-20 11:53:34 +00:00
renovate[bot]
acf1adba80 chore(deps): update dependency jest to v29.5.0 2023-03-13 09:54:48 +00:00
renovate[bot]
a204ff8c03 chore(deps): update dependency @testing-library/dom to v9.0.1 2023-03-13 09:47:40 +00:00
Mashal Malik
e8ccc4b707 chore: Update transifex api from v2 to v3 (#310)
* chore: Update transifex api from v2 to v3

* refactor: remove duplicate line
2023-03-06 17:58:12 +05:00
renovate[bot]
4d86780c73 chore(deps): update dependency @testing-library/dom to v9 2023-02-27 11:44:52 +00:00
renovate[bot]
fdbb83f51e fix(deps): update font awesome to v6.3.0 2023-02-27 11:38:21 +00:00
Feanil Patel
f6e4664d37 Merge pull request #304 from openedx/repo_checks/ensure_workflows
Update standard workflow files.
2023-02-24 11:45:23 -05:00
Feanil Patel
65177fcdcc build: Updating a missing workflow file add-depr-ticket-to-depr-board.yml.
The .github/workflows/add-depr-ticket-to-depr-board.yml workflow is missing or needs an update to stay in
sync with the current standard for this workflow as defined in the
`.github` repo of the `openedx` GitHub org.
2023-02-23 13:57:40 -05:00
Feanil Patel
7601249fb6 build: Creating a missing workflow file add-remove-label-on-comment.yml.
The .github/workflows/add-remove-label-on-comment.yml workflow is missing or needs an update to stay in
sync with the current standard for this workflow as defined in the
`.github` repo of the `openedx` GitHub org.
2023-02-23 13:57:40 -05:00
Feanil Patel
c8ef3dad93 build: Creating a missing workflow file self-assign-issue.yml.
The .github/workflows/self-assign-issue.yml workflow is missing or needs an update to stay in
sync with the current standard for this workflow as defined in the
`.github` repo of the `openedx` GitHub org.
2023-02-23 13:57:39 -05:00
renovate[bot]
b02fe00c71 fix(deps): update dependency @edx/paragon to v20.28.4 2023-02-20 11:19:51 +00:00
renovate[bot]
4404aede33 chore(deps): update dependency jest to v29.4.3 2023-02-20 11:12:57 +00:00
renovate[bot]
1b0edb10c4 chore(deps): update dependency @testing-library/dom to v8.20.0 2023-02-13 12:12:41 +00:00
renovate[bot]
546adff45e chore(deps): update dependency @edx/brand to v1.2.0 2023-02-13 12:05:28 +00:00
renovate[bot]
94b14fd618 chore(deps): update dependency redux-saga to v1.2.2 2023-02-06 13:51:48 +00:00
renovate[bot]
5b8a9a587b chore(deps): update dependency redux to v4.2.1 2023-02-06 13:44:55 +00:00
renovate[bot]
2650cb59b3 chore(deps): update actions/setup-node action to v3 (#212)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-06 15:11:28 +05:00
renovate[bot]
bc2b13175a chore(deps): update dependency husky to v8 (#235)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-06 14:30:50 +05:00
renovate[bot]
85e8094833 fix(deps): update font awesome to v6 (#281)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-02-06 14:17:48 +05:00
dependabot[bot]
aff8dda3ee chore(deps): bump decode-uri-component from 0.2.0 to 0.2.2 (#287)
Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2.
- [Release notes](https://github.com/SamVerschueren/decode-uri-component/releases)
- [Commits](https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.2)

---
updated-dependencies:
- dependency-name: decode-uri-component
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-01 17:15:00 +05:00
dependabot[bot]
51b505552d chore(deps): bump cookiejar from 2.1.3 to 2.1.4 (#296)
Bumps [cookiejar](https://github.com/bmeck/node-cookiejar) from 2.1.3 to 2.1.4.
- [Release notes](https://github.com/bmeck/node-cookiejar/releases)
- [Commits](https://github.com/bmeck/node-cookiejar/commits)

---
updated-dependencies:
- dependency-name: cookiejar
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-01 17:14:36 +05:00
dependabot[bot]
3648f1b6be build(deps): bump json5 from 1.0.1 to 1.0.2
Bumps [json5](https://github.com/json5/json5) from 1.0.1 to 1.0.2.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v1.0.1...v1.0.2)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-30 14:12:31 +00:00
Bilal Qamar
c78b6964b9 chore: updated frontend-build version to v12.4.19 (#297) 2023-01-25 18:51:25 +05:00
renovate[bot]
664d05134b fix(deps): update dependency @edx/paragon to v20.27.0 2023-01-23 10:38:54 +00:00
renovate[bot]
b969522cd0 chore(deps): update dependency @edx/frontend-build to v12.4.16 2023-01-23 10:34:46 +00:00
renovate[bot]
0cd8210ea7 chore(deps): update dependency @testing-library/dom to v8.19.1 2023-01-16 10:17:13 +00:00
renovate[bot]
1c763c2102 chore(deps): update dependency @edx/frontend-build to v12.4.15 2023-01-09 09:27:54 +00:00
Mashal Malik
073003284a Moving code coverage from codecov package to CI (#289)
* fix: removed derpeciated package codecov

* fix: install edx/paragon 20.20.0 fixed version

* fix: specified paragron 20.20.0 version

Co-authored-by: Shahroz Ahmad <shahroz.ahmad@arbisoft.com>
2022-12-29 12:25:49 +05:00
Bilal Qamar
92fdf85c9a feat: paragon updated to v20 & frontend-build version updated
* feat: paragon updated to v20 & frontend-build version updated

* refactor: moved paragon from devDependencies to satisfy eslint rule

* refactor: updated snapshots
2022-12-09 15:57:09 +05:00
Sagirov Eugeniy
5ee8a8c75c feat: Account pages. Updated menu items urls. 2022-12-02 12:28:15 +00:00
Abdullah Waheed
536d67404f refactor: updated renovate config to auto update minor and patch versions of edx dependencies 2022-11-30 13:17:21 +00:00
Bilal Qamar
9d99bfcec6 refactor: updated snapshots 2022-11-25 16:53:50 +05:00
Bilal Qamar
3180c9d973 refactor: moved paragon from devDependencies to satisfy eslint rule 2022-11-25 16:48:29 +05:00
Bilal Qamar
1645274d9f feat: paragon updated to v20 & frontend-build version updated 2022-11-25 16:36:06 +05:00
Bilal Qamar
84e43cb038 refactor: bumped loader-utils 2022-11-25 16:26:11 +05:00
55 changed files with 10038 additions and 30003 deletions

View File

@@ -1,4 +1,6 @@
ACCESS_TOKEN_COOKIE_NAME=edx-jwt-cookie-header-payload
ACCOUNT_PROFILE_URL=http://localhost:1995
ACCOUNT_SETTINGS_URL=http://localhost:1997
BASE_URL=localhost:8080
CREDENTIALS_BASE_URL=http://localhost:18150
CSRF_TOKEN_API_PATH=/csrf/api/v1/token

View File

@@ -16,4 +16,4 @@ jobs:
secrets:
GITHUB_APP_ID: ${{ secrets.GRAPHQL_AUTH_APP_ID }}
GITHUB_APP_PRIVATE_KEY: ${{ secrets.GRAPHQL_AUTH_APP_PEM }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_ISSUE_BOT_TOKEN }}
SLACK_BOT_TOKEN: ${{ secrets.SLACK_ISSUE_BOT_TOKEN }}

View File

@@ -0,0 +1,20 @@
# This workflow runs when a comment is made on the ticket
# If the comment starts with "label: " it tries to apply
# the label indicated in rest of comment.
# If the comment starts with "remove label: ", it tries
# to remove the indicated label.
# Note: Labels are allowed to have spaces and this script does
# not parse spaces (as often a space is legitimate), so the command
# "label: really long lots of words label" will apply the
# label "really long lots of words label"
name: Allows for the adding and removing of labels via comment
on:
issue_comment:
types: [created]
jobs:
add_remove_labels:
uses: openedx/.github/.github/workflows/add-remove-label-on-comment.yml@master

View File

@@ -9,18 +9,17 @@ on:
jobs:
tests:
runs-on: ubuntu-latest
strategy:
matrix:
node: [16]
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Nodejs Env
run: echo "NODE_VER=`cat .nvmrc`" >> $GITHUB_ENV
- name: Setup Nodejs
uses: actions/setup-node@v2
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
node-version: ${{ env.NODE_VER }}
- name: Install dependencies
run: npm ci
- name: Validate package-lock.json changes

View File

@@ -10,5 +10,4 @@ on:
jobs:
version-check:
uses: openedx/.github/.github/workflows/lockfileversion-check.yml@master
uses: openedx/.github/.github/workflows/lockfile-check.yml@master

View File

@@ -9,13 +9,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Nodejs Env
run: echo "NODE_VER=`cat .nvmrc`" >> $GITHUB_ENV
- name: Setup Node.js
uses: actions/setup-node@v2
uses: actions/setup-node@v4
with:
node-version: 16
node-version: ${{ env.NODE_VER }}
- name: Install dependencies
run: npm ci
- name: Validate package-lock.json changes
@@ -31,7 +33,7 @@ jobs:
- name: Build
run: npm run build
- name: Release
uses: cycjimmy/semantic-release-action@v2
uses: cycjimmy/semantic-release-action@v3
with:
semantic_version: 16
env:

12
.github/workflows/self-assign-issue.yml vendored Normal file
View File

@@ -0,0 +1,12 @@
# This workflow runs when a comment is made on the ticket
# If the comment starts with "assign me" it assigns the author to the
# ticket (case insensitive)
name: Assign comment author to ticket if they say "assign me"
on:
issue_comment:
types: [created]
jobs:
self_assign_by_comment:
uses: openedx/.github/.github/workflows/self-assign-issue.yml@master

1
.nvmrc Normal file
View File

@@ -0,0 +1 @@
18

View File

@@ -1,14 +1,12 @@
transifex_resource = frontend-component-header
export TRANSIFEX_RESOURCE = frontend-component-header
transifex_langs = "ar,fr,es_419,zh_CN,pt,it,de,uk,ru,hi,fr_CA"
transifex_utils = ./node_modules/.bin/transifex-utils.js
i18n = ./src/i18n
transifex_input = $(i18n)/transifex_input.json
tx_url1 = https://www.transifex.com/api/2/project/edx-platform/resource/$(transifex_resource)/translation/en/strings/
tx_url2 = https://www.transifex.com/api/2/project/edx-platform/resource/$(transifex_resource)/source/
# This directory must match .babelrc .
transifex_temp = ./temp/babel-plugin-react-intl
transifex_temp = ./temp/babel-plugin-formatjs
build:
rm -rf ./dist
@@ -19,7 +17,7 @@ build:
@rm -rf dist/__mocks__
requirements:
npm install
npm ci
i18n.extract:
# Pulling display strings from .jsx files into .json files...
@@ -42,11 +40,11 @@ push_translations:
# Pushing strings to Transifex...
tx push -s
# Fetching hashes from Transifex...
./node_modules/reactifex/bash_scripts/get_hashed_strings.sh $(tx_url1)
./node_modules/@edx/reactifex/bash_scripts/get_hashed_strings_v3.sh
# Writing out comments to file...
$(transifex_utils) $(transifex_temp) --comments
$(transifex_utils) $(transifex_temp) --comments --v3-scripts-path
# Pushing comments to Transifex...
./node_modules/reactifex/bash_scripts/put_comments.sh $(tx_url2)
./node_modules/@edx/reactifex/bash_scripts/put_comments_v3.sh
# Pulls translations from Transifex.
pull_translations:

View File

@@ -2,22 +2,42 @@
frontend-component-header
#########################
|Build Status| |Codecov| |npm_version| |npm_downloads| |license| |semantic-release|
|license| |Build Status| |Codecov| |npm_version| |npm_downloads| |semantic-release|
********
Overview
Purpose
********
A generic header for Open edX micro-frontend applications.
************
Requirements
Getting Started
************
Prerequisites
=============
The `devstack`_ is currently recommended as a development environment for your
new MFE. If you start it with ``make dev.up.lms`` that should give you
everything you need as a companion to this frontend.
Note that it is also possible to use `Tutor`_ to develop an MFE. You can refer
to the `relevant tutor-mfe documentation`_ to get started using it.
.. _Devstack: https://github.com/openedx/devstack
.. _Tutor: https://github.com/overhangio/tutor
.. _relevant tutor-mfe documentation: https://github.com/overhangio/tutor-mfe#mfe-development
Requirements
============
This component uses ``@edx/frontend-platform`` services such as i18n, analytics, configuration, and the ``AppContext`` React component, and expects that it has been loaded into a micro-frontend that has been properly initialized via ``@edx/frontend-platform``'s ``initialize`` function. `Please visit the frontend template application to see an example. <https://github.com/openedx/frontend-template-application/blob/master/src/index.jsx>`_
Environment Variables
=====================
====================
* ``LMS_BASE_URL`` - The URL of the LMS of your Open edX instance.
* ``LOGOUT_URL`` - The URL of the API endpoint which performs a user logout.
@@ -26,13 +46,14 @@ Environment Variables
Defaults to "localhost" in development.
* ``LOGO_URL`` - The URL of the site's logo. This logo is displayed in the header.
* ``ORDER_HISTORY_URL`` - The URL of the order history page.
* ``ACCOUNT_PROFILE_URL`` - The URL of the account profile page.
* ``ACCOUNT_SETTINGS_URL`` - The URL of the account settings page.
* ``AUTHN_MINIMAL_HEADER`` - A boolean flag which hides the main menu, user menu, and logged-out
menu items when truthy. This is intended to be used in micro-frontends like
frontend-app-authentication in which these menus are considered distractions from the user's task.
************
Installation
************
============
To install this header into your Open edX micro-frontend, run the following command in your MFE:
@@ -40,9 +61,33 @@ To install this header into your Open edX micro-frontend, run the following comm
This will make the component available to be imported into your application.
*****
Cloning and Startup
===================
.. code-block::
1. Clone your new repo:
``git clone https://github.com/openedx/frontend-component-header.git``
2. Use node v18.x.
The current version of the micro-frontend build scripts support node 18.
Using other major versions of node *may* work, but this is unsupported. For
convenience, this repository includes an .nvmrc file to help in setting the
correct node version via `nvm <https://github.com/nvm-sh/nvm>`_.
3. Install npm dependencies:
``cd frontend-component-header && npm ci``
4. Start the dev server:
``npm start``
Usage
*****
=====
This library has the following exports:
@@ -56,14 +101,12 @@ Examples
* `An example of component and messages usage. <https://github.com/openedx/frontend-template-application/blob/3355bb3a96232390e9056f35b06ffa8f105ed7ca/src/index.jsx#L21>`_
* `An example of SCSS file usage. <https://github.com/openedx/frontend-template-application/blob/3cd5485bf387b8c479baf6b02bf59e3061dc3465/src/index.scss#L8>`_
***********
Development
***********
===========
Install dependencies::
npm i
npm ci
Start the development server::
@@ -73,6 +116,63 @@ Build a production distribution::
npm run build
License
=======
The code in this repository is licensed under the AGPLv3 unless otherwise
noted.
Please see `LICENSE <LICENSE>`_ for details.
Contributing
============
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
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.
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.
Our real-time conversations are on Slack. You can request a `Slack
invitation`_, then join our `community Slack workspace`_. Because this is a
frontend repository, the best place to discuss it would be in the `#wg-frontend
channel`_.
For anything non-trivial, the best path is to open an issue in this repository
with as many details about the issue you are facing as you can provide.
https://github.com/openedx/frontend-component-header/issues
For more information about these options, see the `Getting Help`_ page.
.. _Slack invitation: https://openedx.org/slack
.. _community Slack workspace: https://openedx.slack.com/
.. _#wg-frontend channel: https://openedx.slack.com/archives/C04BM6YC7A6
.. _Getting Help: https://openedx.org/community/connect
The Open edX Code of Conduct
============================
All community members are expected to follow the `Open edX Code of Conduct`_.
.. _Open edX Code of Conduct: https://openedx.org/code-of-conduct/
Reporting Security Issues
=========================
Please do not report security issues in public. Please email security@openedx.org.
.. |Build Status| image:: https://api.travis-ci.com/edx/frontend-component-header.svg?branch=master
:target: https://travis-ci.com/edx/frontend-component-header
.. |Codecov| image:: https://img.shields.io/codecov/c/github/edx/frontend-component-header
@@ -84,4 +184,4 @@ Build a production distribution::
.. |license| image:: https://img.shields.io/npm/l/@edx/frontend-component-header.svg
:target: @edx/frontend-component-header
.. |semantic-release| image:: https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg
:target: https://github.com/semantic-release/semantic-release
:target: https://github.com/semantic-release/semantic-release

View File

@@ -1,3 +1,3 @@
const { createConfig } = require('@edx/frontend-build');
const { createConfig } = require('@openedx/frontend-build');
module.exports = createConfig('babel-preserve-modules');

View File

@@ -7,6 +7,7 @@ import { AppContext, AppProvider } from '@edx/frontend-platform/react';
import Header from '@edx/frontend-component-header';
import './index.scss';
import StudioHeader from '../src/studio-header/StudioHeader';
subscribe(APP_READY, () => {
ReactDOM.render(
@@ -32,7 +33,35 @@ subscribe(APP_READY, () => {
}}>
<Header />
</AppContext.Provider>
<h5 className="mt-2">Logged in state</h5>
<h5 className="mt-2 mb-5">Logged in state</h5>
<AppContext.Provider value={{
authenticatedUser: {
userId: '123abc',
username: 'testuser',
roles: [],
administrator: false,
},
config: getConfig(),
}}>
<StudioHeader
number="run123"
org="testX"
title="Course Name"
isHiddenMainMenu={false}
mainMenuDropdowns={[
{
id: 'content-dropdown',
buttonTitle: 'Content',
items: [{
href: '#',
title: 'Outline',
}],
},
]}
outlineLink="#"
/>
</AppContext.Provider>
<h5 className="mt-2">Logged in state for Studio header</h5>
</AppProvider>,
document.getElementById('root'),
);

View File

@@ -1,6 +1,6 @@
@import "@edx/brand/paragon/fonts";
@import "@edx/brand/paragon/variables";
@import "@edx/paragon/scss/core/core";
@import "@openedx/paragon/scss/core/core";
@import "@edx/brand/paragon/overrides";
@import "@edx/frontend-component-header/index";

View File

@@ -1,4 +1,4 @@
const { createConfig } = require('@edx/frontend-build');
const { createConfig } = require('@openedx/frontend-build');
module.exports = createConfig('jest', {
setupFilesAfterEnv: [

38103
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,7 @@
},
"scripts": {
"build": "make build",
"i18n_extract": "BABEL_ENV=i18n fedx-scripts babel src --quiet > /dev/null",
"i18n_extract": "fedx-scripts formatjs extract",
"lint": "fedx-scripts eslint --ext .js --ext .jsx .",
"snapshot": "fedx-scripts jest --updateSnapshot",
"start": "fedx-scripts webpack-dev-server --progress",
@@ -33,43 +33,43 @@
},
"homepage": "https://github.com/openedx/frontend-component-header#readme",
"devDependencies": {
"@edx/brand": "npm:@edx/brand-openedx@1.1.0",
"@edx/frontend-build": "12.3.0",
"@edx/frontend-platform": "^3.0.1",
"@edx/paragon": "19.25.3",
"@testing-library/dom": "8.19.0",
"@testing-library/jest-dom": "5.16.5",
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
"@edx/browserslist-config": "^1.1.1",
"@edx/frontend-platform": "6.2.0",
"@edx/reactifex": "^2.1.1",
"@openedx/frontend-build": "13.0.27",
"@openedx/paragon": "21.13.1",
"@testing-library/dom": "9.3.4",
"@testing-library/jest-dom": "5.17.0",
"@testing-library/react": "10.4.9",
"enzyme": "3.11.0",
"enzyme-adapter-react-16": "1.15.7",
"husky": "7.0.4",
"jest": "29.3.1",
"husky": "8.0.3",
"jest": "29.7.0",
"jest-chain": "1.1.6",
"prop-types": "15.8.1",
"react": "16.14.0",
"react-dom": "16.14.0",
"react": "17.0.2",
"react-dom": "17.0.2",
"react-redux": "7.2.9",
"react-router-dom": "5.3.4",
"react-test-renderer": "16.14.0",
"reactifex": "1.1.1",
"redux": "4.2.0",
"redux-saga": "1.2.1"
"react-router-dom": "6.21.1",
"react-test-renderer": "17.0.2",
"redux": "4.2.1",
"redux-saga": "1.3.0"
},
"dependencies": {
"@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/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",
"axios-mock-adapter": "1.22.0",
"babel-polyfill": "6.26.0",
"react-responsive": "8.2.0",
"react-transition-group": "4.4.5"
},
"peerDependencies": {
"@edx/frontend-platform": "^2.0.0 || ^3.0.0",
"@edx/paragon": ">= 7.0.0 < 21.0.0",
"@edx/frontend-platform": "^7.0.0",
"prop-types": "^15.5.10",
"react": "^16.9.0",
"react-dom": "^16.9.0"
"react": "^16.9.0 || ^17.0.0",
"react-dom": "^16.9.0 || ^17.0.0",
"@openedx/paragon": ">= 21.5.7 < 22.0.0"
}
}

View File

@@ -22,6 +22,11 @@
"pin"
],
"automerge": true
},
{
"matchPackagePatterns": ["@edx", "@openedx"],
"matchUpdateTypes": ["minor", "patch"],
"automerge": true
}
],
"timezone": "America/New_York"

View File

@@ -55,12 +55,12 @@ const Header = ({ intl }) => {
},
{
type: 'item',
href: `${config.LMS_BASE_URL}/u/${authenticatedUser.username}`,
href: `${config.ACCOUNT_PROFILE_URL}/u/${authenticatedUser.username}`,
content: intl.formatMessage(messages['header.user.menu.profile']),
},
{
type: 'item',
href: `${config.LMS_BASE_URL}/account/settings`,
href: config.ACCOUNT_SETTINGS_URL,
content: intl.formatMessage(messages['header.user.menu.account.settings']),
},
{

View File

@@ -14,7 +14,8 @@ MenuTrigger.defaultProps = {
tag: 'div',
className: null,
};
const MenuTriggerType = <MenuTrigger />.type;
const MenuTriggerComp = <MenuTrigger />;
const MenuTriggerType = MenuTriggerComp.type;
const MenuContent = ({ tag, className, ...attributes }) => React.createElement(tag, {
className: ['menu-content', className].join(' '),

View File

@@ -1,200 +0,0 @@
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { AppContext } from '@edx/frontend-platform/react';
import {
APP_CONFIG_INITIALIZED,
ensureConfig,
getConfig,
mergeConfig,
subscribe,
} from '@edx/frontend-platform';
import { ActionRow } from '@edx/paragon';
import { Menu, MenuTrigger, MenuContent } from './Menu';
import Avatar from './Avatar';
import { LinkedLogo, Logo } from './Logo';
import { CaretIcon } from './Icons';
import messages from './Header.messages';
ensureConfig([
'STUDIO_BASE_URL',
'LOGOUT_URL',
'LOGIN_URL',
'SITE_NAME',
'LOGO_URL',
'ORDER_HISTORY_URL',
], 'StudioHeader component');
subscribe(APP_CONFIG_INITIALIZED, () => {
mergeConfig({
AUTHN_MINIMAL_HEADER: !!process.env.AUTHN_MINIMAL_HEADER,
}, 'StudioHeader additional config');
});
class StudioDesktopHeaderBase extends React.Component {
constructor(props) { // eslint-disable-line no-useless-constructor
super(props);
}
renderUserMenu() {
const {
userMenu,
avatar,
username,
intl,
} = this.props;
return (
<Menu transitionClassName="menu-dropdown" transitionTimeout={250}>
<MenuTrigger
tag="button"
aria-label={intl.formatMessage(messages['header.label.account.menu.for'], { username })}
className="btn btn-outline-primary d-inline-flex align-items-center pl-2 pr-3"
>
<Avatar size="1.5em" src={avatar} alt="" className="mr-2" />
{username} <CaretIcon role="img" aria-hidden focusable="false" />
</MenuTrigger>
<MenuContent className="mb-0 dropdown-menu show dropdown-menu-right pin-right shadow py-2">
{userMenu.map(({ type, href, content }) => (
<a className={`dropdown-${type}`} key={`${type}-${content}`} href={href}>{content}</a>
))}
</MenuContent>
</Menu>
);
}
renderLoggedOutItems() {
const { loggedOutItems } = this.props;
return loggedOutItems.map((item, i, arr) => (
<a
key={`${item.type}-${item.content}`}
className={i < arr.length - 1 ? 'btn mr-2 btn-link' : 'btn mr-2 btn-outline-primary'}
href={item.href}
>
{item.content}
</a>
));
}
render() {
const {
logo,
logoAltText,
logoDestination,
loggedIn,
intl,
actionRowContent,
} = this.props;
const logoProps = { src: logo, alt: logoAltText, href: logoDestination };
const logoClasses = getConfig().AUTHN_MINIMAL_HEADER ? 'mw-100' : null;
return (
<header className="site-header-desktop">
<a className="nav-skip sr-only sr-only-focusable" href="#main">{intl.formatMessage(messages['header.label.skip.nav'])}</a>
<div className={`container-fluid ${logoClasses}`}>
<div className="nav-container position-relative d-flex align-items-center">
{logoDestination === null ? <Logo className="logo" src={logo} alt={logoAltText} /> : <LinkedLogo className="logo" {...logoProps} />}
<ActionRow>
{actionRowContent}
<nav
aria-label={intl.formatMessage(messages['header.label.secondary.nav'])}
className="nav secondary-menu-container align-items-center ml-auto"
>
{loggedIn ? this.renderUserMenu() : this.renderLoggedOutItems()}
</nav>
</ActionRow>
</div>
</div>
</header>
);
}
}
StudioDesktopHeaderBase.propTypes = {
userMenu: PropTypes.arrayOf(PropTypes.shape({
type: PropTypes.oneOf(['item', 'menu']),
href: PropTypes.string,
content: PropTypes.string,
})),
loggedOutItems: PropTypes.arrayOf(PropTypes.shape({
type: PropTypes.oneOf(['item', 'menu']),
href: PropTypes.string,
content: PropTypes.string,
})),
logo: PropTypes.string,
logoAltText: PropTypes.string,
logoDestination: PropTypes.string,
avatar: PropTypes.string,
username: PropTypes.string,
loggedIn: PropTypes.bool,
actionRowContent: PropTypes.element,
// i18n
intl: intlShape.isRequired,
};
StudioDesktopHeaderBase.defaultProps = {
userMenu: [],
loggedOutItems: [],
logo: null,
logoAltText: null,
logoDestination: null,
avatar: null,
username: null,
loggedIn: false,
actionRowContent: null,
};
const StudioDesktopHeader = injectIntl(StudioDesktopHeaderBase);
const StudioHeader = ({ intl, actionRowContent }) => {
const { authenticatedUser, config } = useContext(AppContext);
const userMenu = authenticatedUser === null ? [] : [
{
type: 'item',
href: `${config.STUDIO_BASE_URL}`,
content: intl.formatMessage(messages['header.user.menu.studio.home']),
},
{
type: 'item',
href: `${config.STUDIO_BASE_URL}/maintenance`,
content: intl.formatMessage(messages['header.user.menu.studio.maintenance']),
},
{
type: 'item',
href: config.LOGOUT_URL,
content: intl.formatMessage(messages['header.user.menu.logout']),
},
];
const props = {
logo: config.LOGO_URL,
logoAltText: config.SITE_NAME,
logoDestination: config.STUDIO_BASE_URL,
loggedIn: authenticatedUser !== null,
username: authenticatedUser !== null ? authenticatedUser.username : null,
avatar: authenticatedUser !== null ? authenticatedUser.avatar : null,
actionRowContent,
userMenu,
loggedOutItems: [],
};
return <StudioDesktopHeader {...props} />;
};
StudioHeader.propTypes = {
intl: intlShape.isRequired,
actionRowContent: PropTypes.element,
};
StudioHeader.defaultProps = {
// eslint-disable-next-line react/jsx-no-useless-fragment
actionRowContent: <></>,
};
export default injectIntl(StudioHeader);

View File

@@ -1,108 +0,0 @@
/* eslint-disable react/prop-types */
import React, { useMemo } from 'react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import TestRenderer from 'react-test-renderer';
import { Link } from 'react-router-dom';
import { AppContext } from '@edx/frontend-platform/react';
import {
ActionRow,
Button,
Dropdown,
} from '@edx/paragon';
import { StudioHeader } from './index';
const StudioHeaderComponent = ({ contextValue, appMenu = null, mainMenu = [] }) => (
<IntlProvider locale="en" messages={{}}>
<AppContext.Provider
value={contextValue}
>
<StudioHeader appMenu={appMenu} mainMenu={mainMenu} />
</AppContext.Provider>
</IntlProvider>
);
const StudioHeaderContext = ({ actionRowContent = null }) => {
const headerContextValue = useMemo(() => ({
authenticatedUser: {
userId: 'abc123',
username: 'edX',
roles: [],
administrator: false,
},
config: {
STUDIO_BASE_URL: process.env.STUDIO_BASE_URL,
SITE_NAME: process.env.SITE_NAME,
LOGIN_URL: process.env.LOGIN_URL,
LOGOUT_URL: process.env.LOGOUT_URL,
LOGO_URL: process.env.LOGO_URL,
},
}), []);
return (
<IntlProvider locale="en" messages={{}}>
<AppContext.Provider
value={headerContextValue}
>
<StudioHeader actionRowContent={actionRowContent} />
</AppContext.Provider>
</IntlProvider>
);
};
describe('<StudioHeader />', () => {
it('renders correctly', () => {
const contextValue = {
authenticatedUser: {
userId: 'abc123',
username: 'edX',
roles: [],
administrator: false,
},
config: {
STUDIO_BASE_URL: process.env.STUDIO_BASE_URL,
SITE_NAME: process.env.SITE_NAME,
LOGIN_URL: process.env.LOGIN_URL,
LOGOUT_URL: process.env.LOGOUT_URL,
LOGO_URL: process.env.LOGO_URL,
},
};
const component = <StudioHeaderComponent contextValue={contextValue} />;
const wrapper = TestRenderer.create(component);
expect(wrapper.toJSON()).toMatchSnapshot();
});
it('renders correctly with optional action row content', () => {
const actionRowContent = (
<>
<Dropdown>
<Dropdown.Toggle variant="outline-primary" id="library-header-menu-dropdown">
Settings
</Dropdown.Toggle>
<Dropdown.Menu>
<Dropdown.Item as={Link} to="#">Dropdown Item 1</Dropdown.Item>
<Dropdown.Item as={Link} to="#">Dropdown Item 2</Dropdown.Item>
<Dropdown.Item as={Link} to="#">Dropdown Item 3</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
<ActionRow.Spacer />
<Button
variant="tertiary"
href="#"
rel="noopener noreferrer"
target="_blank"
title="Help Button"
>Help
</Button>
</>
);
const component = <StudioHeaderContext actionRowContent={actionRowContent} />;
const wrapper = TestRenderer.create(component);
expect(wrapper.toJSON()).toMatchSnapshot();
});
});

View File

@@ -1,226 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<StudioHeader /> renders correctly 1`] = `
<header
className="site-header-desktop"
>
<a
className="nav-skip sr-only sr-only-focusable"
href="#main"
>
Skip to main content
</a>
<div
className="container-fluid null"
>
<div
className="nav-container position-relative d-flex align-items-center"
>
<img
alt="edX"
className="logo"
src="https://edx-cdn.org/v3/default/logo.svg"
/>
<div
className="pgn__action-row"
>
<nav
aria-label="Secondary"
className="nav secondary-menu-container align-items-center ml-auto"
>
<div
className="menu null"
onKeyDown={[Function]}
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
<button
aria-expanded={false}
aria-haspopup="menu"
aria-label="Account menu for edX"
className="menu-trigger btn btn-outline-primary d-inline-flex align-items-center pl-2 pr-3"
onClick={[Function]}
>
<span
className="avatar overflow-hidden d-inline-flex rounded-circle mr-2"
style={
Object {
"height": "1.5em",
"width": "1.5em",
}
}
>
<svg
aria-hidden={true}
focusable="false"
height="24px"
role="img"
style={
Object {
"height": "1.5em",
"width": "1.5em",
}
}
version="1.1"
viewBox="0 0 24 24"
width="24px"
>
<path
d="M4.10255106,18.1351061 C4.7170266,16.0581859 8.01891846,14.4720277 12,14.4720277 C15.9810815,14.4720277 19.2829734,16.0581859 19.8974489,18.1351061 C21.215206,16.4412566 22,14.3122775 22,12 C22,6.4771525 17.5228475,2 12,2 C6.4771525,2 2,6.4771525 2,12 C2,14.3122775 2.78479405,16.4412566 4.10255106,18.1351061 Z M12,24 C5.372583,24 0,18.627417 0,12 C0,5.372583 5.372583,0 12,0 C18.627417,0 24,5.372583 24,12 C24,18.627417 18.627417,24 12,24 Z M12,13 C9.790861,13 8,11.209139 8,9 C8,6.790861 9.790861,5 12,5 C14.209139,5 16,6.790861 16,9 C16,11.209139 14.209139,13 12,13 Z"
fill="currentColor"
/>
</svg>
</span>
edX
<svg
aria-hidden={true}
focusable="false"
height="16px"
role="img"
version="1.1"
viewBox="0 0 16 16"
width="16px"
>
<path
d="M7,4 L7,8 L11,8 L11,10 L5,10 L5,4 L7,4 Z"
fill="currentColor"
transform="translate(8.000000, 7.000000) rotate(-45.000000) translate(-8.000000, -7.000000) "
/>
</svg>
</button>
</div>
</nav>
</div>
</div>
</div>
</header>
`;
exports[`<StudioHeader /> renders correctly with optional action row content 1`] = `
<header
className="site-header-desktop"
>
<a
className="nav-skip sr-only sr-only-focusable"
href="#main"
>
Skip to main content
</a>
<div
className="container-fluid null"
>
<div
className="nav-container position-relative d-flex align-items-center"
>
<img
alt="edX"
className="logo"
src="https://edx-cdn.org/v3/default/logo.svg"
/>
<div
className="pgn__action-row"
>
<div
className="dropdown"
data-testid="dropdown"
>
<button
aria-expanded={false}
aria-haspopup={true}
className="dropdown-toggle btn btn-outline-primary"
disabled={false}
id="library-header-menu-dropdown"
onClick={[Function]}
type="button"
>
Settings
</button>
</div>
<span
className="pgn__action-row-spacer"
/>
<a
className="btn btn-tertiary"
href="#"
onClick={[Function]}
onKeyDown={[Function]}
rel="noopener noreferrer"
role="button"
target="_blank"
title="Help Button"
>
Help
</a>
<nav
aria-label="Secondary"
className="nav secondary-menu-container align-items-center ml-auto"
>
<div
className="menu null"
onKeyDown={[Function]}
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
<button
aria-expanded={false}
aria-haspopup="menu"
aria-label="Account menu for edX"
className="menu-trigger btn btn-outline-primary d-inline-flex align-items-center pl-2 pr-3"
onClick={[Function]}
>
<span
className="avatar overflow-hidden d-inline-flex rounded-circle mr-2"
style={
Object {
"height": "1.5em",
"width": "1.5em",
}
}
>
<svg
aria-hidden={true}
focusable="false"
height="24px"
role="img"
style={
Object {
"height": "1.5em",
"width": "1.5em",
}
}
version="1.1"
viewBox="0 0 24 24"
width="24px"
>
<path
d="M4.10255106,18.1351061 C4.7170266,16.0581859 8.01891846,14.4720277 12,14.4720277 C15.9810815,14.4720277 19.2829734,16.0581859 19.8974489,18.1351061 C21.215206,16.4412566 22,14.3122775 22,12 C22,6.4771525 17.5228475,2 12,2 C6.4771525,2 2,6.4771525 2,12 C2,14.3122775 2.78479405,16.4412566 4.10255106,18.1351061 Z M12,24 C5.372583,24 0,18.627417 0,12 C0,5.372583 5.372583,0 12,0 C18.627417,0 24,5.372583 24,12 C24,18.627417 18.627417,24 12,24 Z M12,13 C9.790861,13 8,11.209139 8,9 C8,6.790861 9.790861,5 12,5 C14.209139,5 16,6.790861 16,9 C16,11.209139 14.209139,13 12,13 Z"
fill="currentColor"
/>
</svg>
</span>
edX
<svg
aria-hidden={true}
focusable="false"
height="16px"
role="img"
version="1.1"
viewBox="0 0 16 16"
width="16px"
>
<path
d="M7,4 L7,8 L11,8 L11,10 L5,10 L5,4 L7,4 Z"
fill="currentColor"
transform="translate(8.000000, 7.000000) rotate(-45.000000) translate(-8.000000, -7.000000) "
/>
</svg>
</button>
</div>
</nav>
</div>
</div>
</div>
</header>
`;

View File

@@ -1,6 +1,4 @@
{
"general.register.sentenceCase": "التسجيل",
"general.signIn.sentenceCase": "تسجيل الدخول",
"header.links.courses": "المساقات",
"header.links.programs": "البرامج",
"header.links.content.search": "اكتشف الجديد",
@@ -23,11 +21,16 @@
"header.label.secondary.nav": "القائمة الثانوية",
"header.label.skip.nav": "التخطي إلى المحتوى الرئيسي",
"header.label.app.nav": "تطبيق",
"general.register.sentenceCase": "التسجيل",
"general.signIn.sentenceCase": "تسجيل الدخول",
"header.menu.dashboard.label": "لوحة المعلومات",
"header.help.label": "المساعدة",
"header.menu.profile.label": "الملف الشخصي",
"header.menu.account.label": "الحساب",
"header.menu.orderHistory.label": "سجل الطلبيات",
"header.navigation.skipNavLink": "التخطي إلى المحتوى الرئيسي",
"header.menu.signOut.label": "تسجيل الخروج"
"header.menu.signOut.label": "تسجيل الخروج",
"header.user.menu.studio": "Studio Home",
"header.user.menu.maintenance": "Maintenance",
"header.label.courseOutline": "Back to course outline in Studio"
}

View File

@@ -1,6 +1,4 @@
{
"general.register.sentenceCase": "Register",
"general.signIn.sentenceCase": "Sign in",
"header.links.courses": "Courses",
"header.links.programs": "Programs",
"header.links.content.search": "Discover New",
@@ -23,11 +21,16 @@
"header.label.secondary.nav": "Secondary",
"header.label.skip.nav": "Skip to main content",
"header.label.app.nav": "App",
"general.register.sentenceCase": "Register",
"general.signIn.sentenceCase": "Sign in",
"header.menu.dashboard.label": "Dashboard",
"header.help.label": "Help",
"header.menu.profile.label": "Profile",
"header.menu.account.label": "Account",
"header.menu.orderHistory.label": "Order History",
"header.navigation.skipNavLink": "Skip to main content.",
"header.menu.signOut.label": "Sign Out"
"header.menu.signOut.label": "Sign Out",
"header.user.menu.studio": "Studio Home",
"header.user.menu.maintenance": "Maintenance",
"header.label.courseOutline": "Back to course outline in Studio"
}

View File

@@ -1,6 +1,4 @@
{
"general.register.sentenceCase": "Registrarse",
"general.signIn.sentenceCase": "Iniciar sesión",
"header.links.courses": "Cursos",
"header.links.programs": "Programas",
"header.links.content.search": "Encontrar nuevo",
@@ -23,11 +21,16 @@
"header.label.secondary.nav": "Secondary",
"header.label.skip.nav": "Ir al contenido principal",
"header.label.app.nav": "Aplicación",
"general.register.sentenceCase": "Registrarse",
"general.signIn.sentenceCase": "Iniciar sesión",
"header.menu.dashboard.label": "Panel de Control",
"header.help.label": "Ayuda",
"header.menu.profile.label": "Perfil",
"header.menu.account.label": "Cuenta",
"header.menu.orderHistory.label": "Historial de órdenes",
"header.navigation.skipNavLink": "Dirígete al contenido principal.",
"header.menu.signOut.label": "Cerrar sesión"
"header.menu.signOut.label": "Cerrar sesión",
"header.user.menu.studio": "Inicio Studio",
"header.user.menu.maintenance": "Mantenimiento",
"header.label.courseOutline": "Volver al esquema del curso en Studio"
}

View File

@@ -1,6 +1,4 @@
{
"general.register.sentenceCase": "S'inscrire",
"general.signIn.sentenceCase": "Connectez-vous",
"header.links.courses": "Cours",
"header.links.programs": "Programmes",
"header.links.content.search": "Explorer les cours",
@@ -23,11 +21,16 @@
"header.label.secondary.nav": "Secondaire",
"header.label.skip.nav": "Passer au contenu principal",
"header.label.app.nav": "Application",
"general.register.sentenceCase": "S'inscrire",
"general.signIn.sentenceCase": "Connectez-vous",
"header.menu.dashboard.label": "Tableau de bord",
"header.help.label": "Aide",
"header.menu.profile.label": "Profil",
"header.menu.account.label": "Compte",
"header.menu.orderHistory.label": "Historique des commandes",
"header.navigation.skipNavLink": "Passer au contenu principal",
"header.menu.signOut.label": "Se déconnecter"
"header.menu.signOut.label": "Se déconnecter",
"header.user.menu.studio": "Studio Home",
"header.user.menu.maintenance": "Maintenance",
"header.label.courseOutline": "Back to course outline in Studio"
}

View File

@@ -1,6 +1,4 @@
{
"general.register.sentenceCase": "Inscription",
"general.signIn.sentenceCase": "Connexion",
"header.links.courses": "Cours",
"header.links.programs": "Programmes",
"header.links.content.search": "Découvrir les nouveautés",
@@ -23,11 +21,16 @@
"header.label.secondary.nav": "Secondaire",
"header.label.skip.nav": "Passer au contenu de cette vue",
"header.label.app.nav": "Application",
"general.register.sentenceCase": "Inscription",
"general.signIn.sentenceCase": "Connexion",
"header.menu.dashboard.label": "Tableau de bord",
"header.help.label": "Aide",
"header.menu.profile.label": "Profil",
"header.menu.account.label": "Compte",
"header.menu.orderHistory.label": "Historique des commandes",
"header.navigation.skipNavLink": "Passer au contenu principal.",
"header.menu.signOut.label": "Se déconnecter"
"header.menu.signOut.label": "Se déconnecter",
"header.user.menu.studio": "Accueil Studio",
"header.user.menu.maintenance": "Entretien",
"header.label.courseOutline": "Retour au plan de cours dans Studio"
}

View File

@@ -1,33 +1,36 @@
{
"general.register.sentenceCase": "Register",
"general.signIn.sentenceCase": "Sign in",
"header.links.courses": "Courses",
"header.links.programs": "Programs",
"header.links.content.search": "Discover New",
"header.links.schools": "Schools & Partners",
"header.user.menu.dashboard": "Dashboard",
"header.user.menu.profile": "Profile",
"header.user.menu.account.settings": "Account",
"header.user.menu.order.history": "Order History",
"header.user.menu.logout": "Logout",
"header.user.menu.login": "Login",
"header.user.menu.register": "Sign Up",
"header.user.menu.studio.home": "Studio Home",
"header.user.menu.studio.maintenance": "Maintenance",
"header.label.account.nav": "Account",
"header.label.account.menu": "Account Menu",
"header.label.account.menu.for": "Account menu for {username}",
"header.label.main.nav": "Main",
"header.label.main.menu": "Main Menu",
"header.label.main.header": "Main",
"header.label.secondary.nav": "Secondary",
"header.label.skip.nav": "Skip to main content",
"header.label.app.nav": "App",
"header.menu.dashboard.label": "Dashboard",
"header.help.label": "Help",
"header.menu.profile.label": "Profile",
"header.menu.account.label": "Account",
"header.menu.orderHistory.label": "Order History",
"header.navigation.skipNavLink": "Skip to main content.",
"header.menu.signOut.label": "Sign Out"
"header.links.courses": "पाठ्यक्रम",
"header.links.programs": "कार्यक्रमों",
"header.links.content.search": "नया खोजें",
"header.links.schools": "स्कूलों और भागीदारों",
"header.user.menu.dashboard": "डैशबोर्ड",
"header.user.menu.profile": "प्रोफ़ाइल",
"header.user.menu.account.settings": "खाता",
"header.user.menu.order.history": "ऑर्डर इतिहास",
"header.user.menu.logout": "लॉग आउट",
"header.user.menu.login": "लॉगिन",
"header.user.menu.register": "साइन अप",
"header.user.menu.studio.home": "स्टूडियो होम",
"header.user.menu.studio.maintenance": "अनुरक्षण करना",
"header.label.account.nav": "खाता",
"header.label.account.menu": "खाता मेनू",
"header.label.account.menu.for": "{username} के लिए खाता मेनू",
"header.label.main.nav": "मुख्य",
"header.label.main.menu": "मुख्य मेनू",
"header.label.main.header": "मुख्य",
"header.label.secondary.nav": "माध्यमिक",
"header.label.skip.nav": "मुख्य विषयवस्तु में जाएं",
"header.label.app.nav": "ऐप",
"general.register.sentenceCase": "रजिस्टर करें",
"general.signIn.sentenceCase": "साइन इन करें",
"header.menu.dashboard.label": "डैशबोर्ड",
"header.help.label": "मदद",
"header.menu.profile.label": "प्रोफ़ाइल",
"header.menu.account.label": "खाता",
"header.menu.orderHistory.label": "ऑर्डर इतिहास",
"header.navigation.skipNavLink": "मुख्य सामग्री पर जाएँ।",
"header.menu.signOut.label": "साइन आउट करें",
"header.user.menu.studio": "स्टूडियो होम",
"header.user.menu.maintenance": "अनुरक्षण करना",
"header.label.courseOutline": "स्टूडियो में पाठ्यक्रम की रूपरेखा पर वापस जाएँ"
}

View File

@@ -1,6 +1,4 @@
{
"general.register.sentenceCase": "Register",
"general.signIn.sentenceCase": "Sign in",
"header.links.courses": "Courses",
"header.links.programs": "Programs",
"header.links.content.search": "Discover New",
@@ -23,11 +21,16 @@
"header.label.secondary.nav": "Secondary",
"header.label.skip.nav": "Skip to main content",
"header.label.app.nav": "App",
"general.register.sentenceCase": "Register",
"general.signIn.sentenceCase": "Sign in",
"header.menu.dashboard.label": "Dashboard",
"header.help.label": "Help",
"header.menu.profile.label": "Profile",
"header.menu.account.label": "Account",
"header.menu.orderHistory.label": "Order History",
"header.navigation.skipNavLink": "Skip to main content.",
"header.menu.signOut.label": "Sign Out"
"header.menu.signOut.label": "Sign Out",
"header.user.menu.studio": "Studio Home",
"header.user.menu.maintenance": "Maintenance",
"header.label.courseOutline": "Back to course outline in Studio"
}

View File

@@ -1,6 +1,4 @@
{
"general.register.sentenceCase": "Register",
"general.signIn.sentenceCase": "Sign in",
"header.links.courses": "Courses",
"header.links.programs": "Programs",
"header.links.content.search": "Discover New",
@@ -23,11 +21,16 @@
"header.label.secondary.nav": "Secondary",
"header.label.skip.nav": "Skip to main content",
"header.label.app.nav": "App",
"general.register.sentenceCase": "Register",
"general.signIn.sentenceCase": "Sign in",
"header.menu.dashboard.label": "Dashboard",
"header.help.label": "Help",
"header.menu.profile.label": "Profile",
"header.menu.account.label": "Account",
"header.menu.orderHistory.label": "Order History",
"header.navigation.skipNavLink": "Skip to main content.",
"header.menu.signOut.label": "Sign Out"
"header.menu.signOut.label": "Sign Out",
"header.user.menu.studio": "Studio Home",
"header.user.menu.maintenance": "Maintenance",
"header.label.courseOutline": "Back to course outline in Studio"
}

View File

@@ -1,6 +1,4 @@
{
"general.register.sentenceCase": "Register",
"general.signIn.sentenceCase": "Sign in",
"header.links.courses": "Courses",
"header.links.programs": "Programs",
"header.links.content.search": "Discover New",
@@ -23,11 +21,16 @@
"header.label.secondary.nav": "Secondary",
"header.label.skip.nav": "Skip to main content",
"header.label.app.nav": "App",
"general.register.sentenceCase": "Register",
"general.signIn.sentenceCase": "Sign in",
"header.menu.dashboard.label": "Dashboard",
"header.help.label": "Help",
"header.menu.profile.label": "Profile",
"header.menu.account.label": "Account",
"header.menu.orderHistory.label": "Order History",
"header.navigation.skipNavLink": "Skip to main content.",
"header.menu.signOut.label": "Sign Out"
"header.menu.signOut.label": "Sign Out",
"header.user.menu.studio": "Studio Home",
"header.user.menu.maintenance": "Maintenance",
"header.label.courseOutline": "Back to course outline in Studio"
}

View File

@@ -1,8 +1,6 @@
{
"general.register.sentenceCase": "Register",
"general.signIn.sentenceCase": "Sign in",
"header.links.courses": "Courses",
"header.links.programs": "Programs",
"header.links.courses": "Курси",
"header.links.programs": "Програми",
"header.links.content.search": "Discover New",
"header.links.schools": "Schools & Partners",
"header.user.menu.dashboard": "Dashboard",
@@ -15,19 +13,24 @@
"header.user.menu.studio.home": "Studio Home",
"header.user.menu.studio.maintenance": "Maintenance",
"header.label.account.nav": "Account",
"header.label.account.menu": "Account Menu",
"header.label.account.menu.for": "Account menu for {username}",
"header.label.account.menu": "Меню облікового запису",
"header.label.account.menu.for": "Меню облікового запису для {username}",
"header.label.main.nav": "Main",
"header.label.main.menu": "Main Menu",
"header.label.main.header": "Main",
"header.label.secondary.nav": "Secondary",
"header.label.skip.nav": "Skip to main content",
"header.label.skip.nav": "Перейти до головного змісту",
"header.label.app.nav": "App",
"general.register.sentenceCase": "Register",
"general.signIn.sentenceCase": "Увійти",
"header.menu.dashboard.label": "Dashboard",
"header.help.label": "Help",
"header.menu.profile.label": "Profile",
"header.menu.account.label": "Account",
"header.menu.orderHistory.label": "Order History",
"header.navigation.skipNavLink": "Skip to main content.",
"header.menu.signOut.label": "Sign Out"
"header.navigation.skipNavLink": "Перейти до головного змісту.",
"header.menu.signOut.label": "Sign Out",
"header.user.menu.studio": "Studio Home",
"header.user.menu.maintenance": "Maintenance",
"header.label.courseOutline": "Back to course outline in Studio"
}

View File

@@ -1,6 +1,4 @@
{
"general.register.sentenceCase": "Register",
"general.signIn.sentenceCase": "Sign in",
"header.links.courses": "Courses",
"header.links.programs": "Programs",
"header.links.content.search": "Discover New",
@@ -23,11 +21,16 @@
"header.label.secondary.nav": "Secondary",
"header.label.skip.nav": "Skip to main content",
"header.label.app.nav": "App",
"general.register.sentenceCase": "Register",
"general.signIn.sentenceCase": "Sign in",
"header.menu.dashboard.label": "Dashboard",
"header.help.label": "Help",
"header.menu.profile.label": "Profile",
"header.menu.account.label": "Account",
"header.menu.orderHistory.label": "Order History",
"header.navigation.skipNavLink": "Skip to main content.",
"header.menu.signOut.label": "Sign Out"
"header.menu.signOut.label": "Sign Out",
"header.user.menu.studio": "Studio Home",
"header.user.menu.maintenance": "Maintenance",
"header.label.courseOutline": "Back to course outline in Studio"
}

View File

@@ -1,7 +1,7 @@
import Header from './Header';
import LearningHeader from './learning-header/LearningHeader';
import messages from './i18n/index';
import StudioHeader from './StudioHeader';
import StudioHeader from './studio-header';
export { LearningHeader, messages, StudioHeader };

View File

@@ -3,6 +3,7 @@ $blue: #007db8;
$white: #fff;
@import './Menu/menu.scss';
@import './studio-header/StudioHeader.scss';
.dropdown-item a {
text-decoration: none;
@@ -27,7 +28,7 @@ $white: #fff;
.learning-header {
min-width: 0;
.course-title-lockup {
min-width: 0;
@@ -42,9 +43,9 @@ $white: #fff;
.user-dropdown {
.btn {
height: 3rem;
@media (max-width: -1 + map-get($grid-breakpoints, "sm")) {
padding: 0 0.5rem;
}
// @media (max-width: -1 + map-get($grid-breakpoints, "sm")) {
// padding: 0 0.5rem;
// }
}
}
}

View File

@@ -3,7 +3,7 @@ import React from 'react';
import { getConfig } from '@edx/frontend-platform';
import { getLoginRedirectUrl } from '@edx/frontend-platform/auth';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Button } from '@edx/paragon';
import { Button } from '@openedx/paragon';
import genericMessages from '../generic/messages';

View File

@@ -3,10 +3,9 @@ import PropTypes from 'prop-types';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faUserCircle } from '@fortawesome/free-solid-svg-icons';
import { getConfig } from '@edx/frontend-platform';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Dropdown } from '@edx/paragon';
import { Dropdown } from '@openedx/paragon';
import messages from './messages';
@@ -19,8 +18,8 @@ const AuthenticatedUserDropdown = ({ intl, username }) => {
return (
<>
<a className="text-gray-700 mr-3" href={`${getConfig().SUPPORT_URL}`}>{intl.formatMessage(messages.help)}</a>
<Dropdown className="user-dropdown">
<a className="text-gray-700" href={`${getConfig().SUPPORT_URL}`}>{intl.formatMessage(messages.help)}</a>
<Dropdown className="user-dropdown ml-3">
<Dropdown.Toggle variant="outline-primary">
<FontAwesomeIcon icon={faUserCircle} className="d-md-none" size="lg" />
<span data-hj-suppress className="d-none d-md-inline">
@@ -29,10 +28,10 @@ const AuthenticatedUserDropdown = ({ intl, username }) => {
</Dropdown.Toggle>
<Dropdown.Menu className="dropdown-menu-right">
{dashboardMenuItem}
<Dropdown.Item href={`${getConfig().LMS_BASE_URL}/u/${username}`}>
<Dropdown.Item href={`${getConfig().ACCOUNT_PROFILE_URL}/u/${username}`}>
{intl.formatMessage(messages.profile)}
</Dropdown.Item>
<Dropdown.Item href={`${getConfig().LMS_BASE_URL}/account/settings`}>
<Dropdown.Item href={getConfig().ACCOUNT_SETTINGS_URL}>
{intl.formatMessage(messages.account)}
</Dropdown.Item>
{ getConfig().ORDER_HISTORY_URL && (

View File

@@ -49,12 +49,12 @@ const LearningHeader = ({
<span className="d-block m-0 font-weight-bold course-title">{courseTitle}</span>
</div>
{showUserDropdown && authenticatedUser && (
<AuthenticatedUserDropdown
username={authenticatedUser.username}
/>
<AuthenticatedUserDropdown
username={authenticatedUser.username}
/>
)}
{showUserDropdown && !authenticatedUser && (
<AnonymousUserMenu />
<AnonymousUserMenu />
)}
</div>
</header>

View File

@@ -12,7 +12,7 @@ describe('Header', () => {
it('displays user button', () => {
render(<Header />);
expect(screen.getByRole('button')).toHaveTextContent(authenticatedUser.username);
expect(screen.getByText(authenticatedUser.username)).toBeInTheDocument();
});
it('displays course data', () => {

View File

@@ -1,9 +1,7 @@
/* eslint-disable import/no-extraneous-dependencies */
import Enzyme from 'enzyme';
import React from 'react';
import PropTypes from 'prop-types';
import Adapter from 'enzyme-adapter-react-16';
import '@testing-library/jest-dom';
import '@testing-library/jest-dom/extend-expect';
import 'babel-polyfill';
@@ -17,11 +15,11 @@ import { IntlProvider } from 'react-intl';
import AppProvider from '@edx/frontend-platform/react/AppProvider';
import appMessages from './i18n';
Enzyme.configure({ adapter: new Adapter() });
// These configuration values are usually set in webpack's EnvironmentPlugin however
// Jest does not use webpack so we need to set these so for testing
process.env.ACCESS_TOKEN_COOKIE_NAME = 'edx-jwt-cookie-header-payload';
process.env.ACCOUNT_PROFILE_URL = 'http://localhost:1995';
process.env.ACCOUNT_SETTINGS_URL = 'http://localhost:1997';
process.env.BASE_URL = 'localhost:1995';
process.env.CREDENTIALS_BASE_URL = 'http://localhost:18150';
process.env.CSRF_TOKEN_API_PATH = '/csrf/api/v1/token';

View File

@@ -0,0 +1,24 @@
import React from 'react';
import PropTypes from 'prop-types';
const BrandNav = ({
studioBaseUrl,
logo,
logoAltText,
}) => (
<a href={studioBaseUrl}>
<img
src={logo}
alt={logoAltText}
className="d-block logo"
/>
</a>
);
BrandNav.propTypes = {
studioBaseUrl: PropTypes.string.isRequired,
logo: PropTypes.string.isRequired,
logoAltText: PropTypes.string.isRequired,
};
export default BrandNav;

View File

@@ -0,0 +1,54 @@
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import {
OverlayTrigger,
Tooltip,
} from '@openedx/paragon';
import messages from './messages';
const CourseLockUp = ({
outlineLink,
org,
number,
title,
// injected
intl,
}) => (
<OverlayTrigger
placement="bottom"
overlay={(
<Tooltip id="course-lock-up">
{title}
</Tooltip>
)}
>
<a
className="course-title-lockup mr-2"
href={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>
</OverlayTrigger>
);
CourseLockUp.propTypes = {
number: PropTypes.string,
org: PropTypes.string,
title: PropTypes.string,
outlineLink: PropTypes.string,
// injected
intl: intlShape.isRequired,
};
CourseLockUp.defaultProps = {
number: null,
org: null,
title: null,
outlineLink: null,
};
export default injectIntl(CourseLockUp);

View File

@@ -0,0 +1,160 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
ActionRow,
Button,
Container,
Nav,
Row,
} from '@openedx/paragon';
import { Close, MenuIcon } from '@openedx/paragon/icons';
import CourseLockUp from './CourseLockUp';
import UserMenu from './UserMenu';
import BrandNav from './BrandNav';
import NavDropdownMenu from './NavDropdownMenu';
const HeaderBody = ({
logo,
logoAltText,
number,
org,
title,
username,
isAdmin,
studioBaseUrl,
logoutUrl,
authenticatedUserAvatar,
isMobile,
setModalPopupTarget,
toggleModalPopup,
isModalPopupOpen,
isHiddenMainMenu,
mainMenuDropdowns,
outlineLink,
}) => {
const renderBrandNav = (
<BrandNav
{...{
studioBaseUrl,
logo,
logoAltText,
}}
/>
);
return (
<Container size="xl" className="px-2.5">
<ActionRow as="header">
{isHiddenMainMenu ? (
<Row className="flex-nowrap ml-4">
{renderBrandNav}
</Row>
) : (
<>
{isMobile ? (
<Button
ref={setModalPopupTarget}
className="d-inline-flex align-items-center"
variant="tertiary"
onClick={toggleModalPopup}
iconBefore={isModalPopupOpen ? Close : MenuIcon}
data-testid="mobile-menu-button"
>
Menu
</Button>
) : (
<div className="w-25">
<Row className="m-0 flex-nowrap">
{renderBrandNav}
<CourseLockUp
{...{
outlineLink,
number,
org,
title,
}}
/>
</Row>
</div>
)}
{isMobile ? (
<>
<ActionRow.Spacer />
{renderBrandNav}
</>
) : (
<Nav data-testid="desktop-menu" className="ml-2">
{mainMenuDropdowns.map(dropdown => {
const { id, buttonTitle, items } = dropdown;
return (
<NavDropdownMenu key={id} {...{ id, buttonTitle, items }} />
);
})}
</Nav>
)}
</>
)}
<ActionRow.Spacer />
<Nav>
<UserMenu
{...{
username,
studioBaseUrl,
logoutUrl,
authenticatedUserAvatar,
isAdmin,
}}
/>
</Nav>
</ActionRow>
</Container>
);
};
HeaderBody.propTypes = {
studioBaseUrl: PropTypes.string.isRequired,
logoutUrl: PropTypes.string.isRequired,
setModalPopupTarget: PropTypes.func,
toggleModalPopup: PropTypes.func,
isModalPopupOpen: PropTypes.bool,
number: PropTypes.string,
org: PropTypes.string,
title: PropTypes.string,
logo: PropTypes.string,
logoAltText: PropTypes.string,
authenticatedUserAvatar: PropTypes.string,
username: PropTypes.string,
isAdmin: PropTypes.bool,
isMobile: PropTypes.bool,
isHiddenMainMenu: PropTypes.bool,
mainMenuDropdowns: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string,
buttonTitle: PropTypes.string,
items: PropTypes.arrayOf(PropTypes.shape({
href: PropTypes.string,
title: PropTypes.string,
})),
})),
outlineLink: PropTypes.string,
};
HeaderBody.defaultProps = {
setModalPopupTarget: null,
toggleModalPopup: null,
isModalPopupOpen: false,
logo: null,
logoAltText: null,
number: '',
org: '',
title: '',
authenticatedUserAvatar: null,
username: null,
isAdmin: false,
isMobile: false,
isHiddenMainMenu: false,
mainMenuDropdowns: [],
outlineLink: null,
};
export default HeaderBody;

View File

@@ -0,0 +1,73 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { useToggle, ModalPopup } from '@openedx/paragon';
import HeaderBody from './HeaderBody';
import MobileMenu from './MobileMenu';
const MobileHeader = ({
mainMenuDropdowns,
...props
}) => {
const [isOpen, , close, toggle] = useToggle(false);
const [target, setTarget] = useState(null);
return (
<>
<HeaderBody
{...props}
isMobile
setModalPopupTarget={setTarget}
toggleModalPopup={toggle}
isModalPopupOpen={isOpen}
/>
<ModalPopup
hasArrow
placement="bottom"
positionRef={target}
isOpen={isOpen}
onClose={close}
onEscapeKey={close}
className="mobile-menu-container"
>
<MobileMenu {...{ mainMenuDropdowns }} />
</ModalPopup>
</>
);
};
MobileHeader.propTypes = {
studioBaseUrl: PropTypes.string.isRequired,
logoutUrl: PropTypes.string.isRequired,
number: PropTypes.string,
org: PropTypes.string,
title: PropTypes.string,
logo: PropTypes.string,
logoAltText: PropTypes.string,
authenticatedUserAvatar: PropTypes.string,
username: PropTypes.string,
isAdmin: PropTypes.bool,
mainMenuDropdowns: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string,
buttonTitle: PropTypes.string,
items: PropTypes.arrayOf(PropTypes.shape({
href: PropTypes.string,
title: PropTypes.string,
})),
})),
outlineLink: PropTypes.string,
};
MobileHeader.defaultProps = {
logo: null,
logoAltText: null,
number: null,
org: null,
title: null,
authenticatedUserAvatar: null,
username: null,
isAdmin: false,
mainMenuDropdowns: [],
outlineLink: null,
};
export default MobileHeader;

View File

@@ -0,0 +1,51 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Collapsible } from '@openedx/paragon';
const MobileMenu = ({
mainMenuDropdowns,
}) => (
<div
className="ml-4 p-2 bg-light-100 border border-gray-200 small rounded"
data-testid="mobile-menu"
>
<div>
{mainMenuDropdowns.map(dropdown => {
const { id, buttonTitle, items } = dropdown;
return (
<Collapsible
className="border-light-100"
title={buttonTitle}
key={id}
>
<ul className="p-0" style={{ listStyleType: 'none' }}>
{items.map(item => (
<li className="mobile-menu-item">
<a href={item.href}>
{item.title}
</a>
</li>
))}
</ul>
</Collapsible>
);
})}
</div>
</div>
);
MobileMenu.propTypes = {
mainMenuDropdowns: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string,
buttonTitle: PropTypes.string,
items: PropTypes.arrayOf(PropTypes.shape({
href: PropTypes.string,
title: PropTypes.string,
})),
})),
};
MobileMenu.defaultProps = {
mainMenuDropdowns: [],
};
export default MobileMenu;

View File

@@ -0,0 +1,40 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
Dropdown,
DropdownButton,
} from '@openedx/paragon';
const NavDropdownMenu = ({
id,
buttonTitle,
items,
}) => (
<DropdownButton
id={id}
title={buttonTitle}
variant="outline-primary"
className="mr-2"
>
{items.map(item => (
<Dropdown.Item
key={`${item.title}-dropdown-item`}
href={item.href}
className="small"
>
{item.title}
</Dropdown.Item>
))}
</DropdownButton>
);
NavDropdownMenu.propTypes = {
id: PropTypes.string.isRequired,
buttonTitle: PropTypes.string.isRequired,
items: PropTypes.arrayOf(PropTypes.shape({
href: PropTypes.string,
title: PropTypes.string,
})).isRequired,
};
export default NavDropdownMenu;

View File

@@ -0,0 +1,75 @@
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import Responsive from 'react-responsive';
import { AppContext } from '@edx/frontend-platform/react';
import { ensureConfig } from '@edx/frontend-platform';
import MobileHeader from './MobileHeader';
import HeaderBody from './HeaderBody';
ensureConfig([
'STUDIO_BASE_URL',
'SITE_NAME',
'LOGOUT_URL',
'LOGIN_URL',
'LOGO_URL',
], 'Studio Header component');
const StudioHeader = ({
number, org, title, isHiddenMainMenu, mainMenuDropdowns, outlineLink,
}) => {
const { authenticatedUser, config } = useContext(AppContext);
const props = {
logo: config.LOGO_URL,
logoAltText: `Studio ${config.SITE_NAME}`,
number,
org,
title,
username: authenticatedUser?.username,
isAdmin: authenticatedUser?.administrator,
authenticatedUserAvatar: authenticatedUser?.avatar,
studioBaseUrl: config.STUDIO_BASE_URL,
logoutUrl: config.LOGOUT_URL,
isHiddenMainMenu,
mainMenuDropdowns,
outlineLink,
};
return (
<div className="studio-header">
<a className="nav-skip sr-only sr-only-focusable" href="#main">Skip to content</a>
<Responsive maxWidth={841}>
<MobileHeader {...props} />
</Responsive>
<Responsive minWidth={842}>
<HeaderBody {...props} />
</Responsive>
</div>
);
};
StudioHeader.propTypes = {
number: PropTypes.string,
org: PropTypes.string,
title: PropTypes.string.isRequired,
isHiddenMainMenu: PropTypes.bool,
mainMenuDropdowns: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string,
buttonTitle: PropTypes.string,
items: PropTypes.arrayOf(PropTypes.shape({
href: PropTypes.string,
title: PropTypes.string,
})),
})),
outlineLink: PropTypes.string,
};
StudioHeader.defaultProps = {
number: '',
org: '',
isHiddenMainMenu: false,
mainMenuDropdowns: [],
outlineLink: null,
};
export default StudioHeader;

View File

@@ -0,0 +1,49 @@
$spacer: 1rem;
$white: #FFFFFF;
.studio-header {
position: relative;
z-index: 1000;
height: 3.75rem;
box-shadow: 0 1px 0 0 rgb(0 0 0 / .1);
background: $white;
.btn-outline-primary {
border-color: $white;
}
.logo {
display: block;
box-sizing: content-box;
position: relative;
top: -.05em;
height: 1.75rem;
padding: $spacer 0;
margin-right: $spacer;
img {
display: block;
height: 100%;
}
}
.course-title-lockup {
@media only screen and (min-width: 769px) {
padding: .5rem;
padding-right: $spacer;
border-right: 1px solid #E5E5E5;
width: 70%;
}
overflow: hidden;
span {
color: #333333;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.375rem;
}
}
}

View File

@@ -0,0 +1,197 @@
/* eslint-disable react/prop-types */
import React, { useMemo } from 'react';
import {
render,
fireEvent,
waitFor,
} from '@testing-library/react';
import { AppContext } from '@edx/frontend-platform/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { Context as ResponsiveContext } from 'react-responsive';
import StudioHeader from './StudioHeader';
import messages from './messages';
const authenticatedUser = {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
avatar: '/imges/test.png',
};
let currentUser;
let screenWidth = 1280;
const RootWrapper = ({
...props
}) => {
const appContextValue = useMemo(() => ({
authenticatedUser: currentUser,
config: {
LOGOUT_URL: process.env.LOGOUT_URL,
LOGO_URL: process.env.LOGO_URL,
SITE_NAME: process.env.SITE_NAME,
STUDIO_BASE_URL: process.env.STUDIO_BASE_URL,
LOGIN_URL: process.env.LOGIN_URL,
},
}), []);
const responsiveContextValue = useMemo(() => ({ width: screenWidth }), []);
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>
);
};
const props = {
number: '123',
org: 'Ed',
title: 'test',
mainMenuDropdowns: [
{
id: 'testId',
buttonTitle: 'test',
items: [
{
title: 'link',
href: '#',
},
],
},
],
outlineLink: 'tEsTLInK',
};
describe('Header', () => {
beforeEach(() => {
jest.clearAllMocks();
currentUser = authenticatedUser;
});
describe('desktop', () => {
it('course lock up should be visible', () => {
const { getByTestId } = render(<RootWrapper {...props} />);
const courseLockUpBlock = getByTestId('course-lock-up-block');
expect(courseLockUpBlock).toBeVisible();
});
it('mobile menu should not be visible', () => {
const { queryByTestId } = render(<RootWrapper {...props} />);
const mobileMenuButton = queryByTestId('mobile-menu-button');
expect(mobileMenuButton).toBeNull();
});
it('desktop menu should be visible', () => {
const { getByTestId } = render(<RootWrapper {...props} />);
const desktopMenu = getByTestId('desktop-menu');
expect(desktopMenu).toBeVisible();
});
it('should render one dropdown', async () => {
const { getAllByRole, getByText } = render(<RootWrapper {...props} />);
const dropdownMenu = getAllByRole('button')[0];
expect(dropdownMenu).toBeVisible();
await waitFor(() => fireEvent.click(dropdownMenu));
const dropdownOption = getByText('link');
expect(dropdownOption).toBeVisible();
});
it('maintenance should not be in user menu', async () => {
currentUser = { ...authenticatedUser, administrator: false };
const { getAllByRole, queryByText } = render(<RootWrapper {...props} />);
const userMenu = getAllByRole('button')[1];
await waitFor(() => fireEvent.click(userMenu));
const maintenanceButton = queryByText(messages['header.user.menu.maintenance'].defaultMessage);
expect(maintenanceButton).toBeNull();
});
it('user menu should use avatar icon', async () => {
currentUser = { ...authenticatedUser, avatar: null };
const { getByTestId } = render(<RootWrapper {...props} />);
const avatarIcon = getByTestId('avatar-icon');
expect(avatarIcon).toBeVisible();
});
it('should hide nav items if prop isHiddenMainMenu true', async () => {
const initialProps = { ...props, isHiddenMainMenu: true };
const { queryByTestId } = render(<RootWrapper {...initialProps} />);
const desktopMenu = queryByTestId('desktop-menu');
const mobileMenuButton = queryByTestId('mobile-menu-button');
expect(mobileMenuButton).toBeNull();
expect(desktopMenu).toBeNull();
});
});
describe('mobile', () => {
beforeEach(() => { screenWidth = 500; });
it('course lock up should not be visible', async () => {
const { queryByTestId } = render(<RootWrapper {...props} />);
const courseLockUpBlock = queryByTestId('course-lock-up-block');
expect(courseLockUpBlock).toBeNull();
});
it('mobile menu should be visible', async () => {
const { getByTestId } = render(<RootWrapper {...props} />);
const mobileMenuButton = getByTestId('mobile-menu-button');
expect(mobileMenuButton).toBeVisible();
await waitFor(() => fireEvent.click(mobileMenuButton));
const mobileMenu = getByTestId('mobile-menu');
expect(mobileMenu).toBeVisible();
});
it('desktop menu should not be visible', () => {
const { queryByTestId } = render(<RootWrapper {...props} />);
const desktopMenu = queryByTestId('desktop-menu');
expect(desktopMenu).toBeNull();
});
it('maintenance should be in user menu', async () => {
const { getAllByRole, getByText } = render(<RootWrapper {...props} />);
const userMenu = getAllByRole('button')[1];
await waitFor(() => fireEvent.click(userMenu));
const maintenanceButton = getByText(messages['header.user.menu.maintenance'].defaultMessage);
expect(maintenanceButton).toBeVisible();
});
it('user menu should use avatar image', async () => {
const { getByTestId } = render(<RootWrapper {...props} />);
const avatarImage = getByTestId('avatar-image');
expect(avatarImage).toBeVisible();
});
it('should hide nav items if prop isHiddenMainMenu true', async () => {
const initialProps = { ...props, isHiddenMainMenu: true };
const { queryByTestId } = render(<RootWrapper {...initialProps} />);
const desktopMenu = queryByTestId('desktop-menu');
const mobileMenuButton = queryByTestId('mobile-menu-button');
expect(mobileMenuButton).toBeNull();
expect(desktopMenu).toBeNull();
});
});
});

View File

@@ -0,0 +1,69 @@
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import {
Avatar,
} from '@openedx/paragon';
import NavDropdownMenu from './NavDropdownMenu';
import getUserMenuItems from './utils';
const UserMenu = ({
username,
studioBaseUrl,
logoutUrl,
authenticatedUserAvatar,
isMobile,
isAdmin,
// injected
intl,
}) => {
const avatar = authenticatedUserAvatar ? (
<img
className="d-block w-100 h-100"
src={authenticatedUserAvatar}
alt={username}
data-testid="avatar-image"
/>
) : (
<Avatar
size="sm"
className="mr-2"
alt={username}
data-testid="avatar-icon"
/>
);
const title = isMobile ? avatar : <>{avatar}{username}</>;
return (
<NavDropdownMenu
buttonTitle={title}
id="user-dropdown-menu"
items={getUserMenuItems({
studioBaseUrl,
logoutUrl,
intl,
isAdmin,
})}
/>
);
};
UserMenu.propTypes = {
username: PropTypes.string,
studioBaseUrl: PropTypes.string.isRequired,
logoutUrl: PropTypes.string.isRequired,
authenticatedUserAvatar: PropTypes.string,
isMobile: PropTypes.bool,
isAdmin: PropTypes.bool,
// injected
intl: intlShape.isRequired,
};
UserMenu.defaultProps = {
isMobile: false,
isAdmin: false,
authenticatedUserAvatar: null,
username: null,
};
export default injectIntl(UserMenu);

View File

@@ -0,0 +1,3 @@
import StudioHeader from './StudioHeader';
export default StudioHeader;

View File

@@ -0,0 +1,56 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
'header.user.menu.studio': {
id: 'header.user.menu.studio',
defaultMessage: 'Studio Home',
description: 'Link to Studio Home',
},
'header.user.menu.maintenance': {
id: 'header.user.menu.maintenance',
defaultMessage: 'Maintenance',
description: 'Link to the Studio maintenance page',
},
'header.user.menu.logout': {
id: 'header.user.menu.logout',
defaultMessage: 'Logout',
description: 'Logout link',
},
'header.label.account.menu': {
id: 'header.label.account.menu',
defaultMessage: 'Account Menu',
description: 'The aria label for the account menu trigger',
},
'header.label.account.menu.for': {
id: 'header.label.account.menu.for',
defaultMessage: 'Account menu for {username}',
description: 'The aria label for the account menu trigger when the username is displayed in it',
},
'header.label.main.nav': {
id: 'header.label.main.nav',
defaultMessage: 'Main',
description: 'The aria label for the main menu nav',
},
'header.label.main.menu': {
id: 'header.label.main.menu',
defaultMessage: 'Main Menu',
description: 'The aria label for the main menu trigger',
},
'header.label.main.header': {
id: 'header.label.main.header',
defaultMessage: 'Main',
description: 'The aria label for the main header',
},
'header.label.secondary.nav': {
id: 'header.label.secondary.nav',
defaultMessage: 'Secondary',
description: 'The aria label for the seconary nav',
},
'header.label.courseOutline': {
id: 'header.label.courseOutline',
defaultMessage: 'Back to course outline in Studio',
description: 'The aria label for the link back to the Studio Course Outline',
},
});
export default messages;

View File

@@ -0,0 +1,36 @@
import messages from './messages';
const getUserMenuItems = ({
studioBaseUrl,
logoutUrl,
intl,
isAdmin,
}) => {
let items = [
{
href: `${studioBaseUrl}`,
title: intl.formatMessage(messages['header.user.menu.studio']),
}, {
href: `${logoutUrl}`,
title: intl.formatMessage(messages['header.user.menu.logout']),
},
];
if (isAdmin) {
items = [
{
href: `${studioBaseUrl}`,
title: intl.formatMessage(messages['header.user.menu.studio']),
}, {
href: `${studioBaseUrl}/maintenance`,
title: intl.formatMessage(messages['header.user.menu.maintenance']),
}, {
href: `${logoutUrl}`,
title: intl.formatMessage(messages['header.user.menu.logout']),
},
];
}
return items;
};
export default getUserMenuItems;

6
src/test-utils.js Normal file
View File

@@ -0,0 +1,6 @@
const executeThunk = async (thunk, dispatch, getState) => {
await thunk(dispatch, getState);
await new Promise(setImmediate);
};
export default executeThunk;

View File

@@ -1,5 +1,5 @@
const path = require('path');
const { createConfig } = require('@edx/frontend-build');
const { createConfig } = require('@openedx/frontend-build');
module.exports = createConfig('webpack-dev', {
entry: path.resolve(__dirname, 'example'),