Compare commits

..

211 Commits

Author SHA1 Message Date
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
julianajlk
994b21c0c1 fix: Change frontend-platform peer dependency to v2 or v3 range 2022-11-14 20:19:37 +00:00
renovate[bot]
940b45ba7e chore(deps): update dependency jest to v29 2022-11-14 07:52:30 +00:00
dependabot[bot]
4efa0a07ae build(deps): bump loader-utils from 1.4.0 to 1.4.1 (#278)
Bumps [loader-utils](https://github.com/webpack/loader-utils) from 1.4.0 to 1.4.1.
- [Release notes](https://github.com/webpack/loader-utils/releases)
- [Changelog](https://github.com/webpack/loader-utils/blob/v1.4.1/CHANGELOG.md)
- [Commits](https://github.com/webpack/loader-utils/compare/v1.4.0...v1.4.1)

---
updated-dependencies:
- dependency-name: loader-utils
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-11-10 17:34:00 +05:00
renovate[bot]
2bd6879bda chore(deps): update actions/checkout action to v3 (#211)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-10 17:18:45 +05:00
Muhammad Abdullah Waheed
b479f0b376 Merge pull request #205 from openedx/dependabot/npm_and_yarn/async-2.6.4
build(deps): bump async from 2.6.3 to 2.6.4
2022-11-08 18:40:15 +05:00
Leangseu Kim
dfdcbc0a8d feat: upgrade frontend platform to version 3 2022-11-07 12:45:43 +00:00
renovate[bot]
c3b02a2946 chore(deps): update dependency enzyme-adapter-react-16 to v1.15.7 2022-11-07 09:36:45 +00:00
renovate[bot]
f6c1a8bcc1 chore(deps): update dependency redux-saga to v1.2.1 2022-10-31 07:42:11 +00:00
Bilal Qamar
6c02962e0d refactor: updated frontend-build & resolved eslint issues 2022-10-26 10:37:57 -03:00
renovate[bot]
acaf98f0b1 chore(deps): update dependency @testing-library/dom to v8.19.0 2022-10-24 08:15:34 +00:00
Adolfo R. Brandes
90351083aa Merge pull request #256 from openedx/abdullahwaheed/transifex-languages-list-update
Supported Transifex languages in Makefile
2022-10-20 16:15:57 -03:00
Adolfo R. Brandes
6f75684ad9 Merge pull request #271 from brian-smith-tcril/studio-header-component
refactor: make studio header more flexible
2022-10-20 14:24:44 -03:00
Brian Smith
a54f099d68 refactor: make studio header more flexible 2022-10-19 10:20:10 -04:00
Diana Huang
02d081dd26 Merge pull request #270 from openedx/diana/transifex-call
fix: Add transifex flag.
2022-10-17 16:28:12 -04:00
Diana Huang
468acc80f0 fix: Add transifex flag.
The Transifex cli started requiring the -t or --translations flag in the pull command in order to fetch translations.

https://github.com/edx/edx-arch-experiments/issues/77
2022-10-17 16:05:53 -04:00
renovate[bot]
90fdd13fbc chore(deps): update dependency react-router-dom to v5.3.4 2022-10-17 08:57:14 +00:00
renovate[bot]
faf1b8522a chore(deps): update dependency react-redux to v7.2.9 2022-10-10 09:15:51 +00:00
Jenkins
e8a28b09bc chore(i18n): update translations 2022-10-09 16:30:19 -04:00
renovate[bot]
c611df3f69 chore(deps): update dependency jest-chain to v1.1.6 2022-10-03 08:05:06 +00:00
Jenkins
ab371f1c3a chore(i18n): update translations 2022-10-02 16:30:36 -04:00
renovate[bot]
fb2002a004 chore(deps): update dependency @testing-library/dom to v8.18.1 2022-09-26 10:35:10 +00:00
renovate[bot]
f955ec4434 chore(deps): update dependency @testing-library/dom to v8.18.0 2022-09-19 09:59:12 +00:00
Sarina Canelake
d529e00d7b Merge pull request #257 from openedx/tcril/fix-gh-org-url
Fix github url strings (org edx -> openedx)
2022-09-13 22:21:04 -04:00
renovate[bot]
e0cbbf7da1 chore(deps): update dependency @testing-library/dom to v8.17.1 2022-09-12 11:50:01 +00:00
Sarina Canelake
ba209fd050 fix: update path to .github workflows to read from openedx org 2022-09-10 18:07:21 -04:00
Sarina Canelake
ae7004e95d fix: fix github url strings (org edx -> openedx) 2022-09-07 09:02:25 -04:00
Abdullah Waheed
de9eb63b07 feat: added new translations in Makefile and updated all the translations 2022-09-06 20:05:09 +05:00
renovate[bot]
bf64a829cc chore(deps): update dependency @edx/frontend-platform to v2.6.2 2022-09-05 13:45:11 +00:00
renovate[bot]
1e1b06dfa5 chore(deps): update dependency @edx/frontend-platform to v2.6.1 2022-08-29 12:34:32 +00:00
renovate[bot]
469a93bd9c chore(deps): update dependency @testing-library/jest-dom to v5.16.5 2022-08-22 11:06:32 +00:00
Muhammad Abdullah Waheed
0645761f05 Merge pull request #250 from openedx/abdullahwaheed/run-build-in-ci
`npm run build` script in Github CI
2022-08-16 11:20:25 +05:00
renovate[bot]
20e9881546 chore(deps): update dependency @edx/frontend-platform to v2.5.2 2022-08-15 10:43:52 +00:00
Abdullah Waheed
42b347058f feat: added build script in github CI 2022-08-15 10:30:48 +05:00
renovate[bot]
b097535580 chore(deps): update dependency @edx/frontend-platform to v2.5.1 2022-08-08 12:39:35 +00:00
edx-semantic-release
d06929ca2b chore(i18n): update translations 2022-08-07 16:29:27 -04:00
renovate[bot]
0a9316c407 fix(deps): update dependency react-transition-group to v4.4.5 2022-08-02 00:28:16 +00:00
Kira Miller
d7704799fe Merge pull request #246 from openedx/kiram15/ENT-6064
chore: updating paragon dependecy
2022-08-01 15:18:47 -04:00
Kira
cc26f750e8 chore: updating paragon dependecy 2022-08-01 13:32:03 -04:00
renovate[bot]
8ac22515a6 chore(deps): update dependency @edx/frontend-platform to v2.5.0 2022-07-25 10:44:21 +00:00
renovate[bot]
b4a8d0afdc chore(deps): update dependency jest to v28.1.3 2022-07-18 12:07:45 +00:00
renovate[bot]
c131842139 chore(deps): update dependency @testing-library/dom to v8.16.0 2022-07-11 13:44:17 +00:00
renovate[bot]
acf5398b31 chore(deps): update dependency jest to v28 2022-07-04 12:55:37 +00:00
renovate[bot]
871e35d53a fix(deps): update dependency @fortawesome/react-fontawesome to ^0.2.0 2022-07-04 06:26:38 +00:00
connorhaugh
c8fb2ded56 Merge pull request #233 from openedx/feat--upgrade-frontend-build-
feat: upgrade frontend-build
2022-06-30 10:28:14 -04:00
connorhaugh
01ffe0b945 feat: upgrade frontend-build 2022-06-30 10:23:35 -04:00
connorhaugh
092c61dada Merge pull request #225 from openedx/renovate/edx-frontend-platform-2.x
chore(deps): update dependency @edx/frontend-platform to v2
2022-06-30 10:11:50 -04:00
renovate[bot]
cc43299932 chore(deps): update dependency @edx/frontend-platform to v2 2022-06-28 18:08:29 +00:00
renovate[bot]
9086b96313 chore(deps): update dependency @testing-library/dom to v8.14.0 2022-06-27 12:17:35 +00:00
renovate[bot]
86b9d4a061 chore(deps): update dependency @edx/paragon to v19.25.3 2022-06-20 12:10:31 +00:00
Renovate Bot
36fd9cb89a chore(deps): update dependency @testing-library/dom to v8 2022-06-13 14:57:57 +00:00
Renovate Bot
5aec913cd6 chore(deps): update dependency @edx/paragon to v19.25.1 2022-06-06 06:59:04 +00:00
Renovate Bot
5da9cc3ce4 chore(deps): update dependency @edx/paragon to v19.25.0 2022-05-30 10:10:04 +00:00
Renovate Bot
97e8c69345 chore(deps): update dependency @edx/paragon to v19.23.1 2022-05-23 13:08:03 +00:00
Renovate Bot
a410ec8ed6 chore(deps): update dependency react-router-dom to v5.3.3 2022-05-23 10:58:27 +00:00
Renovate Bot
7e197e2315 chore(deps): update dependency @edx/frontend-platform to v1.15.6 2022-05-16 19:03:38 +00:00
edX requirements bot
6373bc726f chore!: Dropped support for Node 12 (#216)
BREAKING CHANGE: Dropped support for Node 12
2022-05-16 20:57:50 +05:00
Renovate Bot
1ccf3214f0 chore(deps): update dependency @edx/paragon to v19.22.1 2022-05-16 10:16:14 +00:00
edx-semantic-release
137701d10c chore(i18n): update translations 2022-05-12 16:05:09 -04:00
Tim McCormack
37d39dba95 fix: Fix pull_translations by using correct CLI flag for language filter (#214)
Docs: https://developers.transifex.com/docs/using-the-client
2022-05-12 19:57:30 +00:00
Jawayria
5d297a0d00 feat: Added package-lock file version check (#213) 2022-05-12 12:23:43 +05:00
Renovate Bot
1aa6e80274 chore(deps): update dependency @edx/paragon to v19.20.0 2022-05-02 13:43:13 +00:00
Renovate Bot
15778a5e4a chore(deps): update dependency @edx/frontend-build to v9.2.2 2022-05-02 10:38:49 +00:00
Renovate Bot
48c41a7757 chore(deps): update dependency redux to v4.2.0 2022-04-25 14:19:34 +00:00
Renovate Bot
28ee7518f4 chore(deps): update dependency @edx/paragon to v19.19.0 2022-04-25 11:43:12 +00:00
Carlos Muniz
c35df7ef60 feat: Add StudioHeader with optional AppMenu (#199)
* feat: Add StudioHeader with optional AppMenu

StudioHeader is a graft of Header with an additional optional AppMenu.
Some Frontend Apps use Menus in their custom headers to provide more
functionality in their apps. By adding this functionality in
StudioHeader, it will be easier for frontend apps in Studio to adopt
this component without it affecting the main Header component.

* test: Add tests for StudioHeader and AppMenu

* fix: Remove orderHistory

* fix: Remove Responsive components

* fix: Redefine User Menu for Studio

The userMenu in StudioHeader will be used more for Studio related items
such as Studio Home and Studio Maintenance. This requires new messages
and reestablishing the url destinations of these menu items.

* fix: Remove loggedOutItems

* fix: Remove AUTHN_MINIMAL_HEADER items

* fix: Remove unnecessary tests

Anonymous sessions do not exist in the Studio. And Studio is not Mobile
Ready. Tests of these kind are superfluous and have been removed.

* feat: Turn mainMenu into an optional prop

* test: Add test for optional mainMenu prop

* feat: Update snapshots

* fix: Remove ResponsiveContext

* fix: Remove href and update appMenu prop

Dropping the href because having a link that also works as a dropdown
can be mildly confusing. Changing menu (type, href, content ) triplet
to stick to the pattern; so we removed "menu". Also adding brackets around
the triplet. Lastly, updating test and snapshot.

Co-authored-by: Carlos Muniz <cmuniz@trcil.org>
2022-04-21 13:43:37 -04:00
Renovate Bot
a8d5a0b62a chore(deps): update dependency react-router-dom to v5.3.1 2022-04-18 15:48:35 +00:00
dependabot[bot]
64f55150b6 build(deps): bump async from 2.6.3 to 2.6.4
Bumps [async](https://github.com/caolan/async) from 2.6.3 to 2.6.4.
- [Release notes](https://github.com/caolan/async/releases)
- [Changelog](https://github.com/caolan/async/blob/v2.6.4/CHANGELOG.md)
- [Commits](https://github.com/caolan/async/compare/v2.6.3...v2.6.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-18 11:56:29 +00:00
Renovate Bot
9ac38762eb chore(deps): update dependency react-redux to v7.2.8 2022-04-18 11:55:33 +00:00
Renovate Bot
72077302c0 chore(deps): update dependency jest to v27.5.1 2022-04-11 15:26:22 +00:00
Renovate Bot
f1a54ededf chore(deps): update dependency @testing-library/jest-dom to v5.16.4 2022-04-11 12:14:34 +00:00
Renovate Bot
709844a459 chore(deps): update dependency @edx/frontend-build to v9.1.4 2022-04-04 13:35:10 +00:00
Usama Sadiq
032c68edf2 Merge pull request #195 from openedx/jenkins/transifex-client-migration-541fe55
fix: transifex migration to new client
2022-04-04 14:10:03 +05:00
edX requirements bot
be8b6fba01 fix: transifex migration to new client 2022-03-17 08:56:54 -04:00
David Joy
541fe55d6e build: delete CODEOWNERS file
We no longer have a community-engineering team.
2022-03-04 10:42:58 -05:00
Jawayria
28a62cf263 Merge pull request #194 from openedx/jawayria/test-peer-dep
fix: update peer dep
2022-03-04 20:33:55 +05:00
Jawayria
6e7584c89c fix: update peer dep 2022-03-04 20:29:16 +05:00
Jawayria
49b03689a0 Merge pull request #193 from openedx/jawayria/update-deps
fix: Upgraded dependencies for Node 16
2022-03-04 12:50:50 +05:00
Jawayria
fdb00ce1f5 fix: Upgraded dependencies for Node 16 2022-03-03 19:55:31 +05:00
Renovate Bot
5d1d73a6b6 chore(deps): update dependency @edx/frontend-build to v9.1.1 2022-02-25 13:21:05 +00:00
Jawayria
b292ee39ad Merge pull request #181 from openedx/jawayria/node-16
feat: Added support for node v16
2022-02-25 17:09:30 +05:00
Sarina Canelake
c5f13022de Merge DEPR automation workflow
Add DEPR workflow automation
2022-02-24 15:22:08 -05:00
Jawayria
47a3962118 build: Added support for node v16 2022-02-24 21:29:25 +05:00
Sarina Canelake
b55d0d6bea build: add DEPR workflow automation 2022-02-23 14:38:13 -05:00
Renovate Bot
8cb19859aa chore(deps): update dependency jest to v27.5.0 2022-02-07 09:54:56 +00:00
Renovate Bot
bafa6452ca chore(deps): update dependency @testing-library/jest-dom to v5.16.2 2022-02-07 09:20:49 +00:00
edX Transifex Bot
3ab658ffa0 chore(i18n): update translations 2022-01-23 15:32:36 -05:00
Renovate Bot
72b6a2c2a0 chore(deps): update dependency prop-types to v15.8.1 2022-01-10 09:13:51 +00:00
Renovate Bot
a479556795 chore(deps): update dependency jest to v27.4.7 2022-01-10 08:55:54 +00:00
Renovate Bot
8f03fa84ac chore(deps): update dependency prop-types to v15.8.0 2022-01-03 10:24:10 +00:00
Renovate Bot
990d82be6a chore(deps): update dependency jest to v27.4.5 2022-01-03 10:05:52 +00:00
edX Transifex Bot
b90db977a0 chore(i18n): update translations 2022-01-02 15:31:58 -05:00
Renovate Bot
093452860e chore(deps): update dependency @testing-library/react to v10.4.9 2021-12-27 08:39:46 +00:00
Renovate Bot
8ac9a2dc3c chore(deps): update dependency @edx/frontend-platform to v1.14.9 2021-12-27 08:22:33 +00:00
David Joy
440eacbce4 fix: Causing a patch version bump so we can release the transifex fixes (#165) 2021-12-20 11:16:11 -05:00
Renovate Bot
725fa982c7 chore(deps): update dependency @testing-library/jest-dom to v5.16.1 2021-12-20 10:22:13 +00:00
Renovate Bot
fe41fbc0b1 chore(deps): update dependency @edx/frontend-platform to v1.14.4 2021-12-20 10:03:50 +00:00
edX Transifex Bot
0de0a3f07f chore(i18n): update translations 2021-12-19 15:31:34 -05:00
Diana Huang
b0009fef42 Merge pull request #159 from edx/diana/update-transifex-langs
chore: Update transifex languages.
2021-12-10 09:22:31 -05:00
Diana Huang
8bfae8bf2f chore: Update transifex languages.
Adding fr_CA to our transifex languages to
pull down the language files into the repo.

https://github.com/openedx/build-test-release-wg/issues/116
2021-12-10 09:14:54 -05:00
Bianca Severino
abe0bbbf51 Merge pull request #158 from edx/bseverino/paragon-dependency
fix: update peer dependency of paragon
2021-12-09 11:55:20 -05:00
Bianca Severino
c6e971ef1d fix: update peer dependency of paragon 2021-12-09 11:48:34 -05:00
edX Transifex Bot
2f0a07b7eb chore(i18n): update translations 2021-12-08 22:22:31 +05:00
renovate[bot]
56593df3bd chore(deps): update dependency @testing-library/dom to v7.31.2 (#155)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-12-06 10:45:58 +00:00
renovate[bot]
45735231aa fix(deps): update font awesome (#154)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-12-06 08:54:16 +00:00
David Joy
eb1b914e84 build: pin semantic-release to v16 (#153)
Newer versions don't support node 12.
2021-12-03 11:58:54 -05:00
Asad Iqbal
2db0fd5c33 feat: Introduced Learning Course Header in Header MFE. (#133)
Co-authored-by: asadiqbal08 <asad.iqbal@arbisoft.com>
2021-12-02 14:17:17 -05:00
renovate[bot]
7e33da45c2 chore(deps): update dependency @edx/frontend-platform to v1.14.1 (#151)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-29 08:45:29 +00:00
renovate[bot]
632938f354 chore(deps): update dependency tmpl to 1.0.5 [security] (#149)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-08 11:07:22 -05:00
renovate[bot]
540fe28dc0 Merge pull request #136 from edx/renovate/edx-frontend-platform-1.x
chore(deps): update dependency @edx/frontend-platform to v1.14.0
2021-11-08 10:12:13 +00:00
Renovate Bot
b5671cc84e chore(deps): update dependency @edx/frontend-platform to v1.14.0 2021-11-08 05:31:59 +00:00
renovate[bot]
56963c2eb1 Merge pull request #135 from edx/renovate/redux-4.x
chore(deps): update dependency redux to v4.1.2
2021-11-01 09:15:30 +00:00
Renovate Bot
1099fcaa74 chore(deps): update dependency redux to v4.1.2 2021-11-01 09:08:21 +00:00
renovate[bot]
cea6e4814e Merge pull request #134 from edx/renovate/react-redux-7.x
chore(deps): update dependency react-redux to v7.2.6
2021-11-01 09:04:00 +00:00
Renovate Bot
4a6da6bde0 chore(deps): update dependency react-redux to v7.2.6 2021-11-01 04:29:04 +00:00
renovate[bot]
3da27f829c Merge pull request #132 from edx/renovate/husky-7.x
chore(deps): update dependency husky to v7.0.4
2021-10-25 09:07:09 +00:00
Renovate Bot
d45e26d70a chore(deps): update dependency husky to v7.0.4 2021-10-25 05:47:41 +00:00
Ned Batchelder
35731f6cb9 Merge pull request #131 from edx/nedbat/remove-commitlint-config
build: no need for local commitlint
2021-10-22 14:24:16 -04:00
Ned Batchelder
4aa29ffafe build: no need for local commitlint
We now have a commitlint GitHub Action that uses a central configuration file to ensure uniformity across repos.
2021-10-22 12:05:08 -04:00
Ned Batchelder
f73df420c8 build: use the organization commitlint check 2021-10-07 13:56:38 -04:00
renovate[bot]
fd13828590 Merge pull request #129 from edx/renovate/commitlint-monorepo
chore(deps): update commitlint monorepo to v13.2.0
2021-10-04 08:30:45 +00:00
Renovate Bot
0160cbb7e3 chore(deps): update commitlint monorepo to v13.2.0 2021-10-04 05:35:41 +00:00
renovate[bot]
e82bf4de14 Merge pull request #128 from edx/renovate/edx-frontend-platform-1.x
chore(deps): update dependency @edx/frontend-platform to v1.12.7
2021-09-20 08:29:11 +00:00
Renovate Bot
4218ea0126 chore(deps): update dependency @edx/frontend-platform to v1.12.7 2021-09-20 05:06:50 +00:00
renovate[bot]
72ef660feb Merge pull request #127 from edx/renovate/edx-frontend-platform-1.x
chore(deps): update dependency @edx/frontend-platform to v1.12.5
2021-09-13 08:45:16 +00:00
Renovate Bot
5a262f5d2f chore(deps): update dependency @edx/frontend-platform to v1.12.5 2021-09-13 06:45:22 +00:00
renovate[bot]
ca5a094332 Merge pull request #126 from edx/renovate/react-router-monorepo
chore(deps): update dependency react-router-dom to v5.3.0
2021-09-06 08:32:24 +00:00
Renovate Bot
8ca2a43835 chore(deps): update dependency react-router-dom to v5.3.0 2021-09-06 08:25:47 +00:00
renovate[bot]
03ce864782 Merge pull request #125 from edx/renovate/react-redux-7.x
chore(deps): update dependency react-redux to v7.2.5
2021-09-06 08:21:19 +00:00
Renovate Bot
25558d995d chore(deps): update dependency react-redux to v7.2.5 2021-09-06 05:02:37 +00:00
renovate[bot]
ccdcea7c20 Merge pull request #122 from edx/renovate/husky-7.x
chore(deps): update dependency husky to v7.0.2
2021-08-30 09:37:00 +00:00
Renovate Bot
f45c238ce2 chore(deps): update dependency husky to v7.0.2 2021-08-30 05:20:59 +00:00
renovate[bot]
8bec10bbeb Merge pull request #121 from edx/renovate/edx-frontend-platform-1.x
chore(deps): update dependency @edx/frontend-platform to v1.12.4
2021-08-23 08:44:42 +00:00
Renovate Bot
7cd2e11395 chore(deps): update dependency @edx/frontend-platform to v1.12.4 2021-08-23 05:27:00 +00:00
renovate[bot]
bcc0e9afb4 Merge pull request #119 from edx/renovate/edx-frontend-platform-1.x
chore(deps): update dependency @edx/frontend-platform to v1.12.3
2021-08-16 06:57:45 +00:00
Renovate Bot
5a5ae1eb31 chore(deps): update dependency @edx/frontend-platform to v1.12.3 2021-08-16 04:39:43 +00:00
stvn
334793cb81 merge(#111): renovate/husky-7.x
commits
=======
- chore(deps): update dependency husky to v7
2021-08-09 17:20:09 -07:00
Renovate Bot
171d820d60 chore(deps): update dependency husky to v7 2021-08-10 00:04:43 +00:00
stvn
7399c9b6cb merge(#115): renovate/codecov-codecov-action-2.x
commits
=======
- chore(deps): update codecov/codecov-action action to v2
2021-08-09 16:59:18 -07:00
Renovate Bot
ff4aaf163b chore(deps): update codecov/codecov-action action to v2 2021-08-09 23:39:46 +00:00
stvn
72195a63f2 merge(#118): renovate/major-commitlint-monorepo
commits
=======
- chore(deps): update commitlint monorepo to v13
2021-08-09 16:38:59 -07:00
Renovate Bot
7654ad5192 chore(deps): update commitlint monorepo to v13 2021-08-09 08:38:25 +00:00
renovate[bot]
c6443d414b Merge pull request #117 from edx/renovate/redux-4.x
chore(deps): update dependency redux to v4.1.1
2021-08-09 08:34:32 +00:00
Renovate Bot
0057de55bd chore(deps): update dependency redux to v4.1.1 2021-08-09 04:55:24 +00:00
renovate[bot]
af563a1794 Merge pull request #116 from edx/renovate/edx-frontend-platform-1.x
chore(deps): update dependency @edx/frontend-platform to v1.11.3
2021-08-02 07:24:16 +00:00
Renovate Bot
a5a66c1f5c chore(deps): update dependency @edx/frontend-platform to v1.11.3 2021-08-02 04:50:25 +00:00
renovate[bot]
61ce560c80 Merge pull request #114 from edx/renovate/codecov-3.x
chore(deps): update dependency codecov to v3.8.3
2021-07-26 07:46:15 +00:00
Renovate Bot
a9605b576b chore(deps): update dependency codecov to v3.8.3 2021-07-26 04:59:45 +00:00
renovate[bot]
bde691da06 Merge pull request #113 from edx/renovate/edx-frontend-platform-1.x
chore(deps): update dependency @edx/frontend-platform to v1.11.1
2021-07-19 07:07:47 +00:00
Renovate Bot
c6eb62557e chore(deps): update dependency @edx/frontend-platform to v1.11.1 2021-07-19 05:01:38 +00:00
David Joy
20649f06d9 Merge pull request #107 from bitmakerla/chris/order_history_in_user_menu
feat: Order history item added in user menu
2021-06-11 17:27:40 -04:00
ChrisChV
f2632fb449 feat: Order history item added in user menu
In Header.jsx, the Order history item was added in the user menu using the ORDER_HISTORY_URL environment variable as href.
Environment Variables section in README.rst updated with the description of ORDER_HISTORY_URL.
2021-06-05 10:43:06 -05:00
stvn
d4bf756993 merge(#86): renovate/actions-setup-node-2.x
commits
=======
- chore(deps): update actions/setup-node action to v2
2021-06-04 22:04:32 -07:00
Renovate Bot
076a7c8832 chore(deps): update actions/setup-node action to v2 2021-06-05 05:01:03 +00:00
stvn
ee53d4dfb8 merge(#106): build/renovate
commits
=======
- build(renovate): fix json syntax
2021-06-04 22:00:32 -07:00
stvn
42668c6d5a build(renovate): fix json syntax 2021-06-04 21:53:57 -07:00
stvn
5645dd4491 merge(#104): build/renovate-stricter
commits
=======
- build(renovate): be more selective about automerging devDependencies
2021-06-04 13:42:15 -07:00
stvn
2bb3febc39 build(renovate): be more selective about automerging devDependencies 2021-06-04 13:21:37 -07:00
Renovate Bot
2fb9804b91 chore(deps): update dependency husky to v6 2021-06-04 00:50:59 +00:00
Renovate Bot
a09317aaae chore(deps): update commitlint monorepo to v12 2021-06-04 00:43:30 +00:00
Renovate Bot
080621bcea fix(deps): update dependency react-transition-group to v4.4.2 2021-06-04 00:34:45 +00:00
Renovate Bot
37e9ef0434 fix(deps): update dependency react-responsive to v8.2.0 2021-06-04 00:24:39 +00:00
Renovate Bot
df1b6ff941 chore(deps): update react monorepo to v16.14.0 2021-06-04 00:13:10 +00:00
Renovate Bot
52b8b7ca11 chore(deps): update dependency redux to v4.1.0 2021-06-04 00:00:12 +00:00
Renovate Bot
9ed0af96ca chore(deps): update dependency react-router-dom to v5.2.0 2021-06-03 23:46:01 +00:00
Renovate Bot
8d0bd0ca05 chore(deps): update dependency react-redux to v7.2.4 2021-06-03 23:30:28 +00:00
Renovate Bot
de83d5f20f chore(deps): update dependency husky to v3.1.0 2021-06-03 23:12:56 +00:00
Renovate Bot
54ff71b0ec chore(deps): update dependency enzyme-adapter-react-16 to v1.15.6 2021-06-03 22:54:19 +00:00
Renovate Bot
a8347625ae chore(deps): update dependency enzyme to v3.11.0 2021-06-03 22:34:36 +00:00
Renovate Bot
305ae120c6 chore(deps): update dependency codecov to v3.8.2 2021-06-03 22:14:04 +00:00
Renovate Bot
e1468b5396 chore(deps): update dependency @edx/paragon to v12.8.0 2021-06-03 21:52:33 +00:00
Renovate Bot
10767bdaac chore(deps): update dependency @edx/frontend-platform to v1.11.0 2021-06-03 21:27:30 +00:00
Renovate Bot
484d6af4d9 chore(deps): update dependency @edx/frontend-build to v5.6.14 2021-06-03 21:01:01 +00:00
Renovate Bot
8ba7153806 chore(deps): update commitlint monorepo 2021-06-03 20:31:27 +00:00
stvn
334bdb34f3 merge(#103): build/renovate
commits
=======
- build(renovate): be more liberal about what automerges
2021-06-03 11:18:13 -07:00
stvn
12b63c5583 build(renovate): be more liberal about what automerges
based on https://github.com/edx/frontend-app-account .
2021-06-03 11:00:58 -07:00
stvn
3726792df2 merge: stvn/own/code 2021-05-26 13:42:44 -07:00
stvn
cc252e32e6 build: add CODEOWNERS; edx/community-engineering
Background
==========
As part of our Squad-based ownership, we should stay on top of what
happens in these repositories. However, due to the number of
repositories (and subsequently pull requests) across the edX ecosystem,
it is challenging to stay on top of notifications, separating the
'signal' from the 'noise'. Email filters can go a long way to taming
Inbox notifications, but this is manual and requires maintenance as
Squad ownership changes. It also fails to account for Github-specific behavior.

Proposal
========
By leveraging Github support for `CODEOWNERS` files [1],
we can ensure that our team is at least CCed explicitly, here,
in the form a requested review. This request is just that, a request,
not a requirement; we are not enacting any new merge requirements
at this time.

- [1] https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners
2021-05-26 12:16:05 -07:00
David Joy
0c58362b49 docs: Updating README with repository details. (#101)
* docs: fleshing out the readme

Making it consistent with frontend-component-footer and much more detailed.

* fix: Removing unused variable, siteName

Also fixing logout URL in development.  (We fixed this in a number of other repositories already, but somehow this one snuck through)
2021-05-13 14:57:25 -04:00
Uzair Rasheed
b00e018105 Merge pull request #97 from edx/van-379/add-skipnav-link
fix: add skipnav link in header
2021-03-19 21:12:14 +05:00
uzairr
02464f9c09 fix: add skipnav link in header 2021-03-19 21:05:18 +05:00
Uzair Rasheed
67c1fb2cda Merge pull request #95 from edx/van-379/add-skipnav-link
add skipnav link in header
2021-03-19 18:23:28 +05:00
uzairr
e84843d83a add skipnav link in header 2021-03-19 14:57:42 +05:00
Renovate Bot
20606c2880 chore(deps): update dependency redux-saga to v1.1.3 2021-02-11 15:34:50 +00:00
Renovate Bot
3ba52a586e chore(deps): update dependency redux to v4.0.5 2021-02-11 10:36:34 +00:00
Renovate Bot
9eafcc9ca0 chore(deps): update dependency @edx/frontend-platform to v1.8.4 2021-02-11 01:55:14 +00:00
Uzair Rasheed
61ecf93785 Merge pull request #92 from edx/align-logo-to-left
fix: align logo to left
2021-02-08 14:57:53 +05:00
uzairr
917e748fc5 fix: align logo to left on authn mfe 2021-02-08 13:18:42 +05:00
Adeel Ehsan
0d64b19ac4 Merge pull request #89 from edx/aehsan/logistration-renmaed-to-authn
fix: logistration renamed to authn
2021-01-28 14:06:36 +05:00
adeelehsan
9415709b81 fix: logistration renamed to authn 2021-01-28 13:08:25 +05:00
Adeel Ehsan
003d8ee1a7 Merge pull request #88 from edx/revert-87-aehsan/logistration_renmaed_to_authn
Revert "logistration renamed to auth n"
2021-01-28 12:58:14 +05:00
Adeel Ehsan
18dd01d3d2 Revert "logistration renamed to auth n" 2021-01-28 12:52:31 +05:00
Adeel Ehsan
bdb1e03e4e Merge pull request #87 from edx/aehsan/logistration_renmaed_to_authn
logistration renamed to auth n
2021-01-28 12:34:54 +05:00
adeelehsan
5662e5daa3 logistration renamed to auth n 2021-01-28 12:27:17 +05:00
Renovate Bot
9306ce0783 chore(deps): update dependency react-redux to v7.1.3 2021-01-24 19:58:38 +00:00
Renovate Bot
f58ef0ace6 chore(deps): update dependency @edx/frontend-platform to v1.8.1 2021-01-10 13:50:47 +00:00
Waheed Ahmed
7de6ba4381 fix: logistration hide user menu icon on mobile screens (#84)
VAN-227
2020-12-22 13:02:55 +05:00
Adam Stankiewicz
14fe2d9bc6 fix: use LOGO_URL and move to GH actions (#81)
* fix: use LOGO_URL and move to GH actions

* fix: duplicate i18n message id
2020-12-01 09:48:38 -05:00
Waheed Ahmed
78573e30f1 feat: add LOGISTRATION_MINIMAL_HEADER env variable (#75)
The LOGISTRATION_MINIMAL_HEADER environment variable removes the main menu links
from the header. It also removes login/register links from the menu if user
is unauthenticated.
2020-11-23 15:04:20 +05:00
Adam Stankiewicz
ae014d2f24 feat: use logo from config settings, retheme with @edx/brand 2020-11-20 11:20:50 -05:00
Waheed Ahmed
726aff9f8d Revert "modify header for logistration mfe (#73)" (#74)
This reverts commit bb7c5cb39f.
2020-11-18 11:24:13 +05:00
Uzair Rasheed
bb7c5cb39f modify header for logistration mfe (#73)
* modify header for logistration mfe

* Update .gitignore

Co-authored-by: Waheed Ahmed <waheed.ahmed@arbisoft.com>
2020-11-17 20:04:07 +05:00
Renovate Bot
90df9feb87 chore(deps): update dependency codecov to v3.7.2 2020-07-22 03:43:44 +00:00
dependabot[bot]
a86cde04bd chore(deps-dev): bump codecov from 3.6.5 to 3.7.1 (#65)
Bumps [codecov](https://github.com/codecov/codecov-node) from 3.6.5 to 3.7.1.
- [Release notes](https://github.com/codecov/codecov-node/releases)
- [Commits](https://github.com/codecov/codecov-node/compare/v3.6.5...v3.7.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-07-21 09:25:53 -04:00
Renovate Bot
afc03b2253 chore(deps): update dependency codecov to v3.6.5 [security] 2020-02-19 20:32:04 +00:00
Renovate Bot
01cee41441 chore(deps): update dependency codecov to v3.6.4 2020-02-01 03:13:14 +00:00
Renovate Bot
307669cf9f chore(deps): update dependency codecov to v3.6.3 2020-01-31 14:11:18 +00:00
Renovate Bot
265f3be635 chore(deps): update dependency @edx/frontend-platform to v1.1.14 2020-01-28 13:13:00 +00:00
Renovate Bot
7742de1609 chore(deps): update dependency codecov to v3.6.2 2020-01-23 20:22:41 +00:00
Renovate Bot
1778b78f0a fix(deps): update dependency react-responsive to v8.0.3 2020-01-22 17:24:40 +00:00
57 changed files with 44006 additions and 12861 deletions

View File

@@ -1,15 +1,22 @@
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
ECOMMERCE_BASE_URL=http://localhost:18130
LANGUAGE_PREFERENCE_COOKIE_NAME=openedx-language-preference
LMS_BASE_URL=http://localhost:18000
STUDIO_BASE_URL=http://localhost:18010
LOGIN_URL=http://localhost:18000/login
LOGOUT_URL=http://localhost:18000/login
LOGOUT_URL=http://localhost:18000/logout
MARKETING_SITE_BASE_URL=http://localhost:18000
ORDER_HISTORY_URL=localhost:1996/orders
REFRESH_ACCESS_TOKEN_ENDPOINT=http://localhost:18000/login_refresh
SEGMENT_KEY=null
SITE_NAME=Open edX
USER_INFO_COOKIE_NAME=edx-user-info
LOGO_URL=https://edx-cdn.org/v3/default/logo.svg
LOGO_TRADEMARK_URL=https://edx-cdn.org/v3/default/logo-trademark.svg
LOGO_WHITE_URL=https://edx-cdn.org/v3/default/logo-white.svg
FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico

View File

@@ -1,3 +1,4 @@
// eslint-disable-next-line import/no-extraneous-dependencies
const { createConfig } = require('@edx/frontend-build');
module.exports = createConfig('eslint');
module.exports = createConfig('eslint');

View File

@@ -0,0 +1,19 @@
# Run the workflow that adds new tickets that are either:
# - labelled "DEPR"
# - title starts with "[DEPR]"
# - body starts with "Proposal Date" (this is the first template field)
# to the org-wide DEPR project board
name: Add newly created DEPR issues to the DEPR project board
on:
issues:
types: [opened]
jobs:
routeissue:
uses: openedx/.github/.github/workflows/add-depr-ticket-to-depr-board.yml@master
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 }}

37
.github/workflows/ci.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: Default CI
on:
push:
branches:
- master
pull_request:
branches:
- '**'
jobs:
tests:
runs-on: ubuntu-latest
strategy:
matrix:
node: [16]
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Nodejs
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node }}
- name: Install dependencies
run: npm ci
- name: Validate package-lock.json changes
run: make validate-no-uncommitted-package-lock-changes
- name: Lint
run: npm run lint
- name: Test
run: npm run test
- name: Build
run: npm run build
- name: i18n_extract
run: npm run i18n_extract
- name: Coverage
uses: codecov/codecov-action@v2

10
.github/workflows/commitlint.yml vendored Normal file
View File

@@ -0,0 +1,10 @@
# Run commitlint on the commit messages in a pull request.
name: Lint Commit Messages
on:
- pull_request
jobs:
commitlint:
uses: openedx/.github/.github/workflows/commitlint.yml@master

View File

@@ -0,0 +1,14 @@
#check package-lock file version
name: lockfileVersion check
on:
push:
branches:
- master
pull_request:
jobs:
version-check:
uses: openedx/.github/.github/workflows/lockfileversion-check.yml@master

39
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,39 @@
name: Release CI
on:
push:
branches:
- master
jobs:
release:
name: Release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 16
- name: Install dependencies
run: npm ci
- name: Validate package-lock.json changes
run: make validate-no-uncommitted-package-lock-changes
- name: Lint
run: npm run lint
- name: Test
run: npm run test
- name: i18n_extract
run: npm run i18n_extract
- name: Coverage
uses: codecov/codecov-action@v2
- name: Build
run: npm run build
- name: Release
uses: cycjimmy/semantic-release-action@v2
with:
semantic_version: 16
env:
GITHUB_TOKEN: ${{ secrets.SEMANTIC_RELEASE_GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.SEMANTIC_RELEASE_NPM_TOKEN }}

4
.gitignore vendored
View File

@@ -5,3 +5,7 @@ dist
node_modules
temp
src/i18n/transifex_input.json
module.config.js
.idea/
.vscode

View File

@@ -1,17 +0,0 @@
language: node_js
node_js: 12
install:
- npm install
script:
- npm run lint
- npm run test
- npm run build
after_success:
- npx semantic-release
- codecov
env:
global:
# GH_TOKEN
- secure: CFN/uOByWC+7S+AAXECLQ0mNgoiyCDl1ZB5AT5+/qP+xEVs0ysFKDWrD9W8KeQqHqCMPUKNt23nrgxwvFCkSx+n1MAnxZmOdVJWbzGbUB9TsqrwVUALkqof1MrRB8UFVEzIRaO60iRN/L3zVXML+4GsycYX6rHyptbAypxplpljDyKrY8tc/mM7AGZ9eVFGSq+7CXXmdvkhP9kLkH1tIYvR7wjTKvZHbHf6YRjIVCiyzxM4S/E9l8JRnbERp02XosRD62PUJXXk6EJVn6Qoub6CaPnpew5crW0iRF1UJs54U29zWd/S+LuW66WkLfJu7rDq6AFJNMtMNusJxwVkOv4X+p0oJDWZEhojW+/Wm10UAu8/g5oAqeePZEGiSbT3Hp1VqOc4FY/kmOLiM+L6oq/AA2XX6iiE8lA64IH5R8ApQamF4GTUYTvKHeLPgXnGJH7A95Xy9/+jmX9I9wJREMrHrkyPjoX/NTRdG+RrebB1+An9Bt9vAbG862gbOZfgTcuWHDOlG5gcA964Fr7RqR5a62yr45Gw+Q0lTrLj5mGAjjSpMRIAQzi9e7oXmoMZnvenu/WJAe5M+u+/gv82HeXcMwLvNNGSvz+0i1xNUOoX1zHG046oGKiX0Zu+l0JfNwihJTO7vJlaITmjhfOyufwpk74xEyrhf8nLF9e5Frec=
# NPM_TOKEN
- secure: fuV04Ctf0mgbw6nTJhsTzGZ6dyafZtGVj30ZkvSWsB9hUU8KDtl7wWVW9EayCQsSyyFgPY9RVav5olgC/zljAjDGg0nfF7n8uKIABA0TXdP683WZd06bVOmDXfL26B3yM83aW2xgHZN6VCCvCE8bLP1V6eV8nsr38gDgxVbHa0YasDMmvtYrog+IjxwjcJx7fD0RbYyi7iJC++pdw9kcFqOad28Us7L/jCn+rC3CmUT4kOwPjP5g1v5sB2FA7ouN5s1hUUTKuttV32VJgRP7wbZzoHeHX5BRGSqijdXNSaK5UwzqRnM1sGZkuNDZhJbSB3q90SQrPRgV+fRizwN8zs8Htb+Kk8+wGY6zNhmi9C+lUIv7UpDYbstMWYIf39+P/24Oj+vJBjMY30M9NWB8gt1OQ0dJUoK53v1+BMVmDB0doL6I53xwzUjQetvqOF0Wm3E3OrqJP00OJdzIcAeh2DzcIRW1SrBhI4HAsl7QJZNpRw11QzJ3K2iQSiWNd8qIuX+XJjzQdn1v09gCstvbP33Vn8tP1x0XTSi8wIhTDqE0bII/Sc80Jh76nu0ItQ8pmX4lGsER4C0N3Dp7Zz51yW7E70AWWLsUrMNdF0oHoP437ZRGPhYs5OI6x5AM2jlU8fJ5aUroEsYFCwkH0OO37THohpAQpApe9zL10miTEbw=

View File

@@ -1,8 +1,9 @@
[main]
host = https://www.transifex.com
[edx-platform.frontend-component-header]
[o:open-edx:p:edx-platform:r:frontend-component-header]
file_filter = src/i18n/messages/<lang>.json
source_file = src/i18n/transifex_input.json
source_lang = en
type = KEYVALUEJSON
type = KEYVALUEJSON

View File

@@ -1,5 +1,5 @@
transifex_resource = frontend-component-header
transifex_langs = "ar,fr,es_419,zh_CN"
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
@@ -50,7 +50,7 @@ push_translations:
# Pulls translations from Transifex.
pull_translations:
tx pull -f --mode reviewed --language=$(transifex_langs)
tx pull -t -f --mode reviewed --languages=$(transifex_langs)
# This target is used by Travis.
validate-no-uncommitted-package-lock-changes:

View File

@@ -1,11 +1,79 @@
#########################
frontend-component-header
=========================
#########################
|Build Status| |Codecov| |npm_version| |npm_downloads| |license| |semantic-release|
This is the standard Open edX header for use in React applications. It has two exports:
- **default**: The Header Component
- **messages**: for i18n in the form of ``{ locale: { key: translatedString } }``
********
Overview
********
A generic header for Open edX micro-frontend applications.
************
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.
* ``LOGIN_URL`` - The URL of the login page where a user can sign into their account.
* ``SITE_NAME`` - The user-facing name of the site, used as `alt` text on the logo in the header.
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:
``npm i --save @edx/frontend-component-header``
This will make the component available to be imported into your application.
*****
Usage
*****
This library has the following exports:
* ``(default)``: The header as a React component.
* ``messages``: Internationalization messages suitable for use with `@edx/frontend-platform/i18n <https://edx.github.io/frontend-platform/module-Internationalization.html>`_
* ``dist/index.scss``: A SASS file which contains style information for the component. It should be imported into the micro-frontend's own SCSS file.
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
Start the development server::
npm start
Build a production distribution::
npm run build
.. |Build Status| image:: https://api.travis-ci.com/edx/frontend-component-header.svg?branch=master
:target: https://travis-ci.com/edx/frontend-component-header

View File

@@ -1,3 +0,0 @@
module.exports = {
extends: ['@commitlint/config-angular'],
};

View File

@@ -4,8 +4,9 @@ import React from 'react';
import ReactDOM from 'react-dom';
import { initialize, getConfig, subscribe, APP_READY } from '@edx/frontend-platform';
import { AppContext, AppProvider } from '@edx/frontend-platform/react';
import Header from '@edx/frontend-component-header';
import './index.scss';
import Header from '../src/';
subscribe(APP_READY, () => {
ReactDOM.render(

View File

@@ -1,2 +1,6 @@
@import "~@edx/paragon/scss/core/core.scss";
@import '../src/index.scss';
@import "@edx/brand/paragon/fonts";
@import "@edx/brand/paragon/variables";
@import "@edx/paragon/scss/core/core";
@import "@edx/brand/paragon/overrides";
@import "@edx/frontend-component-header/index";

View File

@@ -1,7 +1,7 @@
const { createConfig } = require('@edx/frontend-build');
module.exports = createConfig('jest', {
setupFiles: [
setupFilesAfterEnv: [
'<rootDir>/src/setupTest.js',
],
});

54709
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -19,50 +19,56 @@
],
"husky": {
"hooks": {
"pre-commit": "npm run lint",
"commit-msg": "commitlint -e $GIT_PARAMS"
"pre-commit": "npm run lint"
}
},
"repository": {
"type": "git",
"url": "git+https://github.com/edx/frontend-component-header.git"
"url": "git+https://github.com/openedx/frontend-component-header.git"
},
"author": "edX",
"license": "AGPL-3.0",
"bugs": {
"url": "https://github.com/edx/frontend-component-header/issues"
"url": "https://github.com/openedx/frontend-component-header/issues"
},
"homepage": "https://github.com/edx/frontend-component-header#readme",
"homepage": "https://github.com/openedx/frontend-component-header#readme",
"devDependencies": {
"@commitlint/cli": "8.2.0",
"@commitlint/config-angular": "8.2.0",
"@commitlint/prompt": "8.2.0",
"@commitlint/prompt-cli": "8.2.0",
"@edx/frontend-build": "^2.0.4",
"@edx/frontend-platform": "1.1.13",
"@edx/paragon": "7.1.5",
"codecov": "3.6.1",
"enzyme": "3.10.0",
"enzyme-adapter-react-16": "1.14.0",
"husky": "3.0.9",
"prop-types": "15.7.2",
"react": "16.9.0",
"react-dom": "16.9.0",
"react-redux": "^7.1.1",
"react-router-dom": "^5.1.2",
"react-test-renderer": "16.9.0",
"@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",
"@testing-library/react": "10.4.9",
"codecov": "3.8.3",
"enzyme": "3.11.0",
"enzyme-adapter-react-16": "1.15.7",
"husky": "7.0.4",
"jest": "29.3.1",
"jest-chain": "1.1.6",
"prop-types": "15.8.1",
"react": "16.14.0",
"react-dom": "16.14.0",
"react-redux": "7.2.9",
"react-router-dom": "5.3.4",
"react-test-renderer": "16.14.0",
"reactifex": "1.1.1",
"redux": "^4.0.4",
"redux-saga": "^1.1.1"
"redux": "4.2.0",
"redux-saga": "1.2.1"
},
"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/react-fontawesome": "^0.2.0",
"babel-polyfill": "6.26.0",
"react-responsive": "8.0.2",
"react-transition-group": "4.3.0"
"react-responsive": "8.2.0",
"react-transition-group": "4.4.5"
},
"peerDependencies": {
"@edx/frontend-platform": "^1.1.4",
"@edx/paragon": "^7.0.0",
"@edx/frontend-platform": "^2.0.0 || ^3.0.0",
"@edx/paragon": ">= 7.0.0 < 21.0.0",
"prop-types": "^15.5.10",
"react": "^16.9.0",
"react-dom": "^16.9.0"

View File

@@ -1,9 +1,33 @@
{
"extends": [
"config:base"
"config:base",
"schedule:weekly",
":automergeLinters",
":automergeMinor",
":automergeTesters",
":enableVulnerabilityAlerts",
":rebaseStalePrs",
":semanticCommits",
":updateNotScheduled"
],
"patch": {
"automerge": true
},
"rebaseStalePrs": true
"packageRules": [
{
"matchDepTypes": [
"devDependencies"
],
"matchUpdateTypes": [
"lockFileMaintenance",
"minor",
"patch",
"pin"
],
"automerge": true
},
{
"matchPackagePatterns": ["@edx"],
"matchUpdateTypes": ["minor", "patch"],
"automerge": true
}
],
"timezone": "America/New_York"
}

View File

@@ -3,16 +3,16 @@ import PropTypes from 'prop-types';
import { AvatarIcon } from './Icons';
function Avatar({
const Avatar = ({
size,
src,
alt,
className,
}) {
}) => {
const avatar = src ? (
<img className="d-block w-100 h-100" src={src} alt={alt} />
) : (
<AvatarIcon className="text-muted" style={{ width: size, height: size }} role="img" aria-hidden focusable="false" />
<AvatarIcon style={{ width: size, height: size }} role="img" aria-hidden focusable="false" />
);
return (
@@ -23,7 +23,7 @@ function Avatar({
{avatar}
</span>
);
}
};
Avatar.propTypes = {
src: PropTypes.string,

View File

@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { getConfig } from '@edx/frontend-platform';
// Local Components
import { Menu, MenuTrigger, MenuContent } from './Menu';
@@ -22,7 +23,9 @@ class DesktopHeader extends React.Component {
const { mainMenu } = this.props;
// Nodes are accepted as a prop
if (!Array.isArray(mainMenu)) return mainMenu;
if (!Array.isArray(mainMenu)) {
return mainMenu;
}
return mainMenu.map((menuItem) => {
const {
@@ -51,6 +54,24 @@ class DesktopHeader extends React.Component {
});
}
// Renders an optional App Menu for
renderAppMenu() {
const { appMenu } = this.props;
const { content: appMenuContent, menuItems } = appMenu;
return (
<Menu transitionClassName="menu-dropdown" transitionTimeout={250}>
<MenuTrigger tag="a" className="nav-link d-inline-flex align-items-center">
{appMenuContent} <CaretIcon role="img" aria-hidden focusable="false" />
</MenuTrigger>
<MenuContent className="mb-0 dropdown-menu show dropdown-menu-right pin-right shadow py-2">
{menuItems.map(({ type, href, content }) => (
<a className={`dropdown-${type}`} key={`${type}-${content}`} href={href}>{content}</a>
))}
</MenuContent>
</Menu>
);
}
renderUserMenu() {
const {
userMenu,
@@ -64,7 +85,7 @@ class DesktopHeader extends React.Component {
<MenuTrigger
tag="button"
aria-label={intl.formatMessage(messages['header.label.account.menu.for'], { username })}
className="btn btn-light d-inline-flex align-items-center pl-2 pr-3"
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" />
@@ -99,20 +120,31 @@ class DesktopHeader extends React.Component {
logoDestination,
loggedIn,
intl,
appMenu,
} = 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">
<div className="container-fluid">
<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} />}
{logoDestination === null ? <Logo className="logo" src={logo} alt={logoAltText} /> : <LinkedLogo className="logo" {...logoProps} />}
<nav
aria-label={intl.formatMessage(messages['header.label.main.nav'])}
className="nav main-nav"
>
{this.renderMainMenu()}
</nav>
{appMenu ? (
<nav
aria-label={intl.formatMessage(messages['header.label.app.nav'])}
className="nav app-nav"
>
{this.renderAppMenu()}
</nav>
) : null}
<nav
aria-label={intl.formatMessage(messages['header.label.secondary.nav'])}
className="nav secondary-menu-container align-items-center ml-auto"
@@ -150,6 +182,20 @@ DesktopHeader.propTypes = {
// i18n
intl: intlShape.isRequired,
// appMenu
appMenu: PropTypes.shape(
{
content: PropTypes.string,
menuItems: PropTypes.arrayOf(
PropTypes.shape({
type: PropTypes.string,
href: PropTypes.string,
content: PropTypes.string,
}),
),
},
),
};
DesktopHeader.defaultProps = {
@@ -162,6 +208,7 @@ DesktopHeader.defaultProps = {
avatar: null,
username: null,
loggedIn: false,
appMenu: null,
};
export default injectIntl(DesktopHeader);

View File

@@ -2,13 +2,17 @@ import React, { useContext } from 'react';
import Responsive from 'react-responsive';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { AppContext } from '@edx/frontend-platform/react';
import { ensureConfig } from '@edx/frontend-platform/config';
import {
APP_CONFIG_INITIALIZED,
ensureConfig,
mergeConfig,
getConfig,
subscribe,
} from '@edx/frontend-platform';
import DesktopHeader from './DesktopHeader';
import MobileHeader from './MobileHeader';
import LogoSVG from './logo.svg';
import messages from './Header.messages';
ensureConfig([
@@ -16,9 +20,17 @@ ensureConfig([
'LOGOUT_URL',
'LOGIN_URL',
'SITE_NAME',
'LOGO_URL',
'ORDER_HISTORY_URL',
], 'Header component');
function Header({ intl }) {
subscribe(APP_CONFIG_INITIALIZED, () => {
mergeConfig({
AUTHN_MINIMAL_HEADER: !!process.env.AUTHN_MINIMAL_HEADER,
}, 'Header additional config');
});
const Header = ({ intl }) => {
const { authenticatedUser, config } = useContext(AppContext);
const mainMenu = [
@@ -29,6 +41,12 @@ function Header({ intl }) {
},
];
const orderHistoryItem = {
type: 'item',
href: config.ORDER_HISTORY_URL,
content: intl.formatMessage(messages['header.user.menu.order.history']),
};
const userMenu = authenticatedUser === null ? [] : [
{
type: 'item',
@@ -37,12 +55,12 @@ function 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']),
},
{
@@ -52,6 +70,11 @@ function Header({ intl }) {
},
];
// Users should only see Order History if have a ORDER_HISTORY_URL define in the environment.
if (config.ORDER_HISTORY_URL) {
userMenu.splice(-1, 0, orderHistoryItem);
}
const loggedOutItems = [
{
type: 'item',
@@ -66,29 +89,28 @@ function Header({ intl }) {
];
const props = {
logo: LogoSVG,
logo: config.LOGO_URL,
logoAltText: config.SITE_NAME,
siteName: config.SITE_NAME,
logoDestination: `${config.LMS_BASE_URL}/dashboard`,
loggedIn: authenticatedUser !== null,
username: authenticatedUser !== null ? authenticatedUser.username : null,
avatar: authenticatedUser !== null ? authenticatedUser.avatar : null,
mainMenu,
userMenu,
loggedOutItems,
mainMenu: getConfig().AUTHN_MINIMAL_HEADER ? [] : mainMenu,
userMenu: getConfig().AUTHN_MINIMAL_HEADER ? [] : userMenu,
loggedOutItems: getConfig().AUTHN_MINIMAL_HEADER ? [] : loggedOutItems,
};
return (
<React.Fragment>
<>
<Responsive maxWidth={768}>
<MobileHeader {...props} />
</Responsive>
<Responsive minWidth={769}>
<DesktopHeader {...props} />
</Responsive>
</React.Fragment>
</>
);
}
};
Header.propTypes = {
intl: intlShape.isRequired,

View File

@@ -56,6 +56,16 @@ const messages = defineMessages({
defaultMessage: 'Sign Up',
description: 'Link to registration',
},
'header.user.menu.studio.home': {
id: 'header.user.menu.studio.home',
defaultMessage: 'Studio Home',
description: 'Link to the Studio Home',
},
'header.user.menu.studio.maintenance': {
id: 'header.user.menu.studio.maintenance',
defaultMessage: 'Maintenance',
description: 'Link to the Studio Maintenance',
},
'header.label.account.nav': {
id: 'header.label.account.nav',
defaultMessage: 'Account',
@@ -67,7 +77,7 @@ const messages = defineMessages({
description: 'The aria label for the account menu trigger',
},
'header.label.account.menu.for': {
id: 'header.label.account.menu',
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',
},
@@ -91,6 +101,16 @@ const messages = defineMessages({
defaultMessage: 'Secondary',
description: 'The aria label for the seconary nav',
},
'header.label.skip.nav': {
id: 'header.label.skip.nav',
defaultMessage: 'Skip to main content',
description: 'A link used by screen readers to allow users to skip to the main content of the page.',
},
'header.label.app.nav': {
id: 'header.label.app.nav',
defaultMessage: 'App',
description: 'The aria label for the app Nav',
},
});
export default messages;

View File

@@ -1,3 +1,4 @@
/* eslint-disable react/prop-types */
import React from 'react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import TestRenderer from 'react-test-renderer';
@@ -6,26 +7,31 @@ import { Context as ResponsiveContext } from 'react-responsive';
import Header from './index';
const HeaderComponent = ({ width, contextValue }) => (
<ResponsiveContext.Provider value={width}>
<IntlProvider locale="en" messages={{}}>
<AppContext.Provider
value={contextValue}
>
<Header />
</AppContext.Provider>
</IntlProvider>
</ResponsiveContext.Provider>
);
describe('<Header />', () => {
it('renders correctly for anonymous desktop', () => {
const component = (
<ResponsiveContext.Provider value={{ width: 1280 }}>
<IntlProvider locale="en" messages={{}}>
<AppContext.Provider value={{
authenticatedUser: null,
config: {
LMS_BASE_URL: process.env.LMS_BASE_URL,
SITE_NAME: process.env.SITE_NAME,
LOGIN_URL: process.env.LOGIN_URL,
LOGOUT_URL: process.env.LOGOUT_URL,
},
}}
>
<Header />
</AppContext.Provider>
</IntlProvider>
</ResponsiveContext.Provider>
);
const contextValue = {
authenticatedUser: null,
config: {
LMS_BASE_URL: process.env.LMS_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 = <HeaderComponent width={{ width: 1280 }} contextValue={contextValue} />;
const wrapper = TestRenderer.create(component);
@@ -33,29 +39,22 @@ describe('<Header />', () => {
});
it('renders correctly for authenticated desktop', () => {
const component = (
<ResponsiveContext.Provider value={{ width: 1280 }}>
<IntlProvider locale="en" messages={{}}>
<AppContext.Provider value={{
authenticatedUser: {
userId: 'abc123',
username: 'edX',
roles: [],
administrator: false,
},
config: {
LMS_BASE_URL: process.env.LMS_BASE_URL,
SITE_NAME: process.env.SITE_NAME,
LOGIN_URL: process.env.LOGIN_URL,
LOGOUT_URL: process.env.LOGOUT_URL,
},
}}
>
<Header />
</AppContext.Provider>
</IntlProvider>
</ResponsiveContext.Provider>
);
const contextValue = {
authenticatedUser: {
userId: 'abc123',
username: 'edX',
roles: [],
administrator: false,
},
config: {
LMS_BASE_URL: process.env.LMS_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 = <HeaderComponent width={{ width: 1280 }} contextValue={contextValue} />;
const wrapper = TestRenderer.create(component);
@@ -63,24 +62,17 @@ describe('<Header />', () => {
});
it('renders correctly for anonymous mobile', () => {
const component = (
<ResponsiveContext.Provider value={{ width: 500 }}>
<IntlProvider locale="en" messages={{}}>
<AppContext.Provider value={{
authenticatedUser: null,
config: {
LMS_BASE_URL: process.env.LMS_BASE_URL,
SITE_NAME: process.env.SITE_NAME,
LOGIN_URL: process.env.LOGIN_URL,
LOGOUT_URL: process.env.LOGOUT_URL,
},
}}
>
<Header />
</AppContext.Provider>
</IntlProvider>
</ResponsiveContext.Provider>
);
const contextValue = {
authenticatedUser: null,
config: {
LMS_BASE_URL: process.env.LMS_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 = <HeaderComponent width={{ width: 500 }} contextValue={contextValue} />;
const wrapper = TestRenderer.create(component);
@@ -88,29 +80,22 @@ describe('<Header />', () => {
});
it('renders correctly for authenticated mobile', () => {
const component = (
<ResponsiveContext.Provider value={{ width: 500 }}>
<IntlProvider locale="en" messages={{}}>
<AppContext.Provider value={{
authenticatedUser: {
userId: 'abc123',
username: 'edX',
roles: [],
administrator: false,
},
config: {
LMS_BASE_URL: process.env.LMS_BASE_URL,
SITE_NAME: process.env.SITE_NAME,
LOGIN_URL: process.env.LOGIN_URL,
LOGOUT_URL: process.env.LOGOUT_URL,
},
}}
>
<Header />
</AppContext.Provider>
</IntlProvider>
</ResponsiveContext.Provider>
);
const contextValue = {
authenticatedUser: {
userId: 'abc123',
username: 'edX',
roles: [],
administrator: false,
},
config: {
LMS_BASE_URL: process.env.LMS_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 = <HeaderComponent width={{ width: 500 }} contextValue={contextValue} />;
const wrapper = TestRenderer.create(component);

View File

@@ -1,6 +1,6 @@
import React from 'react';
export const MenuIcon = props => (
export const MenuIcon = (props) => (
<svg
width="24px"
height="24px"
@@ -14,7 +14,7 @@ export const MenuIcon = props => (
</svg>
);
export const AvatarIcon = props => (
export const AvatarIcon = (props) => (
<svg
width="24px"
height="24px"
@@ -29,7 +29,7 @@ export const AvatarIcon = props => (
</svg>
);
export const CaretIcon = props => (
export const CaretIcon = (props) => (
<svg
width="16px"
height="16px"

View File

@@ -1,30 +1,25 @@
import React from 'react';
import PropTypes from 'prop-types';
function Logo({ src, alt, ...attributes }) {
return (
<img src={src} alt={alt} {...attributes} />
);
}
const Logo = ({ src, alt, ...attributes }) => (
<img src={src} alt={alt} {...attributes} />
);
Logo.propTypes = {
src: PropTypes.string.isRequired,
alt: PropTypes.string.isRequired,
};
function LinkedLogo({
const LinkedLogo = ({
href,
src,
alt,
...attributes
}) {
return (
<a href={href} {...attributes}>
<img className="d-block" src={src} alt={alt} />
</a>
);
}
}) => (
<a href={href} {...attributes}>
<img className="d-block" src={src} alt={alt} />
</a>
);
LinkedLogo.propTypes = {
href: PropTypes.string.isRequired,

View File

@@ -2,13 +2,10 @@ import React from 'react';
import { CSSTransition } from 'react-transition-group';
import PropTypes from 'prop-types';
function MenuTrigger({ tag, className, ...attributes }) {
return React.createElement(tag, {
className: `menu-trigger ${className}`,
...attributes,
});
}
const MenuTrigger = ({ tag, className, ...attributes }) => React.createElement(tag, {
className: `menu-trigger ${className}`,
...attributes,
});
MenuTrigger.propTypes = {
tag: PropTypes.string,
className: PropTypes.string,
@@ -19,13 +16,10 @@ MenuTrigger.defaultProps = {
};
const MenuTriggerType = <MenuTrigger />.type;
function MenuContent({ tag, className, ...attributes }) {
return React.createElement(tag, {
className: ['menu-content', className].join(' '),
...attributes,
});
}
const MenuContent = ({ tag, className, ...attributes }) => React.createElement(tag, {
className: ['menu-content', className].join(' '),
...attributes,
});
MenuContent.propTypes = {
tag: PropTypes.string,
className: PropTypes.string,
@@ -35,6 +29,17 @@ MenuContent.defaultProps = {
className: null,
};
const menuPropTypes = {
tag: PropTypes.string,
onClose: PropTypes.func,
onOpen: PropTypes.func,
closeOnDocumentClick: PropTypes.bool,
respondToPointerEvents: PropTypes.bool,
className: PropTypes.string,
transitionTimeout: PropTypes.number,
transitionClassName: PropTypes.string,
children: PropTypes.arrayOf(PropTypes.node).isRequired,
};
class Menu extends React.Component {
constructor(props) {
@@ -66,10 +71,14 @@ class Menu extends React.Component {
// Event handlers
onDocumentClick(e) {
if (!this.props.closeOnDocumentClick) return;
if (!this.props.closeOnDocumentClick) {
return;
}
const clickIsInMenu = this.menu.current === e.target || this.menu.current.contains(e.target);
if (clickIsInMenu) return;
if (clickIsInMenu) {
return;
}
this.close();
}
@@ -77,7 +86,9 @@ class Menu extends React.Component {
onTriggerClick(e) {
// Let the browser follow the link of the trigger if the menu
// is already expanded and the trigger has an href attribute
if (this.state.expanded && e.target.getAttribute('href')) return;
if (this.state.expanded && e.target.getAttribute('href')) {
return;
}
e.preventDefault();
this.toggle();
@@ -89,7 +100,9 @@ class Menu extends React.Component {
}
onKeyDown(e) {
if (!this.state.expanded) return;
if (!this.state.expanded) {
return;
}
switch (e.key) {
case 'Escape': {
e.preventDefault();
@@ -131,16 +144,19 @@ class Menu extends React.Component {
}
onMouseEnter() {
if (!this.props.respondToPointerEvents) return;
if (!this.props.respondToPointerEvents) {
return;
}
this.open();
}
onMouseLeave() {
if (!this.props.respondToPointerEvents) return;
if (!this.props.respondToPointerEvents) {
return;
}
this.close();
}
// Internal functions
getFocusableElements() {
@@ -151,7 +167,7 @@ class Menu extends React.Component {
// Any extra props are attributes for the menu
const attributes = {};
Object.keys(this.props)
.filter(property => Menu.propTypes[property] === undefined)
.filter(property => menuPropTypes[property] === undefined)
.forEach((property) => {
attributes[property] = this.props[property];
});
@@ -173,7 +189,9 @@ class Menu extends React.Component {
}
open() {
if (this.props.onOpen) this.props.onOpen();
if (this.props.onOpen) {
this.props.onOpen();
}
this.setState({ expanded: true });
// Listen to touchend and click events to ensure the menu
// can be closed on mobile, pointer, and mixed input devices
@@ -182,7 +200,9 @@ class Menu extends React.Component {
}
close() {
if (this.props.onClose) this.props.onClose();
if (this.props.onClose) {
this.props.onClose();
}
this.setState({ expanded: false });
document.removeEventListener('touchend', this.onDocumentClick, true);
document.removeEventListener('click', this.onDocumentClick, true);
@@ -240,18 +260,7 @@ class Menu extends React.Component {
}
}
Menu.propTypes = {
tag: PropTypes.string,
onClose: PropTypes.func,
onOpen: PropTypes.func,
closeOnDocumentClick: PropTypes.bool,
respondToPointerEvents: PropTypes.bool,
className: PropTypes.string,
transitionTimeout: PropTypes.number,
transitionClassName: PropTypes.string,
children: PropTypes.arrayOf(PropTypes.node).isRequired,
};
Menu.propTypes = menuPropTypes;
Menu.defaultProps = {
tag: 'div',
className: null,
@@ -263,5 +272,4 @@ Menu.defaultProps = {
transitionClassName: 'menu-content',
};
export { Menu, MenuTrigger, MenuContent };

View File

@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { getConfig } from '@edx/frontend-platform';
// Local Components
import { Menu, MenuTrigger, MenuContent } from './Menu';
@@ -22,7 +23,9 @@ class MobileHeader extends React.Component {
const { mainMenu } = this.props;
// Nodes are accepted as a prop
if (!Array.isArray(mainMenu)) return mainMenu;
if (!Array.isArray(mainMenu)) {
return mainMenu;
}
return mainMenu.map((menuItem) => {
const {
@@ -89,17 +92,22 @@ class MobileHeader extends React.Component {
stickyOnMobile,
intl,
mainMenu,
userMenu,
loggedOutItems,
} = this.props;
const logoProps = { src: logo, alt: logoAltText, href: logoDestination };
const stickyClassName = stickyOnMobile ? 'sticky-top' : '';
const logoClasses = getConfig().AUTHN_MINIMAL_HEADER ? 'justify-content-left pl-3' : 'justify-content-center';
return (
<header
aria-label={intl.formatMessage(messages['header.label.main.header'])}
className={`site-header-mobile d-flex justify-content-between align-items-center shadow ${stickyClassName}`}
>
<div className="w-100 d-flex justify-content-start">
{mainMenu.length > 0 ?
<a className="nav-skip sr-only sr-only-focusable" href="#main">{intl.formatMessage(messages['header.label.skip.nav'])}</a>
{mainMenu.length > 0 ? (
<div className="w-100 d-flex justify-content-start">
<Menu className="position-static">
<MenuTrigger
tag="button"
@@ -116,26 +124,29 @@ class MobileHeader extends React.Component {
>
{this.renderMainMenu()}
</MenuContent>
</Menu> : null }
</div>
<div className="w-100 d-flex justify-content-center">
</Menu>
</div>
) : null}
<div className={`w-100 d-flex ${logoClasses}`}>
{ logoDestination === null ? <Logo className="logo" src={logo} alt={logoAltText} /> : <LinkedLogo className="logo" {...logoProps} itemType="http://schema.org/Organization" />}
</div>
<div className="w-100 d-flex justify-content-end align-items-center">
<Menu tag="nav" aria-label={intl.formatMessage(messages['header.label.secondary.nav'])} className="position-static">
<MenuTrigger
tag="button"
className="icon-button"
aria-label={intl.formatMessage(messages['header.label.account.menu'])}
title={intl.formatMessage(messages['header.label.account.menu'])}
>
<Avatar size="1.5rem" src={avatar} alt={username} />
</MenuTrigger>
<MenuContent tag="ul" className="nav flex-column pin-left pin-right border-top shadow py-2">
{loggedIn ? this.renderUserMenuItems() : this.renderLoggedOutItems()}
</MenuContent>
</Menu>
</div>
{userMenu.length > 0 || loggedOutItems.length > 0 ? (
<div className="w-100 d-flex justify-content-end align-items-center">
<Menu tag="nav" aria-label={intl.formatMessage(messages['header.label.secondary.nav'])} className="position-static">
<MenuTrigger
tag="button"
className="icon-button"
aria-label={intl.formatMessage(messages['header.label.account.menu'])}
title={intl.formatMessage(messages['header.label.account.menu'])}
>
<Avatar size="1.5rem" src={avatar} alt={username} />
</MenuTrigger>
<MenuContent tag="ul" className="nav flex-column pin-left pin-right border-top shadow py-2">
{loggedIn ? this.renderUserMenuItems() : this.renderLoggedOutItems()}
</MenuContent>
</Menu>
</div>
) : null}
</header>
);
}

200
src/StudioHeader.jsx Normal file
View File

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

108
src/StudioHeader.test.jsx Normal file
View File

@@ -0,0 +1,108 @@
/* 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

@@ -4,8 +4,14 @@ exports[`<Header /> renders correctly for anonymous desktop 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"
className="container-fluid null"
>
<div
className="nav-container position-relative d-flex align-items-center"
@@ -17,7 +23,7 @@ exports[`<Header /> renders correctly for anonymous desktop 1`] = `
<img
alt="edX"
className="d-block"
src="icon/mock/path"
src="https://edx-cdn.org/v3/default/logo.svg"
/>
</a>
<nav
@@ -58,6 +64,12 @@ exports[`<Header /> renders correctly for anonymous mobile 1`] = `
aria-label="Main"
className="site-header-mobile d-flex justify-content-between align-items-center shadow sticky-top"
>
<a
className="nav-skip sr-only sr-only-focusable"
href="#main"
>
Skip to main content
</a>
<div
className="w-100 d-flex justify-content-start"
>
@@ -126,7 +138,7 @@ exports[`<Header /> renders correctly for anonymous mobile 1`] = `
<img
alt="edX"
className="d-block"
src="icon/mock/path"
src="https://edx-cdn.org/v3/default/logo.svg"
/>
</a>
</div>
@@ -159,7 +171,6 @@ exports[`<Header /> renders correctly for anonymous mobile 1`] = `
>
<svg
aria-hidden={true}
className="text-muted"
focusable="false"
height="24px"
role="img"
@@ -189,8 +200,14 @@ exports[`<Header /> renders correctly for authenticated desktop 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"
className="container-fluid null"
>
<div
className="nav-container position-relative d-flex align-items-center"
@@ -202,7 +219,7 @@ exports[`<Header /> renders correctly for authenticated desktop 1`] = `
<img
alt="edX"
className="d-block"
src="icon/mock/path"
src="https://edx-cdn.org/v3/default/logo.svg"
/>
</a>
<nav
@@ -230,7 +247,7 @@ exports[`<Header /> renders correctly for authenticated desktop 1`] = `
aria-expanded={false}
aria-haspopup="menu"
aria-label="Account menu for edX"
className="menu-trigger btn btn-light d-inline-flex align-items-center pl-2 pr-3"
className="menu-trigger btn btn-outline-primary d-inline-flex align-items-center pl-2 pr-3"
onClick={[Function]}
>
<span
@@ -244,7 +261,6 @@ exports[`<Header /> renders correctly for authenticated desktop 1`] = `
>
<svg
aria-hidden={true}
className="text-muted"
focusable="false"
height="24px"
role="img"
@@ -294,6 +310,12 @@ exports[`<Header /> renders correctly for authenticated mobile 1`] = `
aria-label="Main"
className="site-header-mobile d-flex justify-content-between align-items-center shadow sticky-top"
>
<a
className="nav-skip sr-only sr-only-focusable"
href="#main"
>
Skip to main content
</a>
<div
className="w-100 d-flex justify-content-start"
>
@@ -362,7 +384,7 @@ exports[`<Header /> renders correctly for authenticated mobile 1`] = `
<img
alt="edX"
className="d-block"
src="icon/mock/path"
src="https://edx-cdn.org/v3/default/logo.svg"
/>
</a>
</div>
@@ -395,7 +417,6 @@ exports[`<Header /> renders correctly for authenticated mobile 1`] = `
>
<svg
aria-hidden={true}
className="text-muted"
focusable="false"
height="24px"
role="img"

View File

@@ -0,0 +1,226 @@
// 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>
`;

16
src/generic/messages.js Normal file
View File

@@ -0,0 +1,16 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
registerSentenceCase: {
id: 'general.register.sentenceCase',
defaultMessage: 'Register',
description: 'Text in a button, prompting the user to register.',
},
signInSentenceCase: {
id: 'general.signIn.sentenceCase',
defaultMessage: 'Sign in',
description: 'Text in a button, prompting the user to log in.',
},
});
export default messages;

View File

@@ -1,18 +1,28 @@
import arMessages from './messages/ar.json';
// no need to import en messages-- they are in the defaultMessage field
import es419Messages from './messages/es_419.json';
import frMessages from './messages/fr.json';
import kokrMessages from './messages/ko_KR.json';
import ptbrMessages from './messages/pt_BR.json';
import es419Messages from './messages/es_419.json';
import zhcnMessages from './messages/zh_CN.json';
import ptMessages from './messages/pt.json';
import itMessages from './messages/it.json';
import ukMessages from './messages/uk.json';
import deMessages from './messages/de.json';
import ruMessages from './messages/ru.json';
import hiMessages from './messages/hi.json';
import frCAMessages from './messages/fr_CA.json';
// no need to import en messages-- they are in the defaultMessage field
const messages = {
ar: arMessages,
'es-419': es419Messages,
fr: frMessages,
'zh-cn': zhcnMessages,
'ko-kr': kokrMessages,
'pt-br': ptbrMessages,
pt: ptMessages,
it: itMessages,
de: deMessages,
hi: hiMessages,
'fr-ca': frCAMessages,
ru: ruMessages,
uk: ukMessages,
};
export default messages;

View File

@@ -1,2 +1,33 @@
{
}
"general.register.sentenceCase": "التسجيل",
"general.signIn.sentenceCase": "تسجيل الدخول",
"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": "تطبيق",
"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": "تسجيل الخروج"
}

33
src/i18n/messages/de.json Normal file
View File

@@ -0,0 +1,33 @@
{
"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"
}

View File

@@ -1,2 +1,33 @@
{
}
"general.register.sentenceCase": "Registrarse",
"general.signIn.sentenceCase": "Iniciar sesión",
"header.links.courses": "Cursos",
"header.links.programs": "Programas",
"header.links.content.search": "Encontrar nuevo",
"header.links.schools": "Escuelas y Socios",
"header.user.menu.dashboard": "Panel de Control",
"header.user.menu.profile": "Perfil",
"header.user.menu.account.settings": "Cuenta",
"header.user.menu.order.history": "Historial de órdenes",
"header.user.menu.logout": "Cerrar sesión",
"header.user.menu.login": "Login",
"header.user.menu.register": "Registrarse",
"header.user.menu.studio.home": "Inicio Studio",
"header.user.menu.studio.maintenance": "Mantenimiento",
"header.label.account.nav": "Cuenta",
"header.label.account.menu": "Menú de la cuenta",
"header.label.account.menu.for": "Menú de la cuenta para {username}",
"header.label.main.nav": "Principal",
"header.label.main.menu": "Menú Principal",
"header.label.main.header": "Principal",
"header.label.secondary.nav": "Secondary",
"header.label.skip.nav": "Ir al contenido principal",
"header.label.app.nav": "Aplicació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"
}

View File

@@ -1,2 +1,33 @@
{
}
"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",
"header.links.schools": "Écoles et partenaires",
"header.user.menu.dashboard": "Tableau de bord",
"header.user.menu.profile": "Profil",
"header.user.menu.account.settings": "Compte",
"header.user.menu.order.history": "Historique des commandes",
"header.user.menu.logout": "Déconnexion",
"header.user.menu.login": "Connexion",
"header.user.menu.register": "S'inscrire",
"header.user.menu.studio.home": "Accueil Studio",
"header.user.menu.studio.maintenance": "Maintenance",
"header.label.account.nav": "Compte",
"header.label.account.menu": "Menu du compte",
"header.label.account.menu.for": "Menu du compte pour {username}",
"header.label.main.nav": "Principal",
"header.label.main.menu": "Menu Principal",
"header.label.main.header": "Principal",
"header.label.secondary.nav": "Secondaire",
"header.label.skip.nav": "Passer au contenu principal",
"header.label.app.nav": "Application",
"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"
}

View File

@@ -0,0 +1,33 @@
{
"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",
"header.links.schools": "Écoles et Partenaires",
"header.user.menu.dashboard": "Tableau de bord",
"header.user.menu.profile": "Profil",
"header.user.menu.account.settings": "Compte",
"header.user.menu.order.history": "Historique des commandes",
"header.user.menu.logout": "Déconnexion",
"header.user.menu.login": "Connexion",
"header.user.menu.register": "S'inscrire",
"header.user.menu.studio.home": "Accueil Studio",
"header.user.menu.studio.maintenance": "Entretien",
"header.label.account.nav": "Compte",
"header.label.account.menu": "Menu de compte",
"header.label.account.menu.for": "Menu de compte pour {username}",
"header.label.main.nav": "Principal",
"header.label.main.menu": "Menu principal",
"header.label.main.header": "Principal",
"header.label.secondary.nav": "Secondaire",
"header.label.skip.nav": "Passer au contenu de cette vue",
"header.label.app.nav": "Application",
"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"
}

33
src/i18n/messages/hi.json Normal file
View File

@@ -0,0 +1,33 @@
{
"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"
}

33
src/i18n/messages/it.json Normal file
View File

@@ -0,0 +1,33 @@
{
"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"
}

View File

@@ -1,2 +0,0 @@
{
}

33
src/i18n/messages/pt.json Normal file
View File

@@ -0,0 +1,33 @@
{
"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"
}

View File

@@ -1,2 +0,0 @@
{
}

33
src/i18n/messages/ru.json Normal file
View File

@@ -0,0 +1,33 @@
{
"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"
}

33
src/i18n/messages/uk.json Normal file
View File

@@ -0,0 +1,33 @@
{
"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"
}

View File

@@ -1,2 +1,33 @@
{
}
"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"
}

View File

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

View File

@@ -25,6 +25,30 @@ $white: #fff;
}
}
.learning-header {
min-width: 0;
.course-title-lockup {
min-width: 0;
span {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding-bottom: 0.1rem;
}
}
.user-dropdown {
.btn {
height: 3rem;
@media (max-width: -1 + map-get($grid-breakpoints, "sm")) {
padding: 0 0.5rem;
}
}
}
}
.site-header-mobile,
.site-header-desktop {
position: relative;
@@ -32,6 +56,8 @@ $white: #fff;
}
.site-header-mobile {
height: 3rem;
.nav-link {
text-decoration: none;
cursor: pointer;
@@ -43,7 +69,6 @@ $white: #fff;
.site-header-desktop {
height: 3.75rem;
box-shadow: 0 1px 0 0 rgba(0,0,0,.1);
background: $white;
.nav-link {

View File

@@ -0,0 +1,32 @@
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 genericMessages from '../generic/messages';
const AnonymousUserMenu = ({ intl }) => (
<div>
<Button
className="mr-3"
variant="outline-primary"
href={`${getConfig().LMS_BASE_URL}/register?next=${encodeURIComponent(global.location.href)}`}
>
{intl.formatMessage(genericMessages.registerSentenceCase)}
</Button>
<Button
variant="primary"
href={`${getLoginRedirectUrl(global.location.href)}`}
>
{intl.formatMessage(genericMessages.signInSentenceCase)}
</Button>
</div>
);
AnonymousUserMenu.propTypes = {
intl: intlShape.isRequired,
};
export default injectIntl(AnonymousUserMenu);

View File

@@ -0,0 +1,57 @@
import React from 'react';
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 messages from './messages';
const AuthenticatedUserDropdown = ({ intl, username }) => {
const dashboardMenuItem = (
<Dropdown.Item href={`${getConfig().LMS_BASE_URL}/dashboard`}>
{intl.formatMessage(messages.dashboard)}
</Dropdown.Item>
);
return (
<>
<a className="text-gray-700 mr-3" href={`${getConfig().SUPPORT_URL}`}>{intl.formatMessage(messages.help)}</a>
<Dropdown className="user-dropdown">
<Dropdown.Toggle variant="outline-primary">
<FontAwesomeIcon icon={faUserCircle} className="d-md-none" size="lg" />
<span data-hj-suppress className="d-none d-md-inline">
{username}
</span>
</Dropdown.Toggle>
<Dropdown.Menu className="dropdown-menu-right">
{dashboardMenuItem}
<Dropdown.Item href={`${getConfig().ACCOUNT_PROFILE_URL}/u/${username}`}>
{intl.formatMessage(messages.profile)}
</Dropdown.Item>
<Dropdown.Item href={getConfig().ACCOUNT_SETTINGS_URL}>
{intl.formatMessage(messages.account)}
</Dropdown.Item>
{ getConfig().ORDER_HISTORY_URL && (
<Dropdown.Item href={getConfig().ORDER_HISTORY_URL}>
{intl.formatMessage(messages.orderHistory)}
</Dropdown.Item>
)}
<Dropdown.Item href={getConfig().LOGOUT_URL}>
{intl.formatMessage(messages.signOut)}
</Dropdown.Item>
</Dropdown.Menu>
</Dropdown>
</>
);
};
AuthenticatedUserDropdown.propTypes = {
intl: intlShape.isRequired,
username: PropTypes.string.isRequired,
};
export default injectIntl(AuthenticatedUserDropdown);

View File

@@ -0,0 +1,79 @@
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import { getConfig } from '@edx/frontend-platform';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { AppContext } from '@edx/frontend-platform/react';
import AnonymousUserMenu from './AnonymousUserMenu';
import AuthenticatedUserDropdown from './AuthenticatedUserDropdown';
import messages from './messages';
const LinkedLogo = ({
href,
src,
alt,
...attributes
}) => (
<a href={href} {...attributes}>
<img className="d-block" src={src} alt={alt} />
</a>
);
LinkedLogo.propTypes = {
href: PropTypes.string.isRequired,
src: PropTypes.string.isRequired,
alt: PropTypes.string.isRequired,
};
const LearningHeader = ({
courseOrg, courseNumber, courseTitle, intl, showUserDropdown,
}) => {
const { authenticatedUser } = useContext(AppContext);
const headerLogo = (
<LinkedLogo
className="logo"
href={`${getConfig().LMS_BASE_URL}/dashboard`}
src={getConfig().LOGO_URL}
alt={getConfig().SITE_NAME}
/>
);
return (
<header className="learning-header">
<a className="sr-only sr-only-focusable" href="#main-content">{intl.formatMessage(messages.skipNavLink)}</a>
<div className="container-xl py-2 d-flex align-items-center">
{headerLogo}
<div className="flex-grow-1 course-title-lockup" style={{ lineHeight: 1 }}>
<span className="d-block small m-0">{courseOrg} {courseNumber}</span>
<span className="d-block m-0 font-weight-bold course-title">{courseTitle}</span>
</div>
{showUserDropdown && authenticatedUser && (
<AuthenticatedUserDropdown
username={authenticatedUser.username}
/>
)}
{showUserDropdown && !authenticatedUser && (
<AnonymousUserMenu />
)}
</div>
</header>
);
};
LearningHeader.propTypes = {
courseOrg: PropTypes.string,
courseNumber: PropTypes.string,
courseTitle: PropTypes.string,
intl: intlShape.isRequired,
showUserDropdown: PropTypes.bool,
};
LearningHeader.defaultProps = {
courseOrg: null,
courseNumber: null,
courseTitle: null,
showUserDropdown: true,
};
export default injectIntl(LearningHeader);

View File

@@ -0,0 +1,29 @@
import React from 'react';
import {
authenticatedUser, initializeMockApp, render, screen,
} from '../setupTest';
import { LearningHeader as Header } from '../index';
describe('Header', () => {
beforeAll(async () => {
// We need to mock AuthService to implicitly use `getAuthenticatedUser` within `AppContext.Provider`.
await initializeMockApp();
});
it('displays user button', () => {
render(<Header />);
expect(screen.getByRole('button')).toHaveTextContent(authenticatedUser.username);
});
it('displays course data', () => {
const courseData = {
courseOrg: 'course-org',
courseNumber: 'course-number',
courseTitle: 'course-title',
};
render(<Header {...courseData} />);
expect(screen.getByText(`${courseData.courseOrg} ${courseData.courseNumber}`)).toBeInTheDocument();
expect(screen.getByText(courseData.courseTitle)).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,41 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
dashboard: {
id: 'header.menu.dashboard.label',
defaultMessage: 'Dashboard',
description: 'The text for the user menu Dashboard navigation link.',
},
help: {
id: 'header.help.label',
defaultMessage: 'Help',
description: 'The text for the link to the Help Center',
},
profile: {
id: 'header.menu.profile.label',
defaultMessage: 'Profile',
description: 'The text for the user menu Profile navigation link.',
},
account: {
id: 'header.menu.account.label',
defaultMessage: 'Account',
description: 'The text for the user menu Account navigation link.',
},
orderHistory: {
id: 'header.menu.orderHistory.label',
defaultMessage: 'Order History',
description: 'The text for the user menu Order History navigation link.',
},
skipNavLink: {
id: 'header.navigation.skipNavLink',
defaultMessage: 'Skip to main content.',
description: 'A link used by screen readers to allow users to skip to the main content of the page.',
},
signOut: {
id: 'header.menu.signOut.label',
defaultMessage: 'Sign Out',
description: 'The label for the user menu Sign Out action.',
},
});
export default messages;

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -1,14 +1,29 @@
/* 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';
import 'jest-chain';
import { getConfig, mergeConfig } from '@edx/frontend-platform';
import { configure as configureLogging } from '@edx/frontend-platform/logging';
import { configure as configureI18n } from '@edx/frontend-platform/i18n';
import { configure as configureAuth, MockAuthService } from '@edx/frontend-platform/auth';
import { render as rtlRender } from '@testing-library/react';
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';
@@ -16,10 +31,99 @@ process.env.ECOMMERCE_BASE_URL = 'http://localhost:18130';
process.env.LANGUAGE_PREFERENCE_COOKIE_NAME = 'openedx-language-preference';
process.env.LMS_BASE_URL = 'http://localhost:18000';
process.env.LOGIN_URL = 'http://localhost:18000/login';
process.env.LOGOUT_URL = 'http://localhost:18000/login';
process.env.LOGOUT_URL = 'http://localhost:18000/logout';
process.env.MARKETING_SITE_BASE_URL = 'http://localhost:18000';
process.env.ORDER_HISTORY_URL = 'localhost:1996/orders';
process.env.REFRESH_ACCESS_TOKEN_ENDPOINT = 'http://localhost:18000/login_refresh';
process.env.SEGMENT_KEY = 'segment_whoa';
process.env.SITE_NAME = 'edX';
process.env.USER_INFO_COOKIE_NAME = 'edx-user-info';
process.env.LOGO_URL = 'https://edx-cdn.org/v3/default/logo.svg';
process.env.LOGO_TRADEMARK_URL = 'https://edx-cdn.org/v3/default/logo-trademark.svg';
process.env.LOGO_WHITE_URL = 'https://edx-cdn.org/v3/default/logo-white.svg';
process.env.FAVICON_URL = 'https://edx-cdn.org/v3/default/favicon.ico';
class MockLoggingService {
logInfo = jest.fn();
logError = jest.fn();
}
export const authenticatedUser = {
userId: 'abc123',
username: 'Mock User',
roles: [],
administrator: false,
};
export function initializeMockApp() {
mergeConfig({
INSIGHTS_BASE_URL: process.env.INSIGHTS_BASE_URL || null,
STUDIO_BASE_URL: process.env.STUDIO_BASE_URL || null,
TWITTER_URL: process.env.TWITTER_URL || null,
BASE_URL: process.env.BASE_URL || null,
LMS_BASE_URL: process.env.LMS_BASE_URL || null,
LOGIN_URL: process.env.LOGIN_URL || null,
LOGOUT_URL: process.env.LOGOUT_URL || null,
REFRESH_ACCESS_TOKEN_ENDPOINT: process.env.REFRESH_ACCESS_TOKEN_ENDPOINT || null,
ACCESS_TOKEN_COOKIE_NAME: process.env.ACCESS_TOKEN_COOKIE_NAME || null,
CSRF_TOKEN_API_PATH: process.env.CSRF_TOKEN_API_PATH || null,
LOGO_URL: process.env.LOGO_URL || null,
SITE_NAME: process.env.SITE_NAME || null,
authenticatedUser: {
userId: 'abc123',
username: 'Mock User',
roles: [],
administrator: false,
},
});
const loggingService = configureLogging(MockLoggingService, {
config: getConfig(),
});
const authService = configureAuth(MockAuthService, {
config: getConfig(),
loggingService,
});
// i18n doesn't have a service class to return.
configureI18n({
config: getConfig(),
loggingService,
messages: [appMessages],
});
return { loggingService, authService };
}
function render(
ui,
{
store = null,
...renderOptions
} = {},
) {
const Wrapper = ({ children }) => (
// eslint-disable-next-line react/jsx-filename-extension
<IntlProvider locale="en">
<AppProvider store={store}>
{children}
</AppProvider>
</IntlProvider>
);
Wrapper.propTypes = {
children: PropTypes.node.isRequired,
};
return rtlRender(ui, { wrapper: Wrapper, ...renderOptions });
}
// Re-export everything.
export * from '@testing-library/react';
// Override `render` method.
export {
render,
};

View File

@@ -7,4 +7,9 @@ module.exports = createConfig('webpack-dev', {
path: path.resolve(__dirname, 'example/dist'),
publicPath: '/',
},
resolve: {
alias: {
'@edx/frontend-component-header': path.resolve(__dirname, 'src'),
},
},
});