Compare commits

...

142 Commits

Author SHA1 Message Date
Sarina Canelake
1d12c6506e fix: Update sandbox link in studio footer (#561) 2025-10-20 19:01:40 +00:00
renovate[bot]
39a88243e5 chore(deps): update dependency @openedx/paragon to v23.14.9 (#558)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-20 14:59:07 -04:00
oleksandr.buhaienko
9466f6aced test: Remove support for Node 20 2025-09-26 11:55:14 -03:00
bydawen
7cb75a333f build: Upgrade to Node 24 (#546)
Co-authored-by: Peter Kulko <93188219+PKulkoRaccoonGang@users.noreply.github.com>
2025-09-26 09:19:12 -03:00
Feanil Patel
4dcbae37f2 Merge pull request #557 from openedx/feanil/remove-reactifex-packages
build: remove unused @edx/reactifex package
2025-09-25 14:03:22 -04:00
Feanil Patel
636e71e141 build: remove unused @edx/reactifex package
Remove @edx/reactifex package from devDependencies as it is no longer
needed. Translation extraction functionality has been verified to work
correctly without these dependencies.

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-25 14:01:42 -04:00
renovate[bot]
320cfa17a2 chore(deps): update dependency ts-jest to v29.4.4 (#555)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-25 14:01:30 -04:00
bydawen
b61302c3f5 test: Add Node 24 to CI matrix (#545) 2025-09-16 10:40:30 -04:00
renovate[bot]
e6444392a6 chore(deps): update dependency @openedx/paragon to v23.14.2 (#554)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 20:15:53 +00:00
renovate[bot]
cd10265c60 chore(deps): update dependency @edx/frontend-platform to v8.5.1 (#553)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 05:44:59 +00:00
renovate[bot]
c7a84e62f1 fix(deps): update dependency @fortawesome/react-fontawesome to v0.2.6 (#552)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 10:11:45 +00:00
renovate[bot]
2d3c5ecc4c chore(deps): update dependency @openedx/frontend-build to v14.6.2 (#551)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 06:18:00 +00:00
renovate[bot]
b6c1f77331 fix(deps): update dependency ts-jest to v29.4.1 (#544)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-31 09:11:17 +00:00
renovate[bot]
a3ef54e2bf chore(deps): update dependency @openedx/frontend-build to v14.6.1 (#543)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-29 17:18:07 +00:00
Jacobo Dominguez
bb69baecb0 refactor: replacing injectIntl with useIntl (#550) 2025-08-29 13:15:46 -04:00
renovate[bot]
95e191cf86 chore(deps): update dependency @openedx/paragon to v23.12.2 (#541)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-16 12:52:14 +00:00
renovate[bot]
61702da769 chore(deps): update dependency @edx/frontend-platform to v8.4.0 (#540)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-16 06:07:06 +00:00
renovate[bot]
47d467747f chore(deps): update dependency @openedx/paragon to v23.10.1 (#539)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-09 11:58:16 +00:00
renovate[bot]
75bd146e72 chore(deps): update dependency @edx/frontend-platform to v8.3.9 (#538)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-09 06:00:23 +00:00
jacobo-dominguez-wgu
db201af910 feat: updating help buttons links and texts (#535) 2025-06-03 13:15:58 -04:00
renovate[bot]
281b817084 chore(deps): update dependency @openedx/paragon to v23.10.0 (#537)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-02 11:18:45 +00:00
renovate[bot]
08b69b8096 chore(deps): update dependency @edx/frontend-platform to v8.3.8 (#536)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-02 06:14:10 +00:00
jacobo-dominguez-wgu
7c8876490d feat: updating help section and new slots created (#530) 2025-05-30 11:56:53 -04:00
renovate[bot]
ba2e46bfa9 chore(deps): update dependency react-router-dom to v6.30.1 (#534)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-26 12:46:00 +00:00
renovate[bot]
09cc332306 chore(deps): update dependency @edx/frontend-platform to v8.3.7 (#533)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-26 06:30:37 +00:00
renovate[bot]
1f91a60b6d chore(deps): update dependency @openedx/paragon to v23.6.0 (#532)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-19 07:49:18 +00:00
renovate[bot]
490da867df fix(deps): update dependency ts-jest to v29.3.4 (#531)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-19 04:14:02 +00:00
renovate[bot]
fd723e6063 chore(deps): update dependency @openedx/paragon to v23.5.1 (#529)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-12 11:38:45 +00:00
renovate[bot]
664ca3e78f chore(deps): update dependency @edx/frontend-platform to v8.3.6 (#528)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-12 06:10:48 +00:00
renovate[bot]
18a8a76105 chore(deps): update dependency @openedx/frontend-build to v14.6.0 (#527)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-05 06:41:24 +00:00
renovate[bot]
0ad89f88a1 fix(deps): update dependency ts-jest to v29.3.2 (#525)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-28 06:11:35 +00:00
renovate[bot]
b0391866a3 chore(deps): update dependency @openedx/frontend-build to v14.5.0 (#524)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-28 04:15:28 +00:00
Brian Smith
3a9e3f8fba feat: add another FooterSlot id alias (#523) 2025-04-24 13:53:20 -04:00
renovate[bot]
b2c55a09e5 chore(deps): update dependency @openedx/paragon to v23.4.5 (#522)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-21 06:00:42 +00:00
Brian Smith
6795147255 feat: move FooterSlot and StudioFooterSlot in from slot-footer (#521)
Co-authored-by: Muhammad Anas <88967643+Anas12091101@users.noreply.github.com>
2025-04-17 17:35:52 -04:00
Muhammad Faraz Maqsood
a87c111b3c feat: use PluginSlot to add your site logo to studio footer (#520) 2025-04-17 09:18:52 -04:00
Sarina Canelake
655d6c058b Merge pull request #517 from openedx/update-docs.edx.org-links
docs: Update references to docs.edx.org
2025-04-16 09:20:26 -04:00
Adolfo R. Brandes
923009191f Merge branch 'master' into update-docs.edx.org-links 2025-04-14 11:53:42 -03:00
Adolfo R. Brandes
80da098ec8 Merge pull request #516 from regisb/regisb/no-husky
chore: remove husky 🪓🐶
2025-04-14 11:08:42 -03:00
Adolfo R. Brandes
0197c85de5 Merge branch 'master' into regisb/no-husky 2025-04-14 11:04:08 -03:00
renovate[bot]
0ef407458c chore(deps): update dependency @openedx/frontend-build to v14.4.2 (#519)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-07 13:21:50 +00:00
renovate[bot]
c5d83f658a chore(deps): update dependency @edx/frontend-platform to v8.3.4 (#518)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-07 07:10:41 +00:00
Sarina Canelake
eb941181ea docs: Update references to docs.edx.org
With the transition to docs.openedx.org, update links to docs.edx.org to the community-supported site.
2025-04-06 10:35:39 -04:00
Régis Behmo
ed379258a8 feat: avoid pulling entire lodash library into build (#515)
Importing from lodash was causing the entire library to be pulled. This
change reduces the compressed production build size of
frontend-app-profile by 6kB.
2025-04-02 08:52:10 -04:00
Régis Behmo
8109ba39bb chore: remove husky 🪓🐶
We remove husky, which is triggering pre-push git hooks, including
running "npm lint". This is causing failures when building Docker
images, because "npm clean-install --omit=dev" automatically triggers "npm
prepare", which attemps to run "husky". But husky is not listed in the
build dependencies, only in devDependencies. As a consequence, package
installation is failing with the following error:

        14.13 > @edx/frontend-app-ora-grading@0.0.1 prepare
        14.13 > husky install
        14.13
        14.15 sh: 1: husky: not found

Similar to: https://github.com/openedx/frontend-app-learning/pull/1622
2025-04-02 12:48:48 +02:00
renovate[bot]
ebb87070da chore(deps): update dependency @openedx/frontend-build to v14.4.1 (#514)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-31 11:06:12 +00:00
renovate[bot]
cbcaf4c9fe chore(deps): update dependency @openedx/paragon to v23.4.3 (#513)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-31 05:44:12 +00:00
renovate[bot]
d3dda9e56d chore(deps): update dependency @openedx/paragon to v23.4.2 (#512)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-24 11:07:50 +00:00
renovate[bot]
db3c54201c chore(deps): update dependency @edx/frontend-platform to v8.3.3 (#511)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-24 07:03:14 +00:00
renovate[bot]
8f8b4645b4 chore(deps): update dependency react-router-dom to v6.30.0 (#510)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 11:45:57 +00:00
renovate[bot]
194f93e85e chore(deps): update dependency @openedx/paragon to v23.4.1 (#509)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 05:40:47 +00:00
Peter Kulko
10fc7a2d72 feat: add support for Paragon design tokens (#303)
---------

Co-authored-by: Diana Catalina Olarte <diana.olarte@edunext.co>
2025-03-13 10:58:01 -04:00
renovate[bot]
1d7a871cfd chore(deps): update dependency react-redux to v8.1.3 (#505)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 12:27:46 +00:00
renovate[bot]
cf09847e95 chore(deps): update dependency @openedx/paragon to v22.15.3 (#504)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 05:41:21 +00:00
Adam Stankiewicz
84e30f0fb7 feat: enable support for React 18 (#499) 2025-02-24 10:55:09 -05:00
renovate[bot]
288b6c7d0f fix(deps): update dependency ts-jest to v29.2.6 (#503)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-24 11:43:20 +00:00
renovate[bot]
9a23003f7f chore(deps): update dependency @openedx/paragon to v22.15.2 (#502)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-24 06:07:02 +00:00
renovate[bot]
922a31b90b chore(deps): update dependency @openedx/paragon to v22.15.1 (#501)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-17 05:54:05 +00:00
Adolfo R. Brandes
73236b296b Merge pull request #500 from openedx/feanil/add_owner
docs: Document the owner and drop the old metadata file
2025-02-14 16:55:10 -03:00
Feanil Patel
eb92d8f1f1 docs: Document the owner and drop the old metadata file
This is going to be changing soon with the module federation work so I
think it makes sense to be maintained by the committers-frontend group.

I'm also cleaning up the old openedx.yaml file which is obsolete and out
of date while I'm adding the new metadata that should be up-to-date.
2025-02-14 14:49:31 -05:00
renovate[bot]
bc79936022 chore(deps): update dependency @openedx/paragon to v22.14.0 (#498)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-27 08:52:28 +00:00
renovate[bot]
2aac71a988 chore(deps): update dependency @edx/browserslist-config to v1.5.0 (#497)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-27 05:27:37 +00:00
renovate[bot]
032105da7f chore(deps): update dependency react-router-dom to v6.28.2 (#496)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 05:06:33 +00:00
renovate[bot]
6e2ccf2f2c chore(deps): update dependency @openedx/paragon to v22.13.0 (#495)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-13 08:51:07 +00:00
renovate[bot]
907145be24 chore(deps): update dependency @edx/frontend-platform to v8.1.5 (#494)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-13 05:26:41 +00:00
renovate[bot]
58791757d7 chore(deps): update dependency @edx/frontend-platform to v8.1.4 (#492)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-06 07:47:14 +00:00
renovate[bot]
583aaf676e chore(deps): update dependency @edx/browserslist-config to v1.4.0 (#491)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-30 06:13:49 +00:00
renovate[bot]
2e46ec1f9b fix(deps): update font awesome to v6.7.2 (#490)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-23 06:15:12 +00:00
renovate[bot]
bb80d92512 chore(deps): update dependency react-router-dom to v6.28.1 (#489)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-23 05:32:44 +00:00
renovate[bot]
e138dedd29 chore(deps): update dependency @edx/frontend-platform to v8.1.3 (#488)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-16 06:41:30 +00:00
renovate[bot]
073a6c2a18 chore(deps): update dependency @openedx/frontend-build to v14.2.2 (#487)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-09 06:28:35 +00:00
renovate[bot]
41cf64b9d7 chore(deps): update dependency @openedx/frontend-plugin-framework to v1.4.1 (#485)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-02 05:14:54 +00:00
renovate[bot]
908b5b0b11 fix(deps): update font awesome to v6.7.1 (#484)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-25 11:37:33 +00:00
renovate[bot]
59dd82c919 chore(deps): update dependency @openedx/frontend-build to v14.2.0 (#483)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-25 07:08:46 +00:00
renovate[bot]
afed581a18 chore(deps): update dependency @openedx/paragon to v22.10.0 (#481)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-18 06:59:20 +00:00
renovate[bot]
f6b02cbfe7 chore(deps): update dependency react-router-dom to v6.28.0 (#480)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-11 06:47:54 +00:00
renovate[bot]
ede01dbd20 chore(deps): update dependency @openedx/frontend-plugin-framework to v1.4.0 2024-11-04 07:07:45 +00:00
Bilal Qamar
4a0f244a1a test: Remove support for Node 18 (#472) 2024-10-31 16:04:18 -04:00
renovate[bot]
906d9a78c1 chore(deps): update dependency @openedx/paragon to v22.9.0 2024-10-21 06:15:16 +00:00
renovate[bot]
ac0d5f42f7 chore(deps): update dependency react-router-dom to v6.27.0 2024-10-14 04:18:36 +00:00
renovate[bot]
e9d1b435dd chore(deps): update dependency @edx/frontend-platform to v8.1.2 2024-10-07 07:12:08 +00:00
Rômulo Penido
cc647e7bc8 feat: expose containerProps in StudioFooter [FC-0062] (#463) 2024-10-01 09:44:49 -04:00
renovate[bot]
2d5f5fe787 chore(deps): update dependency @openedx/frontend-build to v14.1.5 2024-09-30 04:09:28 +00:00
renovate[bot]
ef7bd9284a chore(deps): update dependency @openedx/frontend-build to v14.1.4 2024-09-23 04:33:25 +00:00
Bilal Qamar
10f9d7c1cd build: Upgrade to Node 20 (#471) 2024-09-19 17:50:12 -04:00
Bilal Qamar
6b3a49ccec test: Add Node 20 to CI matrix (#467) 2024-09-16 11:41:53 -04:00
renovate[bot]
f473c98bb8 chore(deps): update dependency @openedx/paragon to v22.8.1 2024-09-16 11:27:58 +00:00
renovate[bot]
bd52be3177 chore(deps): update dependency react-router-dom to v6.26.2 2024-09-16 07:18:57 +00:00
renovate[bot]
ef12ac20fe chore(deps): update dependency @openedx/frontend-plugin-framework to v1.3.0 2024-09-09 07:44:24 +00:00
renovate[bot]
7fa6bd8740 chore(deps): update dependency @openedx/frontend-build to v14.1.2 2024-09-09 05:03:32 +00:00
renovate[bot]
8d9178f532 chore(deps): update dependency @openedx/frontend-build to v14.1.1 2024-09-02 07:37:30 +00:00
renovate[bot]
98bcacfced fix(deps): update font awesome to v6.6.0 2024-08-26 11:26:33 +00:00
renovate[bot]
ac78454133 fix(deps): update dependency ts-jest to v29.2.5 2024-08-26 07:03:36 +00:00
renovate[bot]
09b7c8be9a chore(deps): update dependency @openedx/frontend-build to v14.1.0 2024-08-19 10:06:43 +00:00
renovate[bot]
c992a9129a chore(deps): update dependency react-router-dom to v6.26.1 2024-08-19 07:52:04 +00:00
renovate[bot]
7f6ad73a25 chore(deps): update dependency @openedx/frontend-build to v14.0.15 2024-08-12 12:21:16 +00:00
renovate[bot]
cefbd28d9b chore(deps): update dependency @openedx/frontend-plugin-framework to v1.2.3 2024-08-12 06:52:01 +00:00
renovate[bot]
c26bdc54e1 chore(deps): update dependency react-router-dom to v6.26.0 2024-08-05 09:38:33 +00:00
renovate[bot]
02f6b44368 fix(deps): update dependency ts-jest to v29.2.4 2024-08-05 06:46:35 +00:00
renovate[bot]
4cbde7e38f chore(deps): update dependency @openedx/frontend-build to v14.0.14 2024-07-29 10:54:37 +00:00
renovate[bot]
bdb6ecb79f chore(deps): update dependency @edx/frontend-platform to v8.1.1 2024-07-29 07:14:47 +00:00
renovate[bot]
46c1ffd731 chore(deps): update dependency react-router-dom to v6.25.1 2024-07-22 10:14:39 +00:00
renovate[bot]
b6bd211c6e chore(deps): update dependency @openedx/paragon to v22.7.0 2024-07-22 06:29:45 +00:00
renovate[bot]
ef8445a1b2 fix(deps): update dependency ts-jest to v29.2.3 2024-07-22 04:08:27 +00:00
renovate[bot]
5b34595520 fix(deps): update dependency ts-jest to v29.2.2 2024-07-15 09:54:25 +00:00
renovate[bot]
32ae2d8e7c chore(deps): update dependency @edx/frontend-platform to v8.1.0 2024-07-15 06:51:47 +00:00
renovate[bot]
abac1cd6d4 chore(deps): update dependency react-router-dom to v6.24.1 2024-07-08 08:29:05 +00:00
renovate[bot]
e0115e3d88 chore(deps): update dependency @openedx/frontend-plugin-framework to v1.2.2 2024-07-08 04:03:54 +00:00
renovate[bot]
ff18f3add9 fix(deps): update dependency ts-jest to v29.1.5 2024-07-01 12:24:59 +00:00
renovate[bot]
4926c3105b chore(deps): update dependency react-router-dom to v6.24.0 2024-07-01 06:05:04 +00:00
renovate[bot]
838a81a020 chore(deps): update dependency @openedx/paragon to v22.6.1 2024-07-01 04:23:27 +00:00
Adolfo R. Brandes
0ac6840cea Merge pull request #437 from arbrandes/update-codecov
build: Update codecov and use token
2024-06-27 09:23:31 -03:00
Adolfo R. Brandes
26a17bf2e4 build: Update codecov and use token
Update codecov to the latest version and start using the org-wide token for uploads.

See https://github.com/openedx/wg-frontend/issues/179
2024-06-27 09:21:08 -03:00
renovate[bot]
17b9710810 chore(deps): update dependency @openedx/frontend-build to v14.0.10 2024-06-24 07:49:38 +00:00
renovate[bot]
df0129485e chore(deps): update dependency @openedx/frontend-build to v14.0.9 2024-06-17 07:25:48 +00:00
renovate[bot]
1c2f290faa chore(deps): update dependency @openedx/paragon to v22.6.0 2024-06-10 04:43:05 +00:00
renovate[bot]
9af9024bcc chore(deps): update dependency @openedx/frontend-plugin-framework to v1.2.1 2024-06-03 09:31:52 +00:00
renovate[bot]
0008bd485a chore(deps): update dependency @edx/frontend-platform to v8.0.4 2024-06-03 07:09:11 +00:00
renovate[bot]
181b9916a0 fix(deps): update dependency ts-jest to v29.1.4 2024-06-03 04:51:17 +00:00
renovate[bot]
22dbec317c fix(deps): update dependency ts-jest to v29.1.3 2024-05-27 11:12:12 +00:00
renovate[bot]
6f15e4dafa fix(deps): update dependency @fortawesome/react-fontawesome to v0.2.2 2024-05-27 07:24:23 +00:00
renovate[bot]
9f5aed3c76 fix(deps): update dependency @fortawesome/react-fontawesome to v0.2.1 2024-05-20 13:16:53 +00:00
renovate[bot]
54bc04b182 chore(deps): update dependency @edx/frontend-platform to v8.0.3 2024-05-20 04:41:57 +00:00
Brian Smith
ea2578fa15 fix!: remove FooterSlot component
BREAKING CHANGE: the `FooterSlot` component will now be provided by `frontend-slot-footer`.
2024-05-14 11:31:03 -03:00
renovate[bot]
5f7fab236d chore(deps): update dependency @openedx/paragon to v22.4.0 2024-05-13 09:24:28 +00:00
renovate[bot]
adf5f6c9a8 chore(deps): update dependency react-router-dom to v6.23.1 2024-05-13 06:20:49 +00:00
renovate[bot]
132b080195 chore(deps): update dependency @edx/frontend-platform to v8.0.2 2024-05-13 04:57:36 +00:00
Brian Smith
0d4ea96874 feat: provide FooterSlot
This allows consuming MFEs to utilize a standardized Frontend
Plugin Framework `PluginSlot` for replacing the footer.
2024-05-09 13:44:51 -03:00
renovate[bot]
8b9f5fa348 chore(deps): update dependency @openedx/paragon to v22.3.2 2024-05-06 06:47:39 +00:00
renovate[bot]
c014e30ca8 chore(deps): update dependency react-router-dom to v6.23.0 2024-04-29 09:52:14 +00:00
renovate[bot]
0e6ab3c358 chore(deps): update dependency @openedx/paragon to v22.3.1 2024-04-29 06:25:16 +00:00
renovate[bot]
7d0a126580 chore(deps): update dependency @edx/frontend-platform to v8.0.1 2024-04-29 04:40:18 +00:00
Bilal Qamar
c422ce40be feat: Updated frontend-build to bump jest version to v29 (#317)
* refactor: updated frontend-build to bump jest version to v29

* refactor: updated snapshots

* refactor: updated frontend-build & failing snapshots

* refactor: updated package-lock

* refactor: updated frontend-build to alpha, updated snapshots

* refactor: added overrides to resolve dependency issues

* refactor: updated frontend-build to the version which has eslint alpha synced with master

* feat: updated frontend-build and frontend-platform major versions

* refactor: updated peerDependency as per suggestion
2024-04-23 13:56:59 +05:00
renovate[bot]
7299d5367f chore(deps): update dependency @openedx/paragon to v22.2.2 2024-04-22 10:02:00 +00:00
renovate[bot]
38203b91ba chore(deps): update dependency @edx/frontend-platform to v7.1.4 2024-04-22 06:32:01 +00:00
renovate[bot]
83c340fe08 chore(deps): update dependency @openedx/paragon to v22.2.1 2024-04-15 13:02:17 +00:00
renovate[bot]
9eaebf4b11 chore(deps): update dependency @openedx/frontend-build to v13.1.4 2024-04-15 09:58:34 +00:00
renovate[bot]
2a883857cf chore(deps): update dependency @edx/brand to v1.2.3 2024-04-15 06:41:52 +00:00
renovate[bot]
5f26c1ae93 fix(deps): update font awesome to v6.5.2 2024-04-15 05:16:19 +00:00
Samir Sabri
89ad7234ac feat!: remove Transifex calls for OEP-58 (#355) 2024-03-25 11:23:10 -04:00
renovate[bot]
936915f81f chore(deps): update dependency @edx/frontend-platform to v7.1.3 2024-03-25 12:10:00 +00:00
Bilal Qamar
b993992987 chore(deps): update frontend-platform to v7 (#409) 2024-03-22 11:34:48 +05:00
65 changed files with 18030 additions and 15910 deletions

View File

@@ -14,12 +14,10 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Setup Nodejs Env
run: echo "NODE_VER=`cat .nvmrc`" >> $GITHUB_ENV
- name: Setup Nodejs - name: Setup Nodejs
uses: actions/setup-node@v4 uses: actions/setup-node@v4
with: with:
node-version: ${{ env.NODE_VER }} node-version-file: '.nvmrc'
- name: Install dependencies - name: Install dependencies
run: npm ci run: npm ci
- name: Validate package-lock.json changes - name: Validate package-lock.json changes
@@ -33,4 +31,7 @@ jobs:
- name: i18n_extract - name: i18n_extract
run: npm run i18n_extract run: npm run i18n_extract
- name: Coverage - name: Coverage
uses: codecov/codecov-action@v3 uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true

View File

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

View File

@@ -30,7 +30,10 @@ jobs:
- name: i18n_extract - name: i18n_extract
run: npm run i18n_extract run: npm run i18n_extract
- name: Coverage - name: Coverage
uses: codecov/codecov-action@v3 uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: false
- name: Build - name: Build
run: npm run build run: npm run build
- name: Release - name: Release

1
.gitignore vendored
View File

@@ -10,3 +10,4 @@ src/i18n/transifex_input.json
temp/babel-plugin-react-intl temp/babel-plugin-react-intl
/.vscode /.vscode
module.config.js module.config.js
src/i18n/messages

2
.nvmrc
View File

@@ -1 +1 @@
18 24

View File

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

View File

@@ -1,6 +1,3 @@
export TRANSIFEX_RESOURCE = frontend-component-footer
transifex_langs = "ar,fr,es_419,zh_CN,pt,it,de,uk,ru,hi,fr_CA,it_IT,pt_PT,de_DE"
transifex_utils = ./node_modules/.bin/transifex-utils.js transifex_utils = ./node_modules/.bin/transifex-utils.js
i18n = ./src/i18n i18n = ./src/i18n
transifex_input = $(i18n)/transifex_input.json transifex_input = $(i18n)/transifex_input.json
@@ -35,20 +32,6 @@ detect_changed_source_translations:
# Checking for changed translations... # Checking for changed translations...
git diff --exit-code $(i18n) git diff --exit-code $(i18n)
# Pushes translations to Transifex. You must run make extract_translations first.
push_translations:
# Pushing strings to Transifex...
tx push -s
# Fetching hashes from Transifex...
./node_modules/@edx/reactifex/bash_scripts/get_hashed_strings_v3.sh
# Writing out comments to file...
$(transifex_utils) $(transifex_temp) --comments --v3-scripts-path
# Pushing comments to Transifex...
./node_modules/@edx/reactifex/bash_scripts/put_comments_v3.sh
# Pulls translations from Transifex.
pull_translations:
tx pull -t -f --mode reviewed --languages=$(transifex_langs)
# This target is used by Travis. # This target is used by Travis.
validate-no-uncommitted-package-lock-changes: validate-no-uncommitted-package-lock-changes:

View File

@@ -62,9 +62,9 @@ Cloning and Startup
``git clone https://github.com/openedx/frontend-component-footer.git`` ``git clone https://github.com/openedx/frontend-component-footer.git``
2. Use node v18.x. 2. Use node v24.x.
The current version of the micro-frontend build scripts support node 18. The current version of the micro-frontend build scripts support node 24.
Using other major versions of node *may* work, but this is unsupported. For Using other major versions of node *may* work, but this is unsupported. For
convenience, this repository includes an .nvmrc file to help in setting the convenience, this repository includes an .nvmrc file to help in setting the
correct node version via `nvm <https://github.com/nvm-sh/nvm>`_. correct node version via `nvm <https://github.com/nvm-sh/nvm>`_.
@@ -93,6 +93,12 @@ This library has the following exports:
language from its dropdown. language from its dropdown.
* supportedLanguages: An array of objects representing available languages. See example below for object shape. * supportedLanguages: An array of objects representing available languages. See example below for object shape.
Plugin
======
The footer can be replaced or modified using `Frontend Plugin Framework <https://github.com/openedx/frontend-plugin-framework>`_.
Information on how to replace or modify the footer is available `here </src/plugin-slots>`_.
Examples Examples
======== ========
@@ -113,6 +119,8 @@ Component Usage Example::
* `An example of minimal component and messages usage. <https://github.com/openedx/frontend-template-application/blob/3355bb3a96232390e9056f35b06ffa8f105ed7ca/src/index.jsx#L23>`_ * `An example of minimal component and messages usage. <https://github.com/openedx/frontend-template-application/blob/3355bb3a96232390e9056f35b06ffa8f105ed7ca/src/index.jsx#L23>`_
* `An example of SCSS file usage. <https://github.com/openedx/frontend-template-application/blob/3cd5485bf387b8c479baf6b02bf59e3061dc3465/src/index.scss#L9>`_ * `An example of SCSS file usage. <https://github.com/openedx/frontend-template-application/blob/3cd5485bf387b8c479baf6b02bf59e3061dc3465/src/index.scss#L9>`_
Development Development
=========== ===========

14
catalog-info.yaml Normal file
View File

@@ -0,0 +1,14 @@
# This file records information about this repo. Its use is described in OEP-55:
# https://open-edx-proposals.readthedocs.io/en/latest/processes/oep-0055-proc-project-maintainers.html
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: "frontend-component-footer"
description: "A generic footer for the Open edX micro-frontend applications."
annotations:
openedx.org/arch-interest-groups: ""
spec:
owner: group:committers-frontend
type: "library"
lifecycle: "production"

View File

@@ -1,6 +1,4 @@
@import "@edx/brand/paragon/fonts"; @use "@openedx/paragon/dist/core.min.css" as paragonCore;
@import "@edx/brand/paragon/variables"; @use "@openedx/paragon/dist/light.min.css" as paragonLight;
@import "@openedx/paragon/scss/core/core";
@import "@edx/brand/paragon/overrides";
@import "@edx/frontend-component-footer/footer"; @import "@edx/frontend-component-footer/footer";

View File

@@ -1,8 +0,0 @@
# openedx.yaml
---
owner: edx/fedx-team
tags:
- library
- component
- react

32628
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,19 +10,16 @@
"build": "make build", "build": "make build",
"i18n_extract": "fedx-scripts formatjs extract", "i18n_extract": "fedx-scripts formatjs extract",
"lint": "fedx-scripts eslint --ext .js --ext .jsx .", "lint": "fedx-scripts eslint --ext .js --ext .jsx .",
"lint:fix": "fedx-scripts eslint --fix --ext .js --ext .jsx .",
"snapshot": "fedx-scripts jest --updateSnapshot", "snapshot": "fedx-scripts jest --updateSnapshot",
"start": "fedx-scripts webpack-dev-server --progress", "start": "fedx-scripts webpack-dev-server --progress",
"start:with-theme": "paragon install-theme && npm start && npm install", "start:with-theme": "paragon install-theme && npm start && npm install",
"test": "fedx-scripts jest" "test": "fedx-scripts jest --coverage",
"test:watch": "npm run test -- --watch"
}, },
"files": [ "files": [
"/dist" "/dist"
], ],
"husky": {
"hooks": {
"pre-commit": "npm run lint"
}
},
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/openedx/frontend-component-footer.git" "url": "git+https://github.com/openedx/frontend-component-footer.git"
@@ -36,35 +33,39 @@
"devDependencies": { "devDependencies": {
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2", "@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
"@edx/browserslist-config": "^1.1.1", "@edx/browserslist-config": "^1.1.1",
"@openedx/frontend-build": "^13.0.19", "@edx/frontend-platform": "^8.2.1",
"@openedx/paragon": "22.0.0", "@openedx/frontend-build": "^14.3.1",
"@edx/frontend-platform": "6.2.0", "@openedx/paragon": "^23.3.0",
"@edx/reactifex": "^2.1.1",
"@testing-library/jest-dom": "^5.16.4", "@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^12.1.1", "@testing-library/react": "^16.2.0",
"husky": "8.0.3", "@testing-library/dom": "^10.4.0",
"@testing-library/user-event": "^14.6.1",
"prop-types": "15.8.1", "prop-types": "15.8.1",
"react": "17.0.2", "react": "^18.3.1",
"react-dom": "17.0.2", "react-dom": "^18.3.1",
"react-redux": "7.2.9", "react-redux": "8.1.3",
"react-router-dom": "6.21.3", "react-router-dom": "6.30.1",
"react-test-renderer": "17.0.2", "react-test-renderer": "18.3.1",
"redux": "4.2.1", "redux": "4.2.1",
"semantic-release": "21.1.2" "semantic-release": "21.1.2"
}, },
"dependencies": { "dependencies": {
"@fortawesome/fontawesome-svg-core": "6.5.1", "@fortawesome/fontawesome-svg-core": "6.7.2",
"@fortawesome/free-brands-svg-icons": "6.5.1", "@fortawesome/free-brands-svg-icons": "6.7.2",
"@fortawesome/free-regular-svg-icons": "6.5.1", "@fortawesome/free-regular-svg-icons": "6.7.2",
"@fortawesome/free-solid-svg-icons": "6.5.1", "@fortawesome/free-solid-svg-icons": "6.7.2",
"@fortawesome/react-fontawesome": "0.2.0", "@fortawesome/react-fontawesome": "0.2.6",
"lodash": "^4.17.21" "@openedx/frontend-plugin-framework": "^1.7.0",
"classnames": "^2.5.1",
"jest-environment-jsdom": "^29.7.0",
"lodash": "^4.17.21",
"ts-jest": "^29.1.2"
}, },
"peerDependencies": { "peerDependencies": {
"@edx/frontend-platform": "^7.0.0", "@edx/frontend-platform": "^7.0.0 || ^8.0.0",
"@openedx/paragon": ">= 21.11.3 < 23.0.0", "@openedx/paragon": ">= 21.11.3 < 24.0.0",
"prop-types": "^15.5.10", "prop-types": "^15.5.10",
"react": "^16.9.0 || ^17.0.0", "react": "^16.9.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.9.0 || ^17.0.0" "react-dom": "^16.9.0 || ^17.0.0 || ^18.0.0"
} }
} }

View File

@@ -1,5 +1,5 @@
$gray-footer: #fcfcfc !default; $gray-footer: #fcfcfc !default;
.footer { .footer {
background-color: $gray-footer; background-color: var(--pgn-color-light-100, $gray-footer);
} }

View File

@@ -1,6 +1,6 @@
import React from 'react'; import React, { useContext } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { useIntl } from '@edx/frontend-platform/i18n';
import { sendTrackEvent } from '@edx/frontend-platform/analytics'; import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import { ensureConfig } from '@edx/frontend-platform'; import { ensureConfig } from '@edx/frontend-platform';
import { AppContext } from '@edx/frontend-platform/react'; import { AppContext } from '@edx/frontend-platform/react';
@@ -17,13 +17,17 @@ const EVENT_NAMES = {
FOOTER_LINK: 'edx.bi.footer.link', FOOTER_LINK: 'edx.bi.footer.link',
}; };
class SiteFooter extends React.Component { const SiteFooter = ({
constructor(props) { supportedLanguages,
super(props); onLanguageSelected,
this.externalLinkClickHandler = this.externalLinkClickHandler.bind(this); logo,
} }) => {
const intl = useIntl();
const { config } = useContext(AppContext);
externalLinkClickHandler(event) { const showLanguageSelector = supportedLanguages.length > 0 && onLanguageSelected;
const externalLinkClickHandler = (event) => {
const label = event.currentTarget.getAttribute('href'); const label = event.currentTarget.getAttribute('href');
const eventName = EVENT_NAMES.FOOTER_LINK; const eventName = EVENT_NAMES.FOOTER_LINK;
const properties = { const properties = {
@@ -31,17 +35,7 @@ class SiteFooter extends React.Component {
label, label,
}; };
sendTrackEvent(eventName, properties); sendTrackEvent(eventName, properties);
} };
render() {
const {
supportedLanguages,
onLanguageSelected,
logo,
intl,
} = this.props;
const showLanguageSelector = supportedLanguages.length > 0 && onLanguageSelected;
const { config } = this.context;
return ( return (
<footer <footer
@@ -53,6 +47,7 @@ class SiteFooter extends React.Component {
className="d-block" className="d-block"
href={config.LMS_BASE_URL} href={config.LMS_BASE_URL}
aria-label={intl.formatMessage(messages['footer.logo.ariaLabel'])} aria-label={intl.formatMessage(messages['footer.logo.ariaLabel'])}
onClick={externalLinkClickHandler}
> >
<img <img
style={{ maxHeight: 45 }} style={{ maxHeight: 45 }}
@@ -70,13 +65,9 @@ class SiteFooter extends React.Component {
</div> </div>
</footer> </footer>
); );
} };
}
SiteFooter.contextType = AppContext;
SiteFooter.propTypes = { SiteFooter.propTypes = {
intl: intlShape.isRequired,
logo: PropTypes.string, logo: PropTypes.string,
onLanguageSelected: PropTypes.func, onLanguageSelected: PropTypes.func,
supportedLanguages: PropTypes.arrayOf(PropTypes.shape({ supportedLanguages: PropTypes.arrayOf(PropTypes.shape({
@@ -91,5 +82,5 @@ SiteFooter.defaultProps = {
supportedLanguages: [], supportedLanguages: [],
}; };
export default injectIntl(SiteFooter); export default SiteFooter;
export { EVENT_NAMES }; export { EVENT_NAMES };

View File

@@ -1,11 +1,14 @@
/* eslint-disable react/prop-types */ /* eslint-disable react/prop-types */
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import renderer from 'react-test-renderer'; import renderer from 'react-test-renderer';
import { render, fireEvent, screen } from '@testing-library/react'; import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { IntlProvider } from '@edx/frontend-platform/i18n'; import { IntlProvider } from '@edx/frontend-platform/i18n';
import { AppContext } from '@edx/frontend-platform/react'; import { AppContext } from '@edx/frontend-platform/react';
import Footer from './Footer'; import Footer from './Footer';
import FooterSlot from '../plugin-slots/FooterSlot';
import StudioFooterHelpSectionSlot from '../plugin-slots/StudioFooterHelpSectionSlot';
const FooterWithContext = ({ locale = 'es' }) => { const FooterWithContext = ({ locale = 'es' }) => {
const contextValue = useMemo(() => ({ const contextValue = useMemo(() => ({
@@ -21,7 +24,7 @@ const FooterWithContext = ({ locale = 'es' }) => {
<AppContext.Provider <AppContext.Provider
value={contextValue} value={contextValue}
> >
<Footer /> <FooterSlot />
</AppContext.Provider> </AppContext.Provider>
</IntlProvider> </IntlProvider>
); );
@@ -76,21 +79,44 @@ describe('<Footer />', () => {
}); });
describe('handles language switching', () => { describe('handles language switching', () => {
it('calls onLanguageSelected prop when a language is changed', () => { it('calls onLanguageSelected prop when a language is changed', async () => {
const user = userEvent.setup();
const mockHandleLanguageSelected = jest.fn(); const mockHandleLanguageSelected = jest.fn();
render(<FooterWithLanguageSelector languageSelected={mockHandleLanguageSelected} />); render(<FooterWithLanguageSelector languageSelected={mockHandleLanguageSelected} />);
fireEvent.submit(screen.getByTestId('site-footer-submit-btn'), { await user.selectOptions(screen.getByRole('combobox'), 'es');
target: { await user.click(screen.getByTestId('site-footer-submit-btn'));
elements: {
'site-footer-language-select': {
value: 'es',
},
},
},
});
expect(mockHandleLanguageSelected).toHaveBeenCalledWith('es'); expect(mockHandleLanguageSelected).toHaveBeenCalledWith('es');
}); });
}); });
}); });
describe('<StudioFooterHelpSectionSlot />', () => {
const SectionWithContext = ({ locale = 'es' }) => {
const contextValue = useMemo(() => ({
authenticatedUser: null,
config: {
LOGO_TRADEMARK_URL: process.env.LOGO_TRADEMARK_URL,
LMS_BASE_URL: process.env.LMS_BASE_URL,
},
}), []);
return (
<IntlProvider locale={locale}>
<AppContext.Provider
value={contextValue}
>
<StudioFooterHelpSectionSlot />
</AppContext.Provider>
</IntlProvider>
);
};
it('renders correctly', () => {
const tree = renderer
.create(<SectionWithContext />)
.toJSON();
expect(tree).toMatchSnapshot();
});
});

View File

@@ -1,10 +1,11 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n'; import { useIntl, FormattedMessage } from '@edx/frontend-platform/i18n';
const LanguageSelector = ({ const LanguageSelector = ({
intl, options, onSubmit, ...props options, onSubmit, ...props
}) => { }) => {
const intl = useIntl();
const handleSubmit = (e) => { const handleSubmit = (e) => {
e.preventDefault(); e.preventDefault();
const languageCode = e.target.elements['site-footer-language-select'].value; const languageCode = e.target.elements['site-footer-language-select'].value;
@@ -47,7 +48,6 @@ const LanguageSelector = ({
}; };
LanguageSelector.propTypes = { LanguageSelector.propTypes = {
intl: intlShape.isRequired,
onSubmit: PropTypes.func.isRequired, onSubmit: PropTypes.func.isRequired,
options: PropTypes.arrayOf(PropTypes.shape({ options: PropTypes.arrayOf(PropTypes.shape({
value: PropTypes.string, value: PropTypes.string,
@@ -55,4 +55,4 @@ LanguageSelector.propTypes = {
})).isRequired, })).isRequired,
}; };
export default injectIntl(LanguageSelector); export default LanguageSelector;

View File

@@ -12,12 +12,13 @@ exports[`<Footer /> renders correctly renders with a language selector 1`] = `
aria-label="edX Home" aria-label="edX Home"
className="d-block" className="d-block"
href="http://localhost:18000" href="http://localhost:18000"
onClick={[Function]}
> >
<img <img
alt="Powered by Open edX" alt="Powered by Open edX"
src="https://edx-cdn.org/v3/default/logo-trademark.svg" src="https://edx-cdn.org/v3/default/logo-trademark.svg"
style={ style={
Object { {
"maxHeight": 45, "maxHeight": 45,
} }
} }
@@ -81,12 +82,13 @@ exports[`<Footer /> renders correctly renders without a language selector 1`] =
aria-label="edX Home" aria-label="edX Home"
className="d-block" className="d-block"
href="http://localhost:18000" href="http://localhost:18000"
onClick={[Function]}
> >
<img <img
alt="Powered by Open edX" alt="Powered by Open edX"
src="https://edx-cdn.org/v3/default/logo-trademark.svg" src="https://edx-cdn.org/v3/default/logo-trademark.svg"
style={ style={
Object { {
"maxHeight": 45, "maxHeight": 45,
} }
} }
@@ -111,12 +113,13 @@ exports[`<Footer /> renders correctly renders without a language selector in es
aria-label="edX Home" aria-label="edX Home"
className="d-block" className="d-block"
href="http://localhost:18000" href="http://localhost:18000"
onClick={[Function]}
> >
<img <img
alt="Powered by Open edX" alt="Powered by Open edX"
src="https://edx-cdn.org/v3/default/logo-trademark.svg" src="https://edx-cdn.org/v3/default/logo-trademark.svg"
style={ style={
Object { {
"maxHeight": 45, "maxHeight": 45,
} }
} }
@@ -128,3 +131,68 @@ exports[`<Footer /> renders correctly renders without a language selector in es
</div> </div>
</footer> </footer>
`; `;
exports[`<StudioFooterHelpSectionSlot /> renders correctly 1`] = `
[
<div
className="m-0 mt-6 row align-items-center justify-content-center"
>
<div
className="col border-top mr-2"
/>
<button
className="btn btn-outline-primary btn-sm"
data-testid="helpToggleButton"
disabled={false}
onClick={[Function]}
type="button"
>
<span
className="pgn__icon pgn__icon__sm btn-icon-before"
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2Zm1 17h-2v-2h2v2Zm2.07-7.75-.9.92C13.45 12.9 13 13.5 13 15h-2v-.5c0-1.1.45-2.1 1.17-2.83l1.24-1.26c.37-.36.59-.86.59-1.41 0-1.1-.9-2-2-2s-2 .9-2 2H8c0-2.21 1.79-4 4-4s4 1.79 4 4c0 .88-.36 1.68-.93 2.25Z"
fill="currentColor"
/>
</svg>
</span>
Looking for help with Studio?
<span
className="pgn__icon pgn__icon__sm btn-icon-after"
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M16.59 8.59 12 13.17 7.41 8.59 6 10l6 6 6-6-1.41-1.41Z"
fill="currentColor"
/>
</svg>
</span>
</button>
<div
className="col border-top ml-2"
/>
</div>,
<div
className="px-4 container-mw-xl container-fluid"
/>,
]
`;

View File

@@ -1,18 +1,19 @@
import React, { useContext, useState } from 'react'; import React, { useContext } from 'react';
import _ from 'lodash'; import isEmpty from 'lodash/isEmpty';
import { intlShape, injectIntl, FormattedMessage } from '@edx/frontend-platform/i18n'; import { useIntl, FormattedMessage } from '@edx/frontend-platform/i18n';
import { ensureConfig } from '@edx/frontend-platform'; import { ensureConfig } from '@edx/frontend-platform';
import { AppContext } from '@edx/frontend-platform/react'; import { AppContext } from '@edx/frontend-platform/react';
import { import {
ActionRow, ActionRow,
Button,
Container, Container,
Hyperlink, Hyperlink,
Image,
TransitionReplace,
} from '@openedx/paragon'; } from '@openedx/paragon';
import { ExpandLess, ExpandMore, Help } from '@openedx/paragon/icons'; import classNames from 'classnames';
import PropTypes from 'prop-types';
import messages from './messages'; import messages from './messages';
import StudioFooterLogoSlot from '../../plugin-slots/StudioFooterLogoSlot';
import StudioFooterHelpSectionSlot from '../../plugin-slots/StudioFooterHelpSectionSlot';
ensureConfig([ ensureConfig([
'LMS_BASE_URL', 'LMS_BASE_URL',
@@ -26,81 +27,29 @@ ensureConfig([
], 'Studio Footer component'); ], 'Studio Footer component');
const StudioFooter = ({ const StudioFooter = ({
// injected containerProps,
intl,
}) => { }) => {
const [isOpen, setIsOpen] = useState(false); const intl = useIntl();
const { config } = useContext(AppContext); const { config } = useContext(AppContext);
const { containerClassName, ...restContainerProps } = containerProps || {};
return ( return (
<> <>
<div className="m-0 mt-6 row align-items-center justify-content-center"> <StudioFooterHelpSectionSlot containerProps={containerProps} />
<div className="col border-top mr-2" /> <Container
<Button size="xl"
data-testid="helpToggleButton" className={classNames('px-4', containerClassName)}
variant="outline-primary" {...restContainerProps}
onClick={() => setIsOpen(!isOpen)}
iconBefore={Help}
iconAfter={isOpen ? ExpandLess : ExpandMore}
size="sm"
> >
{isOpen ? intl.formatMessage(messages.closeHelpButtonLabel)
: intl.formatMessage(messages.openHelpButtonLabel)}
</Button>
<div className="col border-top ml-2" />
</div>
<Container size="xl" className="px-4">
<TransitionReplace>
{isOpen ? (
<ActionRow key="help-link-button-row" className="py-4" data-testid="helpButtonRow">
<ActionRow.Spacer />
<Button as="a" href="https://docs.edx.org/" size="sm">
<FormattedMessage {...messages.edxDocumentationButtonLabel} />
</Button>
<Button
as="a"
href="https://open.edx.org/"
size="sm"
data-testid="openEdXPortalButton"
>
<FormattedMessage {...messages.openEdxPortalButtonLabel} />
</Button>
<Button
as="a"
href="https://www.edx.org/course/edx101-overview-of-creating-an-edx-course#.VO4eaLPF-n1"
size="sm"
>
<FormattedMessage {...messages.edx101ButtonLabel} />
</Button>
<Button
as="a"
href="https://www.edx.org/course/studiox-creating-a-course-with-edx-studio"
size="sm"
>
<FormattedMessage {...messages.studioXButtonLabel} />
</Button>
{!_.isEmpty(config.SUPPORT_EMAIL) && (
<Button
as="a"
href={`mailto:${config.SUPPORT_EMAIL}`}
size="sm"
data-testid="contactUsButton"
>
<FormattedMessage {...messages.contactUsButtonLabel} />
</Button>
)}
<ActionRow.Spacer />
</ActionRow>
) : null}
</TransitionReplace>
<ActionRow className="pt-3 m-0 x-small"> <ActionRow className="pt-3 m-0 x-small">
© {new Date().getFullYear()} <Hyperlink destination={config.MARKETING_SITE_BASE_URL} target="_blank" className="ml-2">{config.SITE_NAME}</Hyperlink> © {new Date().getFullYear()} <Hyperlink destination={config.MARKETING_SITE_BASE_URL} target="_blank" className="ml-2">{config.SITE_NAME}</Hyperlink>
<ActionRow.Spacer /> <ActionRow.Spacer />
{!_.isEmpty(config.TERMS_OF_SERVICE_URL) && ( {!isEmpty(config.TERMS_OF_SERVICE_URL) && (
<Hyperlink destination={config.TERMS_OF_SERVICE_URL} data-testid="termsOfService"> <Hyperlink destination={config.TERMS_OF_SERVICE_URL} data-testid="termsOfService">
{intl.formatMessage(messages.termsOfServiceLinkLabel)} {intl.formatMessage(messages.termsOfServiceLinkLabel)}
</Hyperlink> </Hyperlink>
)}{!_.isEmpty(config.PRIVACY_POLICY_URL) && ( )}{!isEmpty(config.PRIVACY_POLICY_URL) && (
<Hyperlink destination={config.PRIVACY_POLICY_URL} data-testid="privacyPolicy"> <Hyperlink destination={config.PRIVACY_POLICY_URL} data-testid="privacyPolicy">
{intl.formatMessage(messages.privacyPolicyLinkLabel)} {intl.formatMessage(messages.privacyPolicyLinkLabel)}
</Hyperlink> </Hyperlink>
@@ -125,13 +74,7 @@ const StudioFooter = ({
<FormattedMessage {...messages.trademarkMessage} /> <FormattedMessage {...messages.trademarkMessage} />
<Hyperlink className="ml-1" destination="https://www.edx.org">edX Inc</Hyperlink>. <Hyperlink className="ml-1" destination="https://www.edx.org">edX Inc</Hyperlink>.
<ActionRow.Spacer /> <ActionRow.Spacer />
<Hyperlink destination="https://open.edx.org" className="float-right"> <StudioFooterLogoSlot />
<Image
width="120px"
alt="Powered by Open edX"
src="https://logos.openedx.org/open-edx-logo-tag.png"
/>
</Hyperlink>
</ActionRow> </ActionRow>
</Container> </Container>
</> </>
@@ -139,8 +82,11 @@ const StudioFooter = ({
}; };
StudioFooter.propTypes = { StudioFooter.propTypes = {
// injected containerProps: PropTypes.shape(Container.propTypes),
intl: intlShape.isRequired,
}; };
export default injectIntl(StudioFooter); StudioFooter.defaultProps = {
containerProps: {},
};
export default StudioFooter;

View File

@@ -1,10 +1,12 @@
/* eslint-disable react/prop-types */ /* eslint-disable react/prop-types */
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { fireEvent, render, screen } from '@testing-library/react'; import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom/extend-expect'; import '@testing-library/jest-dom/extend-expect';
import '@testing-library/jest-dom';
import { IntlProvider } from '@edx/frontend-platform/i18n'; import { IntlProvider } from '@edx/frontend-platform/i18n';
import { AppContext } from '@edx/frontend-platform/react'; import { AppContext } from '@edx/frontend-platform/react';
import StudioFooter from './StudioFooter'; import StudioFooterSlot from '../../plugin-slots/StudioFooterSlot';
import messages from './messages'; import messages from './messages';
const config = { const config = {
@@ -35,7 +37,7 @@ const Component = ({ updateVariable }) => {
return ( return (
<IntlProvider locale="en"> <IntlProvider locale="en">
<AppContext.Provider value={contextValue}> <AppContext.Provider value={contextValue}>
<StudioFooter /> <StudioFooterSlot />
</AppContext.Provider> </AppContext.Provider>
</IntlProvider> </IntlProvider>
); );
@@ -56,35 +58,40 @@ describe('Footer', () => {
}); });
}); });
describe('help section expanded view', () => { describe('help section expanded view', () => {
it('help button should read Hide Studio help', () => { it('help button should read Hide Studio help', async () => {
const user = userEvent.setup();
render(<Component />); render(<Component />);
const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage); const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage);
fireEvent.click(helpToggleButton); await user.click(helpToggleButton);
expect(screen.getByText(messages.closeHelpButtonLabel.defaultMessage)) expect(screen.getByText(messages.closeHelpButtonLabel.defaultMessage))
.toBeVisible(); .toBeVisible();
}); });
it('help button link row should be visible', () => { it('help button link row should be visible', async () => {
const user = userEvent.setup();
render(<Component />); render(<Component />);
const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage); const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage);
fireEvent.click(helpToggleButton); await user.click(helpToggleButton);
expect(screen.getByTestId('helpButtonRow')).toBeVisible(); expect(screen.getByTestId('helpButtonRow')).toBeVisible();
}); });
it('Open edX portal button should be visible', () => { it('Open edX portal button should be visible', async () => {
const user = userEvent.setup();
render(<Component />); render(<Component />);
const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage); const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage);
fireEvent.click(helpToggleButton); await user.click(helpToggleButton);
expect(screen.getByTestId('openEdXPortalButton')).toBeVisible(); expect(screen.getByTestId('openEdXDemoCourseButton')).toBeVisible();
}); });
it('should not show contact us button', () => { it('should not show contact us button', async () => {
const user = userEvent.setup();
render(<Component />); render(<Component />);
const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage); const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage);
fireEvent.click(helpToggleButton); await user.click(helpToggleButton);
expect(screen.queryByTestId('contactUsButton')).toBeNull(); expect(screen.queryByTestId('contactUsButton')).toBeNull();
}); });
it('should show contact us button', () => { it('should show contact us button', async () => {
const user = userEvent.setup();
render(<Component updateVariable={['SUPPORT_EMAIL', 'support@email.com']} />); render(<Component updateVariable={['SUPPORT_EMAIL', 'support@email.com']} />);
const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage); const helpToggleButton = screen.getByText(messages.openHelpButtonLabel.defaultMessage);
fireEvent.click(helpToggleButton); await user.click(helpToggleButton);
expect(screen.getByTestId('contactUsButton')).toBeVisible(); expect(screen.getByTestId('contactUsButton')).toBeVisible();
}); });
}); });

View File

@@ -0,0 +1,32 @@
import React from 'react';
import { useIntl } from '@edx/frontend-platform/i18n';
import {
Button,
} from '@openedx/paragon';
import { ExpandLess, ExpandMore, Help } from '@openedx/paragon/icons';
import PropTypes from 'prop-types';
import messages from '../messages';
const HelpButton = ({ isOpen, setIsOpen }) => {
const intl = useIntl();
return (
<Button
data-testid="helpToggleButton"
variant="outline-primary"
onClick={() => setIsOpen(!isOpen)}
iconBefore={Help}
iconAfter={isOpen ? ExpandLess : ExpandMore}
size="sm"
>
{isOpen ? intl.formatMessage(messages.closeHelpButtonLabel)
: intl.formatMessage(messages.openHelpButtonLabel)}
</Button>
);
};
HelpButton.propTypes = {
setIsOpen: PropTypes.func.isRequired,
isOpen: PropTypes.bool.isRequired,
};
export default HelpButton;

View File

@@ -0,0 +1,55 @@
import React, { useMemo } from 'react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { AppContext } from '@edx/frontend-platform/react';
import HelpButton from './HelpButton';
import '@testing-library/jest-dom';
// eslint-disable-next-line react/prop-types
const ButtonWithContext = ({ locale = 'en', isOpen, setIsOpen }) => {
const contextValue = useMemo(() => ({
authenticatedUser: null,
config: { },
}), []);
return (
<IntlProvider locale={locale}>
<AppContext.Provider
value={contextValue}
>
<HelpButton isOpen={isOpen} setIsOpen={setIsOpen} />
</AppContext.Provider>
</IntlProvider>
);
};
describe('HelpButton', () => {
const mockSetIsOpen = jest.fn();
beforeEach(() => {
mockSetIsOpen.mockClear();
});
it('renders with "open" label when isOpen is false', () => {
render(<ButtonWithContext isOpen={false} setIsOpen={mockSetIsOpen} />);
expect(screen.getByTestId('helpToggleButton')).toBeInTheDocument();
expect(screen.getByRole('button')).toHaveTextContent(/help|open/i);
});
it('renders with "close" label when isOpen is true', () => {
render(<ButtonWithContext isOpen setIsOpen={mockSetIsOpen} />);
expect(screen.getByTestId('helpToggleButton')).toBeInTheDocument();
expect(screen.getByRole('button')).toHaveTextContent(/close|help/i);
});
it('calls setIsOpen with the toggled value when clicked', async () => {
const user = userEvent.setup();
const { rerender } = render(<ButtonWithContext isOpen={false} setIsOpen={mockSetIsOpen} />);
await user.click(screen.getByTestId('helpToggleButton'));
expect(mockSetIsOpen).toHaveBeenCalledWith(true);
rerender(<ButtonWithContext isOpen setIsOpen={mockSetIsOpen} />);
await user.click(screen.getByTestId('helpToggleButton'));
expect(mockSetIsOpen).toHaveBeenCalledWith(false);
});
});

View File

@@ -0,0 +1,62 @@
import React, { useContext } from 'react';
import { AppContext } from '@edx/frontend-platform/react';
import isEmpty from 'lodash/isEmpty';
import { ensureConfig } from '@edx/frontend-platform';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import {
ActionRow,
Button,
} from '@openedx/paragon';
import messages from '../messages';
ensureConfig([
'SUPPORT_EMAIL',
], 'Studio Footer Help Content component');
const BUTTONS = [
{
as: 'a',
href: 'https://docs.openedx.org/en/latest/educators/quickstarts/build_a_course.html',
size: 'sm',
message: messages.educatorsDocsButtonLabel,
dataTestid: null,
},
{
as: 'a',
href: 'https://training.openedx.io/courses/course-v1:OpenedX+DemoX+Demo_Course/about',
size: 'sm',
message: messages.openEdxDemoCourseButtonLabel,
dataTestid: 'openEdXDemoCourseButton',
},
];
const HelpContent = () => {
const { config } = useContext(AppContext);
return (
<ActionRow key="help-link-button-row" className="py-4" data-testid="helpButtonRow">
<ActionRow.Spacer />
{BUTTONS.map(({
as, href, size, message, dataTestid,
}) => (
<Button as={as} href={href} size={size} key={message.id} data-testid={dataTestid}>
<FormattedMessage {...message} />
</Button>
))}
{!isEmpty(config.SUPPORT_EMAIL) && (
<Button
as="a"
href={`mailto:${config.SUPPORT_EMAIL}`}
size="sm"
data-testid="contactUsButton"
>
<FormattedMessage {...messages.contactUsButtonLabel} />
</Button>
)}
<ActionRow.Spacer />
</ActionRow>
);
};
export default HelpContent;

View File

@@ -0,0 +1,44 @@
import React, { useMemo } from 'react';
import { render, screen } from '@testing-library/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { AppContext } from '@edx/frontend-platform/react';
import HelpContent from './HelpContent';
import '@testing-library/jest-dom';
import messages from '../messages';
// eslint-disable-next-line react/prop-types
const ContentWithContext = ({ locale = 'en', config = {} }) => {
const contextValue = useMemo(() => ({
authenticatedUser: null,
config,
}), [config]);
return (
<IntlProvider locale={locale}>
<AppContext.Provider
value={contextValue}
>
<HelpContent />
</AppContext.Provider>
</IntlProvider>
);
};
describe('HelpContent', () => {
it('renders all help buttons', () => {
const config = { SUPPORT_EMAIL: 'support@example.com' };
render(<ContentWithContext config={config} />);
expect(screen.getByText(messages.educatorsDocsButtonLabel.defaultMessage)).toBeInTheDocument();
expect(screen.getByText(messages.openEdxDemoCourseButtonLabel.defaultMessage)).toBeInTheDocument();
});
it('does not render contact button if SUPPORT_EMAIL is empty', () => {
render(<ContentWithContext config={{ SUPPORT_EMAIL: null }} />);
expect(screen.queryByTestId('contactUsButton')).not.toBeInTheDocument();
});
it('renders ActionRow with correct test id', () => {
render(<ContentWithContext />);
expect(screen.getByTestId('helpButtonRow')).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,43 @@
import React from 'react';
import {
Container,
TransitionReplace,
} from '@openedx/paragon';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import StudioFooterHelpButtonSlot from '../../../plugin-slots/StudioFooterHelpButtonSlot';
import StudioFooterHelpContentlot from '../../../plugin-slots/StudioFooterHelpContentSlot';
const HelpSection = ({ containerProps }) => {
const [isOpen, setIsOpen] = React.useState(false);
const { containerClassName, ...restContainerProps } = containerProps || {};
return (
<>
<div className="m-0 mt-6 row align-items-center justify-content-center">
<div className="col border-top mr-2" />
<StudioFooterHelpButtonSlot
setIsOpen={setIsOpen}
isOpen={isOpen}
/>
<div className="col border-top ml-2" />
</div>
<Container
size="xl"
className={classNames('px-4', containerClassName)}
{...restContainerProps}
>
{isOpen && <TransitionReplace><StudioFooterHelpContentlot /></TransitionReplace> }
</Container>
</>
);
};
HelpSection.propTypes = {
containerProps: PropTypes.shape(Container.propTypes),
};
HelpSection.defaultProps = {
containerProps: {},
};
export default HelpSection;

View File

@@ -0,0 +1,52 @@
import React, { useMemo } from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { AppContext } from '@edx/frontend-platform/react';
import HelpSection from './HelpSection';
import '@testing-library/jest-dom';
// eslint-disable-next-line react/prop-types
const HelpSectionWithContext = ({ locale = 'en', config = {}, containerProps = null }) => {
const contextValue = useMemo(() => ({
authenticatedUser: null,
config,
}), [config]);
return (
<IntlProvider locale={locale}>
<AppContext.Provider
value={contextValue}
>
<HelpSection containerProps={containerProps} />
</AppContext.Provider>
</IntlProvider>
);
};
describe('HelpSection', () => {
it('renders the HelpButton', () => {
render(<HelpSectionWithContext />);
expect(screen.getByTestId('helpToggleButton')).toBeInTheDocument();
});
it('does not show HelpContent by default', () => {
render(<HelpSectionWithContext />);
expect(screen.queryByTestId('helpButtonRow')).not.toBeInTheDocument();
});
it('shows HelpContent when HelpButton is clicked', () => {
render(<HelpSectionWithContext />);
const button = screen.getByTestId('helpToggleButton');
fireEvent.click(button);
expect(screen.getByTestId('helpButtonRow')).toBeInTheDocument();
});
it('hides HelpContent when HelpButton is clicked twice', () => {
render(<HelpSectionWithContext containerProps={{ containerClassName: 'container', size: 'xl' }} />);
const button = screen.getByTestId('helpToggleButton');
fireEvent.click(button);
expect(screen.queryByTestId('helpButtonRow')).toBeInTheDocument();
fireEvent.click(button);
expect(screen.queryByTestId('helpButtonRow')).not.toBeInTheDocument();
});
});

View File

@@ -11,25 +11,15 @@ const messages = defineMessages({
defaultMessage: 'Hide Studio help', defaultMessage: 'Hide Studio help',
description: 'Label for button that closes the collapsed section with help buttons', description: 'Label for button that closes the collapsed section with help buttons',
}, },
edxDocumentationButtonLabel: { educatorsDocsButtonLabel: {
id: 'authoring.footer.help.edxDocumentation.button.label', id: 'authoring.footer.help.educatorsDocs.button.label',
defaultMessage: 'edX documentation', defaultMessage: 'Open edX Educators Docs',
description: 'Label for button that links to the edX documentation site', description: 'Label for button that links to the build a course quickstart site',
}, },
openEdxPortalButtonLabel: { openEdxDemoCourseButtonLabel: {
id: 'authoring.footer.help.openEdxPortal.button.label', id: 'authoring.footer.help.openEdxDemoCourse.button.label',
defaultMessage: 'Open edX portal', defaultMessage: 'Open edX Demo Course',
description: 'Label for button that links to the Open edX portal', description: 'Label for button that links to the open edX demo course portal',
},
edx101ButtonLabel: {
id: 'authoring.footer.help.edx101.button.label',
defaultMessage: 'Enroll in edX 101',
description: 'Label for button that links to the edX 101 course',
},
studioXButtonLabel: {
id: 'authoring.footer.help.studioX.button.label',
defaultMessage: 'Enroll in StudioX',
description: 'Label for button that links to the edX StudioX course',
}, },
contactUsButtonLabel: { contactUsButtonLabel: {
id: 'authoring.footer.help.contactUs.button.label', id: 'authoring.footer.help.contactUs.button.label',

View File

@@ -1,34 +1 @@
import arMessages from './messages/ar.json'; export default {};
import frMessages from './messages/fr.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';
import dedeCAMessages from './messages/de_DE.json';
import ititCAMessages from './messages/it_IT.json';
import ptptCAMessages from './messages/pt_PT.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,
pt: ptMessages,
it: itMessages,
de: deMessages,
hi: hiMessages,
'fr-ca': frCAMessages,
ru: ruMessages,
uk: ukMessages,
'de-de': dedeCAMessages,
'it-it': ititCAMessages,
'pt-pt': ptptCAMessages,
};
export default messages;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,10 @@
import Footer, { EVENT_NAMES } from './components/Footer'; import Footer, { EVENT_NAMES } from './components/Footer';
import messages from './i18n/index'; import messages from './i18n/index';
import StudioFooter from './components/studio-footer'; import StudioFooter from './components/studio-footer';
import FooterSlot from './plugin-slots/FooterSlot';
import StudioFooterSlot from './plugin-slots/StudioFooterSlot';
export default Footer; export default Footer;
export { messages, EVENT_NAMES, StudioFooter }; export {
messages, EVENT_NAMES, StudioFooter, FooterSlot, StudioFooterSlot,
};

View File

@@ -0,0 +1,51 @@
# FooterSlot
### Slot ID: `org.openedx.frontend.layout.footer.v1`
### Slot ID Aliases
* `footer_slot`
## Description
This slot is used to repace/modify/hide the entire footer.
## Example
The following `env.config.jsx` will replace the default footer.
![Screenshot of default footer](./images/default_footer.png)
with a simple custom footer
![Screenshot of custom footer](./images/custom_footer.png)
```jsx
import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
const config = {
pluginSlots: {
'org.openedx.frontend.layout.footer.v1': {
plugins: [
{
// Hide the default footer
op: PLUGIN_OPERATIONS.Hide,
widgetId: 'default_contents',
},
{
// Insert a custom footer
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'custom_footer',
type: DIRECT_PLUGIN,
RenderWidget: () => (
<h1 style={{textAlign: 'center'}}>🦶</h1>
),
},
},
]
}
},
}
export default config;
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@@ -0,0 +1,14 @@
import React from 'react';
import { PluginSlot } from '@openedx/frontend-plugin-framework';
import Footer from '../../components/Footer';
const FooterSlot = () => (
<PluginSlot
id="org.openedx.frontend.layout.footer.v1"
idAliases={['footer_slot', 'footer_plugin_slot']}
>
<Footer />
</PluginSlot>
);
export default FooterSlot;

View File

@@ -0,0 +1,5 @@
# `frontend-component-footer` Plugin Slots
* [`org.openedx.frontend.layout.footer.v1`](./FooterSlot/)
* [`org.openedx.frontend.layout.studio_footer.v1`](./StudioFooterSlot/)
* [`org.openedx.frontend.layout.studio_footer_logo.v1`](./StudioFooterLogoSlot/)

View File

@@ -0,0 +1,47 @@
# StudioFooterLogoSlot
### Slot ID: `org.openedx.frontend.layout.studio_footer_help_button.v1`
## Description
This slot is used to repace/modify/hide the help button to the studio footer.
## Examples
### Add your custom button.
The following `env.config.jsx` will add a custom help button to the studio footer.
![Screenshot of modified help button](./images/custom_help_button.png)
```jsx
import { PLUGIN_OPERATIONS, DIRECT_PLUGIN } from '@openedx/frontend-plugin-framework';
import {
Button,
} from '@openedx/paragon';
const config = {
pluginSlots: {
'org.openedx.frontend.layout.studio_footer_help_button.v1': {
keepDefault: false,
plugins: [
{
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'studio_footer_helpbutton_addition',
type: DIRECT_PLUGIN,
priority: 40,
RenderWidget: ({isOpen, setIsOpen}) => {
return (
<Button className="button button1" onClick={() => setIsOpen(!isOpen)}>Custom Help Button</Button>
)
}
}
},
],
}
},
};
export default config;
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

View File

@@ -0,0 +1,17 @@
import React from 'react';
import { PluginSlot } from '@openedx/frontend-plugin-framework';
import PropTypes from 'prop-types';
import HelpButton from '../../components/studio-footer/help-components/HelpButton';
const StudioFooterHelpButtonSlot = ({ isOpen, setIsOpen }) => (
<PluginSlot id="org.openedx.frontend.layout.studio_footer_help_button.v1" pluginProps={{ isOpen, setIsOpen }}>
<HelpButton isOpen={isOpen} setIsOpen={setIsOpen} />
</PluginSlot>
);
StudioFooterHelpButtonSlot.propTypes = {
setIsOpen: PropTypes.func.isRequired,
isOpen: PropTypes.bool.isRequired,
};
export default StudioFooterHelpButtonSlot;

View File

@@ -0,0 +1,50 @@
# StudioFooterLogoSlot
### Slot ID: `org.openedx.frontend.layout.studio_footer_help-content.v1`
## Description
This slot is used to repace/modify/hide the help content to the studio footer.
## Examples
### Add a custom help content.
The following `env.config.jsx` will add a custom help content to the studio footer.
![Screenshot of modified help content](./images/custom_help_content.png)
```jsx
import { PLUGIN_OPERATIONS, DIRECT_PLUGIN } from '@openedx/frontend-plugin-framework';
import {
Hyperlink,
} from '@openedx/paragon';
const config = {
pluginSlots: {
'org.openedx.frontend.layout.studio_footer_help-content.v1': {
keepDefault: false,
plugins: [
{
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'studio_footer_helpcontent_addition',
type: DIRECT_PLUGIN,
priority: 40,
RenderWidget: () => {
return (
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', padding: '20px 5px' }}>
<p>Custom Help content</p>
<Hyperlink destination="https://example.com/">more help</Hyperlink>
</div>
)
}
}
},
],
}
},
};
export default config;
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

View File

@@ -0,0 +1,13 @@
import React from 'react';
import { PluginSlot } from '@openedx/frontend-plugin-framework';
import HelpContent from '../../components/studio-footer/help-components/HelpContent';
const StudioFooterHelpContentSlot = () => (
<PluginSlot
id="org.openedx.frontend.layout.studio_footer_help-content.v1"
>
<HelpContent />
</PluginSlot>
);
export default StudioFooterHelpContentSlot;

View File

@@ -0,0 +1,53 @@
# StudioFooterLogoSlot
### Slot ID: `org.openedx.frontend.layout.studio_footer_help_section.v1`
## Description
This slot is used to repace/modify/hide the help section to the studio footer.
![Screenshot of modified help section](./images/custom_help_section.png)
## Examples
### Customize help section.
The following `env.config.jsx` will add a custom help section to the studio footer.
```jsx
import { PLUGIN_OPERATIONS, DIRECT_PLUGIN } from '@openedx/frontend-plugin-framework';
import {
Hyperlink,
Button,
} from '@openedx/paragon';
const config = {
pluginSlots: {
'org.openedx.frontend.layout.studio_footer_help_section.v1': {
keepDefault: false,
plugins: [
{
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'studio_footer_helpsection_addition',
type: DIRECT_PLUGIN,
priority: 40,
RenderWidget: () => {
return (
<div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', padding: '20px 5px' }}>
<p>Custom Help Section custizable as needed </p>
<Button className="button button1">Go to home</Button>
<Hyperlink destination="https://example.com/">About</Hyperlink>
<Hyperlink destination="https://example.com/">Info</Hyperlink>
</div>
)
}
}
},
],
}
},
};
export default config;
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

View File

@@ -0,0 +1,19 @@
import React from 'react';
import { PluginSlot } from '@openedx/frontend-plugin-framework';
import PropTypes from 'prop-types';
import {
Container,
} from '@openedx/paragon';
import HelpSection from '../../components/studio-footer/help-components/HelpSection';
const StudioFooterHelpSectionSlot = ({ containerProps }) => (
<PluginSlot id="org.openedx.frontend.layout.studio_footer_help_section.v1">
<HelpSection containerProps={containerProps} />
</PluginSlot>
);
StudioFooterHelpSectionSlot.propTypes = {
containerProps: PropTypes.shape(Container.propTypes),
};
export default StudioFooterHelpSectionSlot;

View File

@@ -0,0 +1,57 @@
# StudioFooterLogoSlot
### Slot ID: `org.openedx.frontend.layout.studio_footer_logo.v1`
### Slot ID Aliases
* `studio_footer_logo_slot`
## Description
This slot is used to add your site logo to the studio footer.
## Examples
### Add your site logo.
The following `env.config.jsx` will add your site logo to the studio footer.
![Screenshot of modified Studio Footer Logo](./images/add_your_site_logo.png)
```jsx
import { PLUGIN_OPERATIONS, DIRECT_PLUGIN } from '@openedx/frontend-plugin-framework';
import {
Hyperlink,
Image,
} from '@openedx/paragon';
const config = {
pluginSlots: {
'org.openedx.frontend.layout.studio_footer_logo.v1': {
keepDefault: true,
plugins: [
{
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'studio_footer_logo_addition',
type: DIRECT_PLUGIN,
priority: 40,
RenderWidget: () => {
return (
<Hyperlink destination="https://example.com/" className="float-right">
<Image
height="48px"
alt="Hosted by MySite"
src="https://logos.openedx.org/generic-logo.svg"
/>
</Hyperlink>
)
}
}
},
],
}
},
};
export default config;
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 285 KiB

View File

@@ -0,0 +1,17 @@
import React from 'react';
import { PluginSlot } from '@openedx/frontend-plugin-framework';
import { Hyperlink, Image } from '@openedx/paragon';
const StudioFooterLogoSlot = () => (
<PluginSlot id="org.openedx.frontend.layout.studio_footer_logo.v1" idAliases={['studio_footer_logo_slot']}>
<Hyperlink destination="https://openedx.org" className="float-right">
<Image
width="120px"
alt="Powered by Open edX"
src="https://logos.openedx.org/open-edx-logo-tag.png"
/>
</Hyperlink>
</PluginSlot>
);
export default StudioFooterLogoSlot;

View File

@@ -0,0 +1,51 @@
# StudioFooterSlot
### Slot ID: `org.openedx.frontend.layout.studio_footer.v1`
### Slot ID Aliases
* `studio_footer_slot`
## Description
This slot is used to repace/modify/hide the entire studio footer.
## Example
The following `env.config.jsx` will replace the default studio footer.
![Screenshot of default studio footer](./images/default_studio_footer.png)
with a simple custom footer
![Screenshot of custom footer](./images/custom_footer.png)
```jsx
import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
const config = {
pluginSlots: {
'org.openedx.frontend.layout.studio_footer.v1': {
plugins: [
{
// Hide the default footer
op: PLUGIN_OPERATIONS.Hide,
widgetId: 'default_contents',
},
{
// Insert a custom footer
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'custom_footer',
type: DIRECT_PLUGIN,
RenderWidget: () => (
<h1 style={{textAlign: 'center'}}>🦶</h1>
),
},
},
]
}
},
}
export default config;
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

View File

@@ -0,0 +1,11 @@
import React from 'react';
import { PluginSlot } from '@openedx/frontend-plugin-framework';
import StudioFooter from '../../components/studio-footer';
const StudioFooterSlot = () => (
<PluginSlot id="org.openedx.frontend.layout.studio_footer.v1" idAliases={['studio_footer_slot']}>
<StudioFooter />
</PluginSlot>
);
export default StudioFooterSlot;

View File

@@ -2,7 +2,9 @@ const path = require('path');
const { createConfig } = require('@openedx/frontend-build'); const { createConfig } = require('@openedx/frontend-build');
module.exports = createConfig('webpack-dev', { module.exports = createConfig('webpack-dev', {
entry: path.resolve(__dirname, 'example'), entry: {
app: path.resolve(__dirname, 'example'),
},
output: { output: {
path: path.resolve(__dirname, 'example/dist'), path: path.resolve(__dirname, 'example/dist'),
publicPath: '/', publicPath: '/',