Compare commits

..

1170 Commits

Author SHA1 Message Date
edX requirements bot
a1c35f134c chore: update browserslist DB (#1421)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2026-03-16 00:53:02 +00:00
renovate[bot]
41af76f027 fix(deps): update dependency qs to v6.15.0 (#1418)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-03-09 04:52:00 +00:00
edX requirements bot
57a3b0963c chore: update browserslist DB (#1417)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2026-03-09 00:47:58 +00:00
Emad Rad
92dcdd0a98 fix: update button labels in ConfirmationModal for better localization
Close #1415
2026-03-03 15:23:57 -03:00
edX requirements bot
29f0cefc1e chore: update browserslist DB (#1414)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2026-03-02 00:50:06 +00:00
renovate[bot]
794a1d57b9 fix(deps): update dependency bowser to v2.14.1 (#1411)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-23 22:37:50 +00:00
edX requirements bot
fe46e8a1a6 chore: update browserslist DB (#1413)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2026-02-23 21:07:21 +00:00
renovate[bot]
a525d3c22e fix(deps): update dependency core-js to v3.48.0 (#1412)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-23 21:00:37 +00:00
renovate[bot]
81a2c3c0d2 chore(deps): update dependency @edx/frontend-platform to v8.5.5 (#1410)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-16 05:49:56 +00:00
renovate[bot]
0018eafdcc chore(deps): update dependency @edx/browserslist-config to v1.5.1 (#1409)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-16 05:49:31 +00:00
renovate[bot]
e7db2ef753 fix(deps): update dependency qs to v6.14.2 [security] (#1408)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-02-16 01:12:05 +00:00
edX requirements bot
2e986d9b74 chore: update browserslist DB (#1407)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2026-02-16 00:59:58 +00:00
Brian Smith
7c85195a27 fix(deps): regenerate package-lock.json (#1405)
* fix(deps): regenerate package-lock.json

Co-Authored-By: Claude Code <noreply@anthropic.com>

* test: update snapshots

Co-Authored-By: Claude Code <noreply@anthropic.com>

---------

Co-authored-by: Claude Code <noreply@anthropic.com>
2026-02-12 09:41:39 -05:00
edX requirements bot
b686acf5f5 chore: update browserslist DB (#1406)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2026-02-09 00:48:51 +00:00
Anton Melser
166deeafbd docs: Generify currently supported node version 2026-01-27 09:57:11 -03:00
renovate[bot]
f33fe7d0e5 chore(deps): update dependency @edx/frontend-platform to v8.5.4 (#1404)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-26 08:50:30 +00:00
edX requirements bot
e2896dbf94 chore: update browserslist DB (#1403)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2026-01-26 00:42:27 +00:00
renovate[bot]
f07d266a43 chore(deps): update react-router monorepo to v6.30.3 (#1402)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-19 06:45:57 +00:00
edX requirements bot
69cdc5f191 chore: update browserslist DB (#1392)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2026-01-19 00:41:31 +00:00
Awais Ansari
4f51f71acc feat: implemented notifications configurations V3 API (#1401)
* feat: implemented notifications configurations V3 API

* fix: removed default daily email cadence when email toggle is turned on
2026-01-15 18:56:02 +05:00
renovate[bot]
c70eca1fde fix(deps): update dependency qs to v6.14.1 [security] (#1400)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-13 20:21:35 +00:00
Diana Villalvazo
39dc5bbbd2 docs: update owner/maintainer (#1398) 2026-01-13 13:27:29 -06:00
renovate[bot]
7fa61f3714 fix(deps): update dependency bowser to v2.13.1 (#1393)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-01-13 18:43:37 +00:00
Diana Villalvazo
8ce7c1599d test: fix X/twitter broken tests (#1399) 2026-01-13 13:39:44 -05:00
Stanislav
03bdcff331 feat: Change Twitter to X (#1215) 2025-12-02 21:46:31 +05:00
Awais Ansari
d73d840e93 feat: added env parse to boolean functionality (#1389) 2025-12-01 19:39:53 +05:00
edX requirements bot
d23b5f53df chore: update browserslist DB (#1388)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-12-01 00:44:00 +00:00
Awais Ansari
da98bfa021 refactor: simplify notifications channels flag logic (#1381) 2025-11-24 16:20:58 +05:00
renovate[bot]
c37640aa69 fix(deps): update dependency core-js to v3.47.0 (#1384)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-24 05:10:14 +00:00
renovate[bot]
ea35227389 fix(deps): update dependency bowser to v2.13.0 (#1383)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-24 05:10:00 +00:00
edX requirements bot
c35bf95c1c chore: update browserslist DB (#1382)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-11-24 00:39:08 +00:00
renovate[bot]
bd26928154 chore(deps): update react-router monorepo to v6.30.2 (#1379)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-17 05:47:51 +00:00
edX requirements bot
cdc8efe17b chore: update browserslist DB (#1378)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-11-17 00:37:06 +00:00
edX requirements bot
8b6535ea58 chore: update browserslist DB (#1372)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-11-10 00:37:40 +00:00
renovate[bot]
4c9498971a fix(deps): update dependency redux-saga to v1.4.2 (#1370)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-11-03 06:43:39 +00:00
edX requirements bot
a6b6a3f940 chore: update browserslist DB (#1369)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-11-03 00:37:33 +00:00
renovate[bot]
cab1a24e10 chore(deps): update dependency @edx/frontend-platform to v8.5.2 (#1367)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-27 06:51:56 +00:00
renovate[bot]
f6b7782d24 chore(deps): update dependency @edx/frontend-component-footer to v14.9.3 (#1366)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-27 06:51:47 +00:00
edX requirements bot
c7bbe8d0d1 chore: update browserslist DB (#1363)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-10-20 00:37:12 +00:00
renovate[bot]
20fd7ea13b chore(deps): update dependency @openedx/paragon to v23.14.8 (#1361)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-13 00:33:36 -04:00
renovate[bot]
d55d38ec12 fix(deps): update dependency core-js to v3.46.0 (#1362)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-13 04:33:28 +00:00
edX requirements bot
e77c6ee74a chore: update browserslist DB (#1360)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-10-13 00:36:37 +00:00
renovate[bot]
8cb30bedd8 chore(deps): update dependency @testing-library/jest-dom to v6.9.1 (#1359)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-06 05:59:28 +00:00
renovate[bot]
0ab3f5f669 chore(deps): update dependency @openedx/paragon to v23.14.4 (#1358)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-10-06 05:59:22 +00:00
edX requirements bot
9eaab9c2e5 chore: update browserslist DB (#1357)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-10-06 00:34:08 +00:00
renovate[bot]
ac28626b3c fix(deps): update dependency core-js to v3.45.1 (#1356)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-29 04:51:54 +00:00
renovate[bot]
3f90fea26c chore(deps): update dependency @openedx/paragon to v23.14.3 (#1355)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-29 04:51:46 +00:00
edX requirements bot
a0466852d6 chore: update browserslist DB (#1354)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-09-29 00:34:57 +00:00
Feanil Patel
9fad507ada Merge pull request #1353 from openedx/feanil/remove-reactifex-packages
build: remove unused reactifex packages
2025-09-26 16:14:50 -04:00
Feanil Patel
73351fa8e8 build: Drop translation pushing.
This is now handled via the openedx-translations repo so we don't need
this target that uses scripts from the deprecated reactifex repo.
2025-09-26 16:08:41 -04:00
Feanil Patel
c8528a7874 build: remove unused reactifex packages
Remove both reactifex and @edx/reactifex packages from devDependencies
as they are no longer needed. Translation extraction functionality has
been verified to work correctly without these dependencies.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-26 16:08:37 -04:00
bydawen
124909ab74 test: Remove support for Node 20 (#1345)
Co-authored-by: Peter Kulko <93188219+PKulkoRaccoonGang@users.noreply.github.com>
2025-09-26 10:49:44 -03:00
oleksandr.buhaienko
77f66f3afb build: Upgrade to Node 24 2025-09-26 09:17:19 -03:00
renovate[bot]
102288407f chore(deps): update dependency @testing-library/jest-dom to v6.8.0 (#1352)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-22 01:43:38 -04:00
renovate[bot]
dd7c35497e fix(deps): update dependency form-urlencoded to v6.1.6 (#1351)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-22 05:43:31 +00:00
edX requirements bot
8331d37b7f chore: update browserslist DB (#1350)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-09-22 00:37:03 +00:00
bydawen
1d12506b01 test: Add Node 24 to CI matrix (#1342) 2025-09-19 13:55:15 -04:00
renovate[bot]
c22c4ec5a6 fix(deps): update dependency @edx/frontend-platform to v8.5.1 (#1349)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 05:31:26 +00:00
renovate[bot]
69fc4be952 fix(deps): update dependency @edx/frontend-component-footer to v14.9.2 (#1348)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-15 05:31:11 +00:00
Awais Ansari
9d946eacd8 Revert "Replace of injectIntl with useIntl() 9/10" (#1346) 2025-09-11 22:00:53 +05:00
renovate[bot]
0af0935e86 fix(deps): update dependency @edx/frontend-component-footer to v14.9.1 (#1340)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 00:58:49 -04:00
renovate[bot]
fd33842109 fix(deps): update dependency @edx/frontend-component-header to v6.6.1 (#1341)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-08 04:58:43 +00:00
edX requirements bot
13b5f3bc12 chore: update browserslist DB (#1339)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-09-08 00:35:49 +00:00
Brian Smith
502ad904ea Merge pull request #1328 from openedx/revert-1314-1295/replaceInjectIntl10of10
Revert "Replace of injectIntl with useIntl() 10/10"
2025-09-04 18:37:24 -04:00
diana-villalvazo-wgu
594ae27c0e test: do not revert increase on coverage 2025-09-04 15:13:07 -06:00
sundasnoreen12
0924cb1ba3 Revert "Replace of injectIntl with useIntl() 10/10" 2025-09-04 15:13:07 -06:00
Samuel Allan
90ee5800b4 fix: update frontend-build to fix install issues (#1335)
Earlier versions of @openedx/frontend-build used on older version of
'sharp', which caused intermittent installation issues. The version of
'sharp' was updated in @openedx/frontend-build to fix these issues, so
the frontend-build version can be updated here, to fix the issues in
this project too. See
https://github.com/openedx/frontend-build/issues/664 and
https://github.com/openedx/frontend-build/pull/665 for more information.

The frontend-build dependency was updated by:

```
npm install --package-lock-only @openedx/frontend-build
```

Private-ref: https://tasks.opencraft.com/browse/BB-9953
2025-09-04 08:49:37 -06:00
Diana Villalvazo
6be87b4a82 refactor: Replace of injectIntl with useIntl() (#1303) 2025-09-02 13:52:23 -04:00
edX requirements bot
2137e985b3 chore: update browserslist DB (#1334)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-09-01 00:41:51 +00:00
bydawen
ca93f890e1 fix: long email field on account page (#1024)
* fix: long email field on account page

* fix: Add tooltip to the email field

---------

Co-authored-by: Stanislav Lunyachek <stanislav.lunyachek@raccoongang.com>
2025-08-28 15:07:28 +05:00
Eemaan Amir
6a57622a3c test: added test cases to improve test coverage (#1330)
* test: added test cases to improve test coverage

* fix: updated the link for code coverage

* fix: fixed lint errors

* test: added test cases

* fix: fixed message id
2025-08-26 22:04:22 +05:00
renovate[bot]
3d490c3879 fix(deps): update dependency bowser to v2.12.1 (#1333)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-25 04:50:54 +00:00
renovate[bot]
1e09c83300 fix(deps): update dependency @fortawesome/react-fontawesome to v0.2.6 (#1332)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-25 04:50:43 +00:00
edX requirements bot
b660903836 chore: update browserslist DB (#1331)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-08-25 00:36:40 +00:00
renovate[bot]
3d2b8416f9 chore(deps): update dependency @testing-library/jest-dom to v6.7.0 (#1327)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-18 06:57:56 +00:00
renovate[bot]
4afd07201b fix(deps): update dependency @openedx/paragon to v23.14.2 (#1326)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-18 06:57:52 +00:00
edX requirements bot
94ead51915 chore: update browserslist DB (#1317)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-08-18 00:39:58 +00:00
Brayan Cerón
587e3f2647 feat: add slot to extend the profile fields (#1254)
* feat: add ExtendedProfileFieldsSlot component

* feat: update ExtendedProfileFieldsSlot documentation and images

* fix: format code in ExtendedProfileFieldsSlot README for consistency

* feat: add error handling to ExtendedProfileFieldsSlot component

* feat: replace ExtendedProfileFieldsSlot with AdditionalProfileFieldsSlot and update documentation

* feat: add Example component for AdditionalProfileFieldsSlot and update README

* fix: update README to reflect correct slot name and widget ID for AdditionalProfileFieldsSlot

* chore: remove unused default_fields.png image
2025-08-13 12:34:12 -04:00
renovate[bot]
3394656ed2 fix(deps): update dependency @edx/frontend-platform to v8.5.0 (#1322)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 10:25:30 +00:00
renovate[bot]
66eda1a58d fix(deps): update dependency @edx/frontend-component-header to v6.6.0 (#1321)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 10:25:25 +00:00
renovate[bot]
c1ccc8c201 fix(deps): update dependency bowser to v2.12.0 (#1323)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 07:37:14 +00:00
renovate[bot]
4ae65cfcee fix(deps): update dependency @openedx/paragon to v23.14.1 (#1320)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-08-11 07:36:46 +00:00
sundasnoreen12
ad7e2035bc Merge pull request #1314 from WGU-Open-edX/1295/replaceInjectIntl10of10
Replace of injectIntl with useIntl() 10/10
2025-08-11 12:32:26 +05:00
sundasnoreen12
d5d67dbe14 Merge pull request #1307 from WGU-Open-edX/1294/replaceInjectIntl9of10
Replace of injectIntl with useIntl() 9/10
2025-08-11 12:30:53 +05:00
Diana Villalvazo
3423e5efea refactor: Replace of injectIntl with useIntl() (#1306) 2025-08-07 16:35:14 -04:00
Diana Villalvazo
f3d30925a8 refactor: Replace of injectIntl with useIntl() (#1305) 2025-08-07 16:31:34 -04:00
Diana Villalvazo
1ec2c3b262 refactor: Replace of injectIntl with useIntl() (#1304) 2025-08-07 16:27:49 -04:00
Diana Villalvazo
260df228fb Replace of injectIntl with useIntl() 4/10 (#1299)
* refactor: Replace of injectIntl with useIntl()

* fix: improve coverage
2025-08-07 16:24:46 -04:00
Diana Villalvazo
6b740a89c6 refactor: Replace of injectIntl with useIntl() (#1298) 2025-08-07 16:21:34 -04:00
Diana Villalvazo
03f8fdbdc3 refactor: Replace of injectIntl with useIntl() (#1297) 2025-08-07 16:18:20 -04:00
diana-villalvazo-wgu
0b86166a57 test: improve coverage 2025-08-07 13:08:04 -05:00
diana-villalvazo-wgu
46eefa7592 refactor: replace injectIntl 2025-08-07 12:40:04 -05:00
Stanislav
036b4be854 feat: Restore jump nav and content width (#994) 2025-08-07 17:30:39 +05:00
Eemaan Amir
eae0bfdca2 Merge pull request #1318 from openedx/INF-2087
chore: updated the notification section description
2025-08-07 11:29:32 +05:00
eemaanamir
05f5903cbc fix: fixed lint 2025-08-07 11:12:19 +05:00
eemaanamir
725ae950f4 chore: updated the notification section description 2025-08-04 16:46:32 +05:00
Eemaan Amir
38c4f3bad3 Merge pull request #1315 from openedx/INF-2003
chore: cleaned up course level preferences apis
2025-07-31 17:22:59 +05:00
eemaanamir
46acf2a5a4 chore: cleaned up course level preferences apis 2025-07-30 12:37:50 +05:00
wgu-jesse-stewart
e8aafef127 feat: add dev script to package.json (#1313)
* fix: add dev script to package.json

* docs: update readme with dev script
2025-07-29 09:46:38 -04:00
renovate[bot]
cf451770ed fix(deps): update dependency @fortawesome/react-fontawesome to v0.2.3 (#1312)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-28 06:40:00 +00:00
renovate[bot]
d9c3975f26 chore(deps): update dependency @testing-library/jest-dom to v6.6.4 (#1311)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-28 06:39:48 +00:00
diana-villalvazo-wgu
5f42857332 refactor: Replace of injectIntl with useIntl() 2025-07-21 14:38:02 -06:00
renovate[bot]
f6babc2db9 fix(deps): update dependency core-js to v3.44.0 (#1302)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-21 10:33:00 +00:00
renovate[bot]
67a053f3e0 fix(deps): update dependency @edx/frontend-component-header to v6.4.2 (#1301)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-21 07:34:41 +00:00
Diana Villalvazo
845ab30af5 refactor: Replace of injectIntl with useIntl() (#1296) 2025-07-17 16:33:42 -07:00
Muhammad Adeel Tajamul
3244ecf70b feat: added support for non editable field in notification preference (#1284) 2025-07-09 10:00:25 +05:00
renovate[bot]
a1390ebf36 fix(deps): update dependency @openedx/paragon to v23.14.0 (#1283)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 07:34:04 +00:00
renovate[bot]
89f9d9511f fix(deps): update dependency @edx/frontend-component-header to v6.4.1 (#1282)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-07-07 07:33:43 +00:00
edX requirements bot
bc66c74a33 chore: update browserslist DB (#1281)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-07-07 00:39:25 +00:00
Ahtisham Shahid
d760be1a53 feat: integrated notification preferences v2 API (#1280) 2025-07-04 17:00:09 +05:00
renovate[bot]
929a34a0f6 fix(deps): update dependency core-js to v3.43.0 (#1278)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-30 06:08:09 +00:00
edX requirements bot
55fc919c6f chore: update browserslist DB (#1277)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-06-30 00:39:16 +00:00
Hassan Raza
102a93486e Merge pull request #1271 from openedx/hraza/INF-1917
feat: Add notification for discussion app
2025-06-27 19:23:28 +05:00
renovate[bot]
981ba84163 chore(deps): update dependency @openedx/frontend-build to v14.6.1 (#1275)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-26 23:11:28 +00:00
renovate[bot]
8aa918bfb9 fix(deps): update dependency @openedx/paragon to v23.13.0 (#1276)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-26 19:43:54 +00:00
Adam Stankiewicz
4ca2ab9f4e fix: use Container component with size='xl' (#1274) 2025-06-19 10:07:11 -04:00
Brian Smith
c01f1854ee feat!: add design tokens support (#1272)
BREAKING CHANGE: Pre-design-tokens theming is no longer supported.

Co-authored-by: Diana Olarte <diana.olarte@edunext.co>
2025-06-18 12:35:37 -04:00
Hassan Raza
987484d205 feat: Add notification for discussion app 2025-06-17 14:47:36 +05:00
renovate[bot]
f86496468e fix(deps): update dependency @openedx/paragon to v22.20.2 (#1269)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-16 05:45:50 +00:00
renovate[bot]
e916ba29b9 fix(deps): update dependency @edx/frontend-platform to v8.4.0 (#1268)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-16 05:45:34 +00:00
edX requirements bot
6410ce1d8f chore: update browserslist DB (#1267)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-06-16 00:37:49 +00:00
Diana Villalvazo
d0eebfa0ea fix: untranslated user facing texts for i18n (#1245)
Co-authored-by: diana-villalvazo-wgu <dianaximena.villalva@wgu.edu>
2025-06-10 11:57:54 -04:00
renovate[bot]
77daf2fbad fix(deps): update dependency @edx/frontend-component-footer to v14.9.0 (#1264)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-09 05:53:40 +00:00
renovate[bot]
354426037e fix(deps): update dependency @edx/frontend-platform to v8.3.9 (#1263)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-09 05:53:31 +00:00
edX requirements bot
b9af9ed700 chore: update browserslist DB (#1261)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-06-09 00:38:12 +00:00
renovate[bot]
2342eaae82 fix(deps): update dependency @edx/frontend-component-footer to v14.8.0 (#1259)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-02 05:42:07 +00:00
renovate[bot]
a1484264fb fix(deps): update dependency @edx/frontend-platform to v8.3.8 (#1258)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-06-02 05:42:01 +00:00
edX requirements bot
cff4a76b0c chore: update browserslist DB (#1257)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-06-02 00:37:24 +00:00
renovate[bot]
3e2e8095b4 fix(deps): update dependency @openedx/paragon to v22.18.1 (#1256)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-26 01:54:28 -04:00
renovate[bot]
ebd63a13a9 fix(deps): update react-router monorepo to v6.30.1 (#1255)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-26 05:54:21 +00:00
Muhammad Adeel Tajamul
fd0d08daa1 feat: added immediate option in email cadence dropdown (#1251)
* feat: added immediate option in email cadence dropdown

* chore: added tests
2025-05-20 15:51:07 +05:00
renovate[bot]
6ae4c2d68b fix(deps): update dependency @edx/frontend-platform to v8.3.7 (#1252)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-19 13:56:27 +00:00
renovate[bot]
95331d1b10 fix(deps): update dependency @edx/frontend-platform to v8.3.6 (#1250)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-19 07:20:20 +00:00
renovate[bot]
7c63b66d8e fix(deps): update dependency @edx/frontend-component-footer to v14.7.2 (#1249)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-19 07:20:07 +00:00
edX requirements bot
810f506e52 chore: update browserslist DB (#1248)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-05-19 00:37:19 +00:00
edX requirements bot
33fd669c8f chore: update browserslist DB (#1247)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-05-12 00:37:27 +00:00
Muhammad Adeel Tajamul
5c5204fb17 chore: temporarily disabled notification preferences course dropdown (#1246) 2025-05-08 18:18:08 +05:00
renovate[bot]
95db89a9dd chore(deps): update dependency @openedx/frontend-build to v14.6.0 (#1244)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-05 05:28:19 +00:00
renovate[bot]
81a878a658 fix(deps): update dependency @edx/frontend-component-footer to v14.7.1 (#1243)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-05 05:28:06 +00:00
edX requirements bot
b502de846a chore: update browserslist DB (#1242)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2025-05-05 00:37:13 +00:00
renovate[bot]
39f0123820 fix(deps): update dependency @edx/openedx-atlas to ^0.7.0 (#1240)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-28 02:05:08 -04:00
renovate[bot]
7ee70193c0 fix(deps): update dependency @edx/frontend-component-footer to v14.7.0 (#1239)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-28 06:05:00 +00:00
Awais Ansari
0e1574dba7 chore: updated NODE_ENV string in .env (#1238) 2025-04-25 15:21:20 +05:00
Brian Smith
0d45ae6599 feat: import FooterSlot from component package instead of slot package (#1230) 2025-04-24 12:18:00 -04:00
Brian Smith
78246cf26b feat: standardize slot ids (#1237) 2025-04-24 07:28:24 -04:00
renovate[bot]
ca193563ec fix(deps): update dependency @edx/frontend-component-header to v6.4.0 (#1236)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-23 16:13:09 -04:00
Awais Ansari
d0efd35e66 Revert "fix: removed extra api call and strict mode (#1234)" (#1235)
This reverts commit 375b704eef.
2025-04-23 15:42:58 -04:00
sundasnoreen12
375b704eef fix: removed extra api call and strict mode (#1234)
* fix: removed extra api call and strict mode

* fix: added default values
2025-04-23 22:13:06 +05:00
Hassan Raza
ae121358db fix: Update ORA notification display title (#1233) 2025-04-23 16:09:29 +05:00
renovate[bot]
92b7c58af7 fix(deps): update dependency long to v5.3.2 (#1232)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-21 05:46:58 +00:00
renovate[bot]
a4097fe6fc fix(deps): update dependency @openedx/frontend-slot-footer to v1.2.1 (#1231)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-21 05:46:49 +00:00
Awais Ansari
397f688300 fix: scrolling issue for active menu item 2025-04-16 15:29:09 +05:00
Awais Ansari
8bd4b1b9a8 chore: updated package-lock file 2025-04-16 15:29:09 +05:00
Awais Ansari
54d029c181 test: updated test case snapshots 2025-04-16 15:29:09 +05:00
Hassan Raza
e6a4636147 fix: Update course preference title for ora submission (#1222) 2025-04-16 15:29:09 +05:00
sundasnoreen12
efb4162926 test: added test cases 2025-04-16 15:29:09 +05:00
sundasnoreen12
6061232e10 refactor: refactor code 2025-04-16 15:29:09 +05:00
sundasnoreen12
ba6b8c8f9b refactor: refactor code 2025-04-16 15:29:09 +05:00
sundasnoreen12
9c16ba0075 fix: fixed course level preference issue 2025-04-16 15:29:09 +05:00
sundasnoreen12
02b987909b fix: fixed varaible name 2025-04-16 15:29:09 +05:00
sundasnoreen12
1bcc54bb05 fix: added changes for restricted country 2025-04-16 15:29:09 +05:00
sundasnoreen12
5a5b0b905b fix: removed unused selector 2025-04-16 15:29:09 +05:00
sundasnoreen12
44ed49c7d2 refactor: refactored code 2025-04-16 15:29:09 +05:00
sundasnoreen12
386baa3840 fix: changed frequency from never to daily on email preference change 2025-04-16 15:29:09 +05:00
Awais Ansari
f1a56ad6bc fix: updated notifications section url (#1185)
* fix: updated notifiations section url

* fix: updated test cases
2025-04-16 15:29:09 +05:00
Awais Ansari
465bb9f7a0 feat: added notification preferences settings at account level (#1159)
* feat: added notification preferences settings at account level

* fix: fixed test cases

* feat: added api for account notification type

* fix: fixed test cases and label

* test: added update account preference test case

* fix: fixed issue to update email cadence for account notification type

* refactor: updated time

* fix: fixed mixed cadence issue

* fix: fixed border issue when no preferences

* refactor: refactor code

---------

Co-authored-by: sundasnoreen12 <sundasnoreen12@gmail.com>
2025-04-16 15:29:09 +05:00
Muhammad Adeel Tajamul
f9b7525d44 refactor: moved unable to delete into component (#1177) 2025-04-16 15:29:09 +05:00
Muhammad Adeel Tajamul
c7e82295c2 feat: added feature to hide delete button for countries (#1176) 2025-04-16 15:29:09 +05:00
sundasnoreen12
e02cf28b54 fix: rebase with 2u 2025-04-16 15:29:09 +05:00
Awais Ansari
18c51e8e73 fix: translation and console errors (#1166) 2025-04-16 15:29:09 +05:00
ayesha waris
88b444e796 chore: rebase with master (#1158)
* fix: fixed support urls (#1155)

* fix(deps): update dependency @edx/frontend-component-header to v5.7.1 (#1156)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix(deps): update dependency @openedx/frontend-slot-footer to v1.0.6 (#1157)

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>

* fix: fixed certificates url

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-04-16 15:29:09 +05:00
Awais Ansari
71635b33b6 feat: added country disabling feature (#1116)
* feat: added country disabling feature

* refactor: removed isDisabledCountry additional call
2025-04-16 15:29:09 +05:00
Hunia Fatima
515890d5ef feat: upgrade react to v18 (#1220) 2025-04-04 11:29:11 -04:00
renovate[bot]
c2960e1232 chore(deps): update dependency @openedx/frontend-build to v14.4.1 (#1223)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-31 06:44:07 +00:00
Brian Smith
17e12f7f87 chore(deps): update @openedx dependencies to versions that support React 18 (#1218)
* chore(deps): update `@openedx` dependencies to versions that support React 18

* fix(tests): update `JumpNav` tests

* update JumpNav tests to use RTL render/screen instead of `react-test-renderer` snapshots
* update test names to reflect "optional information" not appearing in either scenario
  * the only difference in the snapshot ffc39868e9/src/account-settings/test/__snapshots__/JumpNav.test.jsx.snap is the delete account link

* chore(tests): update snapshots

* chore: fix lint
2025-03-25 12:22:13 -04:00
renovate[bot]
ffc39868e9 fix(deps): update react-router monorepo to v6.30.0 (#1217)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-24 07:32:44 +00:00
renovate[bot]
9ba01af816 fix(deps): update dependency qs to v6.14.0 (#1216)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-24 07:32:39 +00:00
Sarina Canelake
fc222cc76c Update README with Tutor instructions; update outdated edx.rtd.io links (#1203)
* docs: remove Devstack mentions from README instructions

* docs: Update edx.rtd.io links to point at new homes

* docs: Apply code review suggestions

Co-authored-by: Brian Smith <112954497+brian-smith-tcril@users.noreply.github.com>

---------

Co-authored-by: Brian Smith <112954497+brian-smith-tcril@users.noreply.github.com>
2025-03-21 15:15:04 +00:00
renovate[bot]
99a39568de fix(deps): update dependency @openedx/frontend-plugin-framework to v1.6.0 (#1210)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 06:23:54 +00:00
renovate[bot]
9ce3cbfddd fix(deps): update dependency long to v5.3.1 (#1211)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-17 06:23:47 +00:00
renovate[bot]
55a72b3f6e fix(deps): update dependency core-js to v3.41.0 (#1207)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-10 07:26:24 +00:00
renovate[bot]
597004c82e fix(deps): update dependency @openedx/frontend-slot-footer to v1.1.0 (#1206)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-10 07:25:41 +00:00
renovate[bot]
3458b6f410 chore(deps): update dependency @openedx/frontend-build to v14.3.2 (#1204)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-10 05:52:22 +00:00
renovate[bot]
b70b4a796f fix(deps): update dependency @openedx/frontend-plugin-framework to v1.5.0 (#1200)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 06:16:01 +00:00
renovate[bot]
d76945d2f2 fix(deps): update dependency @edx/frontend-platform to v8.2.1 (#1199)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-03-03 06:15:48 +00:00
renovate[bot]
7ee34f7d9a chore(deps): update dependency @openedx/frontend-build to v14.3.1 (#1196)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-24 06:00:19 +00:00
renovate[bot]
96c619cab8 fix(deps): update dependency @edx/frontend-component-header to v5.8.3 (#1195)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-24 06:00:08 +00:00
renovate[bot]
9eda5e588c chore(deps): update dependency @edx/browserslist-config to v1.5.0 (#1191)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-02-17 05:26:57 +00:00
Feanil Patel
3aff0e03e0 Merge pull request #1190 from openedx/feanil-patch-1
docs: Update catalog-info.yaml
2025-02-14 11:12:57 -05:00
Feanil Patel
852358f243 docs: Update catalog-info.yaml
This team was replace/renamed at some point but the catalog file was not updated.
2025-02-14 11:09:12 -05:00
Feanil Patel
5ffd17db4c Merge pull request #1186 from salman2013/salman/update-catalog-info-file
Update catalog-info-file for release data.
2025-01-24 09:40:22 -05:00
renovate[bot]
6f3c9616d6 fix(deps): update dependency long to v5.2.4 (#1188)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 05:37:47 +00:00
renovate[bot]
5f4812ed47 fix(deps): update dependency @edx/frontend-platform to v8.1.5 (#1187)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-20 05:33:41 +00:00
salman2013
d694b5c428 chore: update catalog-info-file for release data and remove openedx.yaml 2025-01-14 15:21:34 +05:00
renovate[bot]
6b73130e9b fix(deps): update font awesome to v6.7.2 (#1183)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-06 07:08:39 +00:00
renovate[bot]
d608f3947e fix(deps): update dependency @edx/frontend-platform to v8.1.4 (#1182)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-01-06 07:08:29 +00:00
renovate[bot]
db83838d5e fix(deps): update dependency @openedx/paragon to v22.13.0 (#1180)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-30 08:07:33 +00:00
renovate[bot]
004cdf35f1 fix(deps): update dependency core-js to v3.39.0 (#1181)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-30 08:07:20 +00:00
renovate[bot]
6cb015e49d chore(deps): update dependency @edx/browserslist-config to v1.4.0 (#1179)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-23 06:14:39 +00:00
renovate[bot]
c5ae2c40d7 fix(deps): update dependency @openedx/frontend-slot-footer to v1.0.7 (#1178)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-23 06:14:32 +00:00
renovate[bot]
46edd0cb70 fix(deps): update dependency @edx/frontend-platform to v8.1.3 (#1175)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-16 06:16:09 +00:00
renovate[bot]
2c6b5c34a9 fix(deps): update dependency @edx/frontend-component-header to v5.8.2 (#1174)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-16 06:15:57 +00:00
renovate[bot]
b000d2e048 fix(deps): update dependency @edx/frontend-component-header to v5.8.1 (#1173)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-09 05:35:05 +00:00
renovate[bot]
437fba16fe chore(deps): update dependency @openedx/frontend-build to v14.2.2 (#1172)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-09 05:34:44 +00:00
renovate[bot]
c4038b4085 fix(deps): update dependency @openedx/paragon to v22.10.0 (#1170)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-02 07:12:14 +00:00
renovate[bot]
496ff21015 fix(deps): update dependency @openedx/frontend-plugin-framework to v1.4.1 (#1169)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-12-02 07:11:53 +00:00
renovate[bot]
23e16ac6e0 fix(deps): update dependency @edx/frontend-component-header to v5.8.0 (#1163)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-27 09:42:18 +00:00
renovate[bot]
d8c5762ee2 chore(deps): update dependency @openedx/frontend-build to v14.2.0 (#1162)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-27 09:42:02 +00:00
sundasnoreen12
4d2d520234 Merge pull request #1165 from openedx/sundas/INF-1655
fix: fixed url break issue
2024-11-27 14:38:48 +05:00
sundasnoreen12
b094722772 fix: fixed url break issue 2024-11-26 17:01:27 +05:00
renovate[bot]
a9f061f10c fix(deps): update dependency qs to v6.13.1 (#1161)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-18 06:51:21 +00:00
renovate[bot]
4eb5d4effc fix(deps): update dependency @edx/frontend-component-header to v5.7.2 (#1160)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-18 06:49:32 +00:00
renovate[bot]
925bcce81c fix(deps): update dependency @openedx/frontend-slot-footer to v1.0.6 (#1157)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-11 07:12:06 +00:00
renovate[bot]
f44e03c7c8 fix(deps): update dependency @edx/frontend-component-header to v5.7.1 (#1156)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-11-11 07:11:39 +00:00
ayesha waris
999988dd88 fix: fixed support urls (#1155) 2024-11-08 14:12:27 +05:00
renovate[bot]
7ec1226965 fix(deps): update dependency @openedx/frontend-plugin-framework to v1.4.0 2024-11-04 06:24:25 +00:00
renovate[bot]
5b00275371 fix(deps): update dependency @edx/frontend-component-header to v5.7.0 2024-11-04 06:24:10 +00:00
renovate[bot]
30c1158775 fix(deps): update dependency universal-cookie to v7.2.2 2024-11-04 05:09:20 +00:00
renovate[bot]
c196b60b39 chore(deps): update dependency @testing-library/jest-dom to v6.6.3 2024-11-04 05:09:08 +00:00
Bilal Qamar
3022a267b1 test: Remove support for Node 18 (#1112) 2024-10-31 15:14:19 -04:00
renovate[bot]
19aacdfaf9 chore(deps): update dependency redux-mock-store to v1.5.5 2024-10-28 04:23:38 +00:00
renovate[bot]
530cca7c59 chore(deps): update dependency @edx/reactifex to v2 (#1148)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-24 17:11:49 +05:00
Bilal Qamar
e66d77cdd1 chore: update TensorFlow packages to v4.22.0 (#1147) 2024-10-24 16:30:42 +05:00
renovate[bot]
30333edfa2 fix(deps): update dependency universal-cookie to v7 (#1138)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-10-24 16:27:36 +05:00
Muhammad Adeel Tajamul
538d85b5dd feat: removed never from cadence options (#1142) 2024-10-23 13:28:58 +05:00
Brian Smith
8066ae58f1 feat(deps): update header to 5.6.0 (#1144) 2024-10-22 19:18:39 -04:00
Adolfo R. Brandes
8ce7362fd8 Merge pull request #1139 from eduNEXT/bav/fix-incorrect-post-accept
fix: send json accept headers for POST requests
2024-10-22 12:40:46 -03:00
Bilal Qamar
9c6f247f0a test: updated failing snapshots 2024-10-22 11:13:43 +00:00
renovate[bot]
085bc19313 fix(deps): update dependency @openedx/paragon to v22.9.0 2024-10-22 11:13:43 +00:00
Alison Langston
ee7c769c2f Merge pull request #1141 from openedx/alangsto/update_idv_failure_message
feat: remove learn more about IDV button
2024-10-21 15:10:15 -04:00
Alie Langston
a37be2dac1 feat: remove learn more about IDV button 2024-10-21 14:58:15 -04:00
renovate[bot]
d0fdc2ac5d chore(deps): update dependency @testing-library/jest-dom to v6.6.2 2024-10-21 07:10:59 +00:00
Bryann Valderrama
8a4eecf6c2 fix: add missing trailing comma 2024-10-16 10:33:37 -05:00
Maria Grimaldi
e2e4003b44 fix: send json accept headers for POST requests 2024-10-15 16:44:42 -05:00
renovate[bot]
434f114220 fix(deps): update react-router monorepo to v6.27.0 2024-10-14 05:00:08 +00:00
Eemaan Amir
c31e1648bf Merge pull request #1132 from openedx/INF-1653
chore: updated notificationTitle for oraGradeAssigned
2024-10-10 12:02:05 +05:00
eemaanamir
379d2df2fc chore: updated notificationTitle for oraGradeAssigned 2024-10-09 16:20:58 +05:00
renovate[bot]
5933ae3034 fix(deps): update dependency @edx/frontend-platform to v8.1.2 2024-10-07 06:48:36 +00:00
renovate[bot]
d1269197ac fix(deps): update dependency @edx/frontend-component-header to v5.5.0 2024-10-07 04:49:32 +00:00
renovate[bot]
12438e79f5 fix(deps): update dependency @openedx/frontend-plugin-framework to v1.3.0 2024-10-07 04:49:08 +00:00
renovate[bot]
57317a4bb6 chore(deps): update dependency @testing-library/jest-dom to v6.5.0 2024-09-30 09:57:32 +00:00
renovate[bot]
3bbc068a73 chore(deps): update dependency @openedx/frontend-build to v14.1.5 2024-09-30 07:44:46 +00:00
Justin Hynes
4179aa798a Merge pull request #1124 from openedx/jhynes/APER-3582_add-missing-setting-default
chore: add missing ACCOUNT_PROFILE_URL setting to the Account MFE
2024-09-24 11:43:27 -04:00
Justin Hynes
a1877bb499 chore: add missing ACCOUNT_PROFILE_URL setting to the Account MFE
This PR adds the `ACCOUNT_PROFILE_URL` setting to the `.env` and `.env.development` settings files
in order to make it easier to configure the communication between the Profile and Account MFEs in
Open edX development environments.
2024-09-24 15:14:29 +00:00
Eemaan Amir
cd44f15634 Merge pull request #1121 from openedx/INF-1580
chore: added notificationTitle for oraGradeAssigned
2024-09-23 18:05:53 +05:00
eemaanamir
83242b1042 chore: added notificationTitle for oraGradeAssigned 2024-09-23 16:57:01 +05:00
renovate[bot]
116c30663b fix(deps): update react-router monorepo to v6.26.2 2024-09-23 06:29:11 +00:00
renovate[bot]
0ed92cc7e8 chore(deps): update dependency @openedx/frontend-build to v14.1.4 2024-09-23 06:29:02 +00:00
renovate[bot]
3ec887089d fix(deps): update dependency core-js to v3.38.1 2024-09-16 04:23:45 +00:00
renovate[bot]
b46165ede0 fix(deps): update dependency @openedx/frontend-slot-footer to v1.0.5 2024-09-16 04:23:31 +00:00
renovate[bot]
752d760030 fix(deps): update dependency @openedx/frontend-slot-footer to v1.0.4 2024-09-09 06:50:34 +00:00
renovate[bot]
3e2c4dc760 chore(deps): update dependency @openedx/frontend-build to v14.1.2 2024-09-09 06:50:25 +00:00
renovate[bot]
4136a63e92 fix(deps): update dependency @edx/openedx-atlas to v0.6.2 2024-09-02 07:21:15 +00:00
renovate[bot]
ca8c9ef4fe chore(deps): update dependency @openedx/frontend-build to v14.1.1 2024-09-02 07:20:57 +00:00
Bilal Qamar
14f1cfd0e1 build: Upgrade to Node 20 (#1090)
* feat: updated node to v20

* refactor: updated package-lock

* refactor: updated package-lock

* refactor: updated lockfile version check workflow

* refactor: updated package-lock along with ci & lockfile version workflows

* refactor: updated lockfile version workflow
2024-08-29 12:08:39 -04:00
Marcos Rigoli
f81ec9544a Merge pull request #1108 from openedx/rijuma/verify-copy-change
fix: Updated copy change on Name Change modal
2024-08-27 10:22:28 -03:00
Marcos
11d7d4beb4 chore: Removed unused translation string 2024-08-23 09:21:45 -03:00
Bilal Qamar
70fc2f9642 test: Add Node 20 to CI matrix (#1107) 2024-08-22 14:35:46 -04:00
Marcos
f1d3c88f76 fix: Updated copy change on Name Change modal 2024-08-21 09:21:03 -03:00
renovate[bot]
c5c05c86f7 fix(deps): update react-router monorepo to v6.26.1 2024-08-19 05:33:50 +00:00
renovate[bot]
b845b34b02 chore(deps): update dependency @openedx/frontend-build to v14.1.0 2024-08-19 05:33:32 +00:00
Marcos Rigoli
27c666cc27 Merge pull request #1097 from openedx/rijuma/idv-experiment-slot
feat: Added PluginSlot to wrap IDVerificationPage component
2024-08-16 10:27:39 -03:00
Marcos
dcdd024809 docs: Updated documentation on the IDV Page Plugin Slot 2024-08-16 10:11:01 -03:00
Isaac Lee
dab4b1dc92 Merge pull request #1102 from openedx/ilee2u/local-module-development-instructions
docs: add local module dev info
2024-08-15 15:27:11 -04:00
ilee2u
c40bcd49ea docs: add local module dev info 2024-08-15 14:40:28 -04:00
renovate[bot]
8e3de876d0 fix(deps): update dependency qs to v6.13.0 2024-08-12 07:38:08 +00:00
renovate[bot]
aff083335c fix(deps): update dependency core-js to v3.38.0 2024-08-12 07:37:54 +00:00
renovate[bot]
18ea60b470 fix(deps): update dependency @openedx/frontend-plugin-framework to v1.2.3 2024-08-12 05:07:40 +00:00
renovate[bot]
9260b4834c chore(deps): update dependency @openedx/frontend-build to v14.0.15 2024-08-12 05:07:13 +00:00
Marcos
d7a5b222ee feat: Added PluginSlot to wrap IDVerificationPage component 2024-08-09 12:45:06 -03:00
Ahtisham Shahid
17ce32b80c fix: updated courseUpdate -> courseUpdates in title text (#1096) 2024-08-07 19:02:09 +05:00
sundasnoreen12
d4693a2c2e Merge pull request #1095 from openedx/sundas/INF-1482
docs: updated read me and catalog info files
2024-07-29 15:00:58 +05:00
sundasnoreen12
a446d5d3fc docs: updated read me and catalog info files 2024-07-29 11:38:07 +03:00
renovate[bot]
8f4139ad87 fix(deps): update dependency @openedx/frontend-slot-footer to v1.0.3 2024-07-29 06:25:22 +00:00
renovate[bot]
87e591cd71 fix(deps): update dependency @edx/frontend-platform to v8.1.1 2024-07-29 06:25:03 +00:00
renovate[bot]
90c02279e3 chore(deps): update dependency @testing-library/jest-dom to v6.4.8 2024-07-29 06:24:54 +00:00
renovate[bot]
600a3de7b6 chore(deps): update dependency @openedx/frontend-build to v14.0.14 2024-07-29 04:31:29 +00:00
Eemaan Amir
4390ca5435 Merge pull request #1089 from openedx/INF-1300
refactor: removed the channel wide toggle feature
2024-07-24 14:13:23 +05:00
eemaanamir
5903ad2ae8 refactor: removed the channel wide toggle feature 2024-07-23 16:32:48 +05:00
Hunia Fatima
cc50ad9744 chore(deps): update font awesome deps (#1087)
* chore(deps): update font awesome deps

* chore(deps): updated snapshots
2024-07-23 15:52:47 +05:00
Hunia Fatima
4e69301a4e chore(deps): update reselect (#1085) 2024-07-23 14:56:45 +05:00
Hunia Fatima
3a877eb55c chore(deps): update memoise one (#1083) 2024-07-23 14:56:34 +05:00
Hunia Fatima
673639caee chore(deps): update react router and react router dom (#1084) 2024-07-23 14:28:38 +05:00
Hunia Fatima
474f7c018d chore(deps): update qs (#1081) 2024-07-23 14:28:04 +05:00
Hunia Fatima
46398e7edc chore(deps): update frontend plugin framework (#1079) 2024-07-23 14:24:23 +05:00
renovate[bot]
dfb06268ab fix(deps): update dependency @edx/frontend-component-header to v5.3.4 2024-07-22 12:46:21 +00:00
Justin Hynes
b0301d5028 Merge pull request #1077 from openedx/jhynes/APER-2823_remove-2U-demographics-collection
feat!: remove 2U demographics collection
2024-07-11 12:54:30 -04:00
Justin Hynes
89d848e408 fix: quality 2024-07-11 13:34:45 +00:00
Justin Hynes
56ba73e455 feat!: remove 2U demographics collection
[APER-2823]

This PR removes functionality specific to 2U and edx.org from the Account MFE that was used for collecting demographics info from learners.
2024-07-11 13:27:05 +00:00
Muhammad Adeel Tajamul
c3d30227a3 temp: removing immediately from email preferences (#1076) 2024-07-08 13:25:50 +05:00
renovate[bot]
f5bedd7708 fix(deps): update react-router monorepo to v6.24.0 2024-07-01 04:53:19 +00:00
renovate[bot]
218c796500 fix(deps): update dependency @openedx/frontend-plugin-framework to v1.2.1 2024-07-01 04:53:01 +00:00
sundasnoreen12
604845f4a3 Merge pull request #1070 from openedx/sundass/INF-1415
fix: fix loading and margin issues
2024-06-24 13:41:09 +05:00
sundasnoreen12
751cdb1076 fix: fix loading and margin issues 2024-06-24 13:34:05 +05:00
renovate[bot]
0ebc4eb3cd fix(deps): update dependency form-urlencoded to v6.1.5 2024-06-24 07:05:41 +00:00
renovate[bot]
8a954c19e2 chore(deps): update dependency @openedx/frontend-build to v14.0.10 2024-06-24 07:05:25 +00:00
renovate[bot]
18b999b9a8 fix(deps): update dependency core-js to v3.37.1 2024-06-17 07:20:17 +00:00
renovate[bot]
b155d04814 chore(deps): update dependency @testing-library/jest-dom to v6.4.6 2024-06-17 07:20:03 +00:00
renovate[bot]
6f715e598f chore(deps): update dependency @openedx/frontend-build to v14.0.9 2024-06-17 04:06:17 +00:00
Adolfo R. Brandes
ab8d6e7913 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-14 14:59:37 -03:00
sundasnoreen12
d2bb164fab Merge pull request #1062 from openedx/sundas/INF-1415
feat: removed app level toggles
2024-06-14 03:07:36 -07:00
sundasnoreen12
5aacaf44a3 refactor: rename selector 2024-06-14 12:17:47 +05:00
sundasnoreen12
4502dbe413 fix: removed role button to remove cursor pointer 2024-06-12 13:59:24 +05:00
sundasnoreen12
f5e2fa7448 test: removed test cases 2024-06-12 13:53:34 +05:00
sundasnoreen12
5954b3122f fix: remove extra check 2024-06-12 00:59:39 -07:00
sundasnoreen12
2bf71cce10 feat: remove app level toggles 2024-06-12 00:57:27 -07:00
renovate[bot]
8fd0320c88 fix(deps): update dependency @fortawesome/react-fontawesome to v0.2.2 2024-06-10 06:16:54 +00:00
renovate[bot]
da15a70942 fix(deps): update dependency @edx/openedx-atlas to v0.6.1 2024-06-10 04:12:10 +00:00
edX requirements bot
52f4c51362 chore: update browserslist DB (#984)
Co-authored-by: abdullahwaheed <42172960+abdullahwaheed@users.noreply.github.com>
2024-06-05 11:36:51 +05:00
renovate[bot]
a0c2fb2686 fix(deps): update dependency @edx/frontend-platform to v8.0.4 2024-06-03 06:42:23 +00:00
renovate[bot]
223f792e2b fix(deps): update dependency @edx/frontend-component-header to v5.3.3 2024-06-03 04:10:15 +00:00
sundasnoreen12
175d9cf5cb Merge pull request #1054 from openedx/sundas/INF-1389
fix: disabled email cadence dropdown while api is pending
2024-05-23 14:17:48 +05:00
sundasnoreen12
94ef29938f fix: disabled email cadence dropdown while api is pending 2024-05-23 13:35:24 +05:00
renovate[bot]
4e38c9e9a6 fix(deps): update dependency @edx/frontend-platform to v8.0.3 2024-05-20 04:39:40 +00:00
Brian Smith
81a1ec7e1e fix: use frontend-slot-footer that supports all footer versions
The previous version of `frontend-slot-footer` had a peer dependency of `^14.0.0`, which caused problems for some methods of installing forked footers. This updates to a version of `frontend-slot-footer` that allows for *any* version of `frontend-component-footer` in the peer dependency.
2024-05-16 16:02:55 -03:00
sundasnoreen12
90da11782f Merge pull request #1049 from openedx/sundas/INF-1360
fix: fixed environment variable issue for email cadence
2024-05-16 13:32:47 +05:00
sundasnoreen12
82c9e07f0f refactor: optimize function for empty array 2024-05-16 12:53:36 +05:00
Brian Smith
16fb3a1bb4 fix: import FooterSlot from frontend-slot-footer package (#1048) 2024-05-15 16:46:34 -04:00
sundasnoreen12
e623f03c4b fix: hide type and web option from the header when there is no preference under app 2024-05-15 17:57:54 +05:00
sundasnoreen12
b2173afb9c refactor: removed lowercase function 2024-05-15 15:36:50 +05:00
sundasnoreen12
c787a77f9e refactor: fixed name 2024-05-15 15:11:15 +05:00
sundasnoreen12
075587c1f0 refactor: refactor name for notification channels 2024-05-15 14:20:13 +05:00
sundasnoreen12
a0b0b1f8d4 fix: fixed channels alignment issue 2024-05-15 14:13:00 +05:00
sundasnoreen12
16f20cad66 fix: fixed environment variable issue for email cadence 2024-05-15 13:21:58 +05:00
Muhammad Abdullah Waheed
40c67995a4 fix: reverted footer plugin to use simple footer (#1047) 2024-05-14 18:02:17 +05:00
sundasnoreen12
0fc68f7d93 Merge pull request #1043 from openedx/Ayesha/INF-1360-fix
fix: fixed hover color of cadence button
2024-05-14 16:12:49 +05:00
sundasnoreen12
90d3668128 fix: added paragon token for color 2024-05-14 16:09:40 +05:00
sundasnoreen12
1440c8239c Merge pull request #1046 from openedx/sundas/INF-1283
fix: core notification is now not visible under updates
2024-05-14 13:08:32 +05:00
sundasnoreen12
bb29f9624e fix: fixed test cases 2024-05-13 17:21:53 +05:00
sundasnoreen12
746b0fed4b fix: core notification is now not visible under updates 2024-05-13 16:03:13 +05:00
ayeshoali
02d14b95a7 fix: fixed hover color of cadence button 2024-05-13 14:58:14 +05:00
ayeshoali
484fc95ea0 refactor: added utils file for showEmailChannel 2024-05-13 13:36:08 +05:00
renovate[bot]
bdb851eb8b fix(deps): update dependency @edx/frontend-component-header to v5.3.1 2024-05-13 06:37:27 +00:00
ayeshoali
b984296550 refactor: grouped imports 2024-05-10 17:12:21 +05:00
ayeshoali
39fca96523 refactor: updated logic to handle show email channel env variable 2024-05-10 17:05:11 +05:00
Brian Smith
d1e817d4ba feat: use frontend-plugin-framework to provide a FooterSlot 2024-05-09 14:13:27 -03:00
ayeshoali
b35042ca97 refactor: resolved comments 2024-05-09 13:43:16 +05:00
ayeshoali
88e9eb3fdf refactor: added environment variable for email channel 2024-05-09 01:05:19 +05:00
ayeshoali
4409be4cc6 fix: fixed hover color of cadence button 2024-05-08 17:55:41 +05:00
ayesha waris
e314de2042 feat: implemented notification preference UI for cadence (#1033)
* feat: implented notification preference UI for cadence

* refactor: refactored code

* refactor: refactored code

* refactor: clean code after adding email cadence

* refactor: refactored and restructured notificationPreferences page

* refactor: refactored and implemented mobile view

* fix: fixed disabled for email cadence

* refactor: resolved spaces and changed font size class

* refactor: resolved conflicts

---------

Co-authored-by: Awais Ansari <awais.ansari63@gmail.com>
2024-05-07 16:54:15 +05:00
renovate[bot]
fc329fe9c2 chore(deps): update dependency @testing-library/jest-dom to v6.4.5 2024-05-06 07:21:07 +00:00
Ahtisham Shahid
aea3a7c830 fix: updated notification app name to grading (#1041) 2024-05-06 12:17:34 +05:00
Ahtisham Shahid
35b5459dec fix: removed core type from ora app (#1040) 2024-04-30 18:09:28 +05:00
renovate[bot]
2b82ac9faf fix(deps): update react-router monorepo to v6.23.0 2024-04-29 07:39:32 +00:00
renovate[bot]
24ad8851e6 fix(deps): update dependency core-js to v3.37.0 2024-04-29 04:53:22 +00:00
renovate[bot]
b62645349e fix(deps): update dependency @edx/frontend-platform to v8.0.1 2024-04-29 04:52:47 +00:00
Awais Ansari
d2c160b771 Merge pull request #1035 from openedx/ahtisham/regen-lock-file
chore: regenerated package lock file
2024-04-25 15:48:05 +05:00
Ahtisham Shahid
6e39072334 chore: regenerated package lock file 2024-04-25 15:02:44 +05:00
Bilal Qamar
245bf9aa6b feat: updated frontend-build & frontend-platform major versions (#958)
* chore: bumped jest to v29, updated snapshots

* refactor: switched frontend-build to openedx, updated snapshots

* refactor: added overrides to resolve dependency issues

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

* refactor: updated frontend-build to alpha branch

* refactor: updated frontend-build to alpha version

* refactor: updated overrides

* feat: updated build and platform major versions, along with edx packages
2024-04-24 18:02:11 +05:00
renovate[bot]
b8a4163629 fix(deps): update dependency @edx/frontend-platform to v7.1.4 2024-04-22 06:20:33 +00:00
Ahtisham Shahid
a5730b625a feat: added new messages for ora notification (#1026) 2024-04-22 11:16:52 +05:00
renovate[bot]
f03ac8b6d9 fix(deps): update dependency @edx/frontend-component-footer to v13.0.5 2024-04-15 06:54:54 +00:00
renovate[bot]
9e72f1c9e9 fix(deps): update dependency qs to v6.12.1 2024-04-15 04:21:13 +00:00
ayesha waris
9693d938c6 Revert "feat: implented notification preference UI for cadence (#1013)" (#1029)
This reverts commit a266e3dca9.
2024-04-05 16:53:13 +05:00
Muhammad Ammar
f45bb43064 Merge pull request #1027 from openedx/ammar/add-more-languages
feat: add new languages
2024-04-04 23:50:13 +05:00
muhammad-ammar
32f9804e4a feat: add new languages 2024-04-04 14:38:43 +05:00
renovate[bot]
feef40f6d0 fix(deps): update dependency @edx/frontend-platform to v7.1.3 2024-04-01 04:04:33 +00:00
ayesha waris
a266e3dca9 feat: implented notification preference UI for cadence (#1013)
* feat: implented notification preference UI for cadence

* refactor: refactored code

* refactor: refactored code

* refactor: clean code after adding email cadence

* refactor: refactored and restructured notificationPreferences page

* refactor: refactored and implemented mobile view

---------

Co-authored-by: Awais Ansari <awais.ansari63@gmail.com>
2024-03-25 20:52:22 +05:00
renovate[bot]
6cc50a983e fix(deps): update dependency core-js to v3.36.1 2024-03-25 06:48:12 +00:00
Samir Sabri
16caae04af feat!: remove Transifex calls for OEP-58 (#887) 2024-03-18 15:05:34 -04:00
renovate[bot]
2452d09913 fix(deps): update dependency @edx/frontend-platform to v7.1.2 2024-03-18 07:30:38 +00:00
renovate[bot]
b7f2a1f689 fix(deps): update dependency @edx/frontend-component-footer to v13.0.4 2024-03-18 05:02:34 +00:00
renovate[bot]
2416782eca fix(deps): update dependency qs to v6.12.0 2024-03-11 07:03:49 +00:00
renovate[bot]
5d0a428bdf fix(deps): update react-router monorepo to v6.22.3 2024-03-11 07:02:50 +00:00
renovate[bot]
fe4909ce07 fix(deps): update dependency @edx/frontend-platform to v7.1.1 2024-03-11 04:24:44 +00:00
renovate[bot]
9d61e836a5 fix(deps): update dependency @edx/frontend-component-footer to v13.0.3 2024-03-11 04:24:18 +00:00
renovate[bot]
891e28ec59 chore(deps): update dependency @testing-library/jest-dom to v6 2024-03-04 16:55:48 +00:00
ayeshoali
5ec4893f47 fix: fixed text appearance 2024-03-04 17:36:37 +05:00
renovate[bot]
c5c0cf5392 fix(deps): update react-router monorepo to v6.22.2 2024-03-04 06:17:55 +00:00
Saad Yousaf
350c2080ec fix: add correct strings for new course update notifications 2024-02-27 13:04:17 +05:00
renovate[bot]
44b718ad02 fix(deps): update dependency core-js to v3.36.0 2024-02-26 06:37:11 +00:00
renovate[bot]
97333ada47 fix(deps): update react-router monorepo to v6.22.1 2024-02-19 06:43:12 +00:00
ayesha waris
3467534bc7 feat: make notification channel headings clickable in notification (#983)
* feat: make notification channel headings clickable in notification preferences

* chore: refactoring the code for readability according to ESLint

* refactor: onChannelToggle updated for readability

* refactor: onChannelToggle updated

* refactor: further simplified onChannelToggle

* perf: updated onChannelToggle to improve performance

* fix: fixed lint error

---------

Co-authored-by: eemaanamir <eemaan.amir@gmail.com>
2024-02-07 15:56:27 +05:00
sundasnoreen12
5f43f945bb Merge pull request #989 from openedx/sundas/INF-1243
test: added test cases to change toggle based on channel
2024-02-07 14:45:46 +05:00
Ahtisham Shahid
0ee0f41f3e Merge pull request #988 from openedx/ahtisham/INF-1242
feat: added notification title for contentReported
2024-02-06 14:13:06 +05:00
renovate[bot]
35b66ae38b fix(deps): update react-router monorepo to v6.22.0 2024-02-05 13:10:30 +00:00
renovate[bot]
361e14c980 fix(deps): update dependency @edx/frontend-platform to v7.1.0 2024-02-05 07:54:43 +00:00
Omar Al-Ithawi
8e967fa3bf feat: tutor-mfe compatiblilty for atlas pull (#987)
- install atlas
 - remove `--filter` to pull all languages by default
 - use ATLAS_OPTIONS to allow custom `--filter`
 - include frontend-platform in `atlas pull`

Refs: FC-0012 OEP-58
2024-02-02 09:33:00 -05:00
sundasnoreen12
67feee5e0b refactor: fix reveiw issues 2024-02-01 19:43:27 +05:00
sundasnoreen12
837ac4e635 test: added test cases to change toggle based on channel 2024-02-01 16:47:02 +05:00
Ahtisham Shahid
5c6ddd2888 feat: added notification title for contentReported 2024-01-31 13:18:10 +05:00
Brian Smith
7ef1b5b92d chore(deps): update paragon and frontend-build to openedx scope (#982) 2024-01-29 10:52:37 -05:00
edX requirements bot
672d39f99c chore: update browserslist DB (#978)
Co-authored-by: abdullahwaheed <abdullahwaheed@users.noreply.github.com>
2024-01-23 15:20:54 +05:00
edx-transifex-bot
08a0d8e30b chore(i18n): update translations (#977)
Co-authored-by: Jenkins <sre+jenkins@edx.org>
2024-01-23 15:00:21 +05:00
renovate[bot]
208c7a1ada fix(deps): update react-router monorepo to v6.21.3 2024-01-22 07:57:21 +00:00
renovate[bot]
afe2a754bd fix(deps): update dependency core-js to v3.35.1 2024-01-22 07:56:21 +00:00
Attiya Ishaque
594ffe4aa9 Merge pull request #971 from openedx/attiya/VAN-1790
feat: add  work experience field
2024-01-19 18:44:25 +05:00
attiyaishaque
8ecdedcdc8 feat: add work experience field 2024-01-19 16:03:17 +05:00
renovate[bot]
11e144dec0 fix(deps): update react-router monorepo to v6.21.2 2024-01-15 07:21:47 +00:00
renovate[bot]
c71e586e64 fix(deps): update dependency redux-saga to v1.3.0 2024-01-15 07:21:36 +00:00
renovate[bot]
d0d2aeed71 fix(deps): update dependency core-js to v3.35.0 2024-01-15 07:20:55 +00:00
renovate[bot]
737833cdeb fix(deps): update dependency classnames to v2.5.1 2024-01-15 05:13:19 +00:00
Bilal Qamar
c00ebc9f64 chore: bumped frontend-platform to v6 (#950)
* chore: bumped frontend-platform to v6

* refactor: updated snapshots

* refactor: updated package-lock

* refactor: bumped frontend-platform version

* refactor: updated package-lock and snapshots

* refactor: updated package-lock
2024-01-11 18:36:13 +05:00
dependabot[bot]
41ee7e9538 chore(deps): bump follow-redirects from 1.15.2 to 1.15.4 (#972)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.2 to 1.15.4.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.2...v1.15.4)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-11 14:01:51 +05:00
renovate[bot]
b7181d5643 fix(deps): update dependency @edx/frontend-component-footer to v12.7.1 2024-01-08 14:39:38 +00:00
renovate[bot]
50e0235a6a fix(deps): update dependency @edx/frontend-component-header to v4.11.1 2024-01-08 06:21:27 +00:00
Dmytro
5166546048 fix: Fixed the display of the selection of available time zones (#896) 2024-01-01 23:07:56 +05:00
renovate[bot]
49d3acd1a1 fix(deps): update dependency @edx/frontend-component-header to v4.11.0 2024-01-01 09:06:06 +00:00
renovate[bot]
cdf799d515 fix(deps): update dependency @edx/frontend-component-footer to v12.7.0 2024-01-01 07:29:31 +00:00
Syed Ali Abbas Zaidi
ed26467f0d feat: migrate enzyme with RTL (#945) 2023-12-26 13:22:06 +05:00
edx-transifex-bot
fea3991915 chore(i18n): update translations (#962)
Co-authored-by: Jenkins <sre+jenkins@edx.org>
2023-12-26 11:37:24 +05:00
renovate[bot]
30ca5f577e fix(deps): update dependency core-js to v3.34.0 2023-12-25 06:51:49 +00:00
renovate[bot]
4047c3c923 fix(deps): update dependency @edx/frontend-component-footer to v12.6.2 2023-12-25 06:51:26 +00:00
renovate[bot]
3d32c8624d fix(deps): update dependency regenerator-runtime to v0.14.1 2023-12-18 07:00:42 +00:00
renovate[bot]
0c70e31655 chore(deps): update dependency @edx/frontend-build to v13.0.14 2023-12-18 07:00:13 +00:00
renovate[bot]
10e1a451f7 fix(deps): update dependency @edx/frontend-component-footer to v12.6.1 2023-12-11 07:34:44 +00:00
renovate[bot]
7f2d3700d7 chore(deps): update dependency @edx/frontend-build to v13.0.12 2023-12-11 07:34:26 +00:00
dependabot[bot]
eb88d9b46d chore(deps-dev): bump @adobe/css-tools from 4.2.0 to 4.3.2 (#951)
Bumps [@adobe/css-tools](https://github.com/adobe/css-tools) from 4.2.0 to 4.3.2.
- [Changelog](https://github.com/adobe/css-tools/blob/main/History.md)
- [Commits](https://github.com/adobe/css-tools/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-08 15:00:28 +05:00
sundasnoreen12
26dbac709f Merge pull request #954 from openedx/sundas/INF-1036
fix: disable app level toggle during api call
2023-12-07 14:18:39 +05:00
sundasnoreen12
d6592a77f9 fix: disable app level toggle during api call 2023-12-06 15:23:06 +05:00
renovate[bot]
91d0feaed9 chore(deps): update actions/checkout action to v4 (#953)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-12-06 13:15:40 +05:00
renovate[bot]
237599c830 fix(deps): update react-router monorepo to v6.20.1 2023-12-04 07:10:49 +00:00
sundasnoreen12
56d0ad3db9 Merge pull request #947 from openedx/sundas/INF-1164
fix: app level toggle is now disabled during api call
2023-11-30 13:20:52 +05:00
sundasnoreen12
c0b504763e fix: app level toggle is now disabled during api call 2023-11-29 22:57:04 +05:00
renovate[bot]
4aa44b89a3 fix(deps): update react-router monorepo to v6.20.0 2023-11-29 13:36:02 +00:00
renovate[bot]
be661b8a27 fix(deps): update dependency @edx/frontend-component-header to v4.10.1 2023-11-27 10:35:20 +00:00
renovate[bot]
3c75052d8b fix(deps): update dependency @edx/frontend-component-footer to v12.6.0 2023-11-27 07:27:16 +00:00
renovate[bot]
229a674469 chore(deps): update dependency @edx/frontend-build to v13.0.8 2023-11-20 11:05:36 +00:00
edx-transifex-bot
2c75c7f790 chore(i18n): update translations (#938)
Co-authored-by: Jenkins <sre+jenkins@edx.org>
2023-11-20 13:08:36 +05:00
renovate[bot]
d595986d0e fix(deps): update dependency core-js to v3.33.3 2023-11-20 08:06:20 +00:00
Mashal Malik
43d8784014 refactor: updated README file to reflect template changes (#893)
* refactor: updated README file to reflect template changes

* refactor: updated README file to reflect template changes

* refactor: updated README file to reflect template changes

* refactor: updated README file to reflect template changes
2023-11-14 12:57:40 +05:00
renovate[bot]
f15b71d20c chore(deps): update dependency @edx/frontend-build to v13.0.5 2023-11-13 07:23:24 +00:00
renovate[bot]
aae79c45bb fix(deps): update dependency core-js to v3.33.2 2023-11-13 07:21:25 +00:00
dependabot[bot]
5233c6aa59 chore(deps): bump @babel/traverse from 7.22.5 to 7.23.2 (#914)
Bumps [@babel/traverse](https://github.com/babel/babel/tree/HEAD/packages/babel-traverse) from 7.22.5 to 7.23.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.23.2/packages/babel-traverse)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-08 17:56:54 +05:00
renovate[bot]
d59c9e38bd chore(deps): update dependency @edx/frontend-build to v13.0.4 2023-11-06 07:42:20 +00:00
renovate[bot]
eb35897bf7 fix(deps): update dependency @edx/frontend-component-footer to v12.5.1 2023-11-06 07:41:03 +00:00
Stanislav
c28b8c6840 fix: Add ID attribute to the main content (#880)
Co-authored-by: Stanislav Lunyachek <lunyachek@MacBook-Pro-M1.local>
2023-11-03 16:17:14 +05:00
Bilal Qamar
1698720aad Revert "refactor: bumped frontend-platform version to v6" (#932)
* Revert "refactor: bumped frontend-platform version to v6 (#910)"

This reverts commit 57a2c7bcb6.

* refactor: updated package-lock
2023-11-01 16:31:22 +05:00
Bilal Qamar
57a2c7bcb6 refactor: bumped frontend-platform version to v6 (#910)
* refactor: bumped frontend-platform version

* refactor: updated paragon and snapshots

* refactor: bumped edx packages

* refactor: bumped frontend-build version

* refactor: bumped frontend-platform version

* refactor: pinned paragon version
2023-10-31 16:58:41 +05:00
renovate[bot]
f6f67a1a80 fix(deps): update dependency @edx/frontend-platform to v5.6.1 2023-10-30 06:32:52 +00:00
renovate[bot]
aec7e1281f fix(deps): update dependency @edx/brand to v1.2.3 2023-10-30 06:31:47 +00:00
edx-transifex-bot
cdf0ee5e4e chore(i18n): update translations (#928)
Co-authored-by: Jenkins <sre+jenkins@edx.org>
2023-10-30 11:26:36 +05:00
Ihor Romaniuk
f0858b7381 fix: trim long text in links in the social networks block (#916) 2023-10-25 15:05:08 +05:00
Helder Sepulveda
5f49bedaa8 fix: Correction to the language code for Português, Italian and German (#903)
fix: Correction to the language code for Português, Italian and German

lowercase code for DE

lowercase code for IT

Co-authored-by: Omar Al-Ithawi <i@omardo.com>
2023-10-23 16:10:24 -04:00
Muhammad Abdullah Waheed
a94f472459 fix: fixed typo of other education which was breaking the response (#919)
* fix: fixed typo of other education which was breaking the response

* fix: fixed typo
2023-10-23 16:10:31 +05:00
renovate[bot]
2fc2d86a44 fix(deps): update dependency @edx/frontend-component-header to v4.8.0 2023-10-23 10:54:56 +00:00
renovate[bot]
899f88ca1e fix(deps): update dependency @edx/frontend-component-footer to v12.5.0 2023-10-23 06:25:12 +00:00
renovate[bot]
399dd04df6 chore(deps): update dependency @edx/frontend-build to v13.0.3 2023-10-23 04:49:09 +00:00
renovate[bot]
46dd8739ab fix(deps): update dependency core-js to v3.33.1 2023-10-23 04:47:14 +00:00
Feanil Patel
5de18e0dba chore: Update to the new version of brand-openedx in the new scope. (#918)
Part of https://github.com/openedx/axim-engineering/issues/23

This updates the brand alias to point to the package at the `openedx`
scope.  This does not impact imports because this package is used via an
alias.
2023-10-20 17:31:08 -04:00
sundasnoreen12
ca3bc9151c Merge pull request #913 from openedx/sundass/INF-1086
fix: removed extra loader while updating preferences
2023-10-19 17:35:56 +05:00
Awais Ansari
9cccf09394 Merge pull request #912 from openedx/aansari/code-refactoring
refactor: removed disable eslint and created channels constant
2023-10-18 16:26:59 +05:00
sundasnoreen12
ced29278d5 test: fixed test cases 2023-10-17 13:27:04 +05:00
sundasnoreen12
bc8197a9af fix: removed extra loader while updating preferences 2023-10-17 13:15:14 +05:00
Awais Ansari
f86677171f refactor: removed disable eslint and created channels constant 2023-10-16 19:49:35 +05:00
renovate[bot]
966b59b70f fix(deps): update dependency @edx/frontend-component-footer to v12.4.0 2023-10-16 07:35:09 +00:00
renovate[bot]
0e6aff7c6f fix(deps): update dependency @edx/frontend-platform to v5.5.4 2023-10-16 07:34:54 +00:00
renovate[bot]
13dbb732e3 fix(deps): update dependency @edx/paragon to v20.46.3 2023-10-16 06:41:16 +00:00
renovate[bot]
b80748aa4c fix(deps): update dependency @edx/frontend-component-header to v4.7.1 2023-10-16 06:39:23 +00:00
Muhammad Abdullah Waheed
f098fe1a3a feat: babel-plugin-react-intl to babel-plugin-formatjs migration (#894)
* feat: babel-plugin-react-intl to babel-plugin-formatjs migration

* fix: upgradfed frontend-build to fix security issue
2023-10-10 17:05:08 +05:00
edx-transifex-bot
a989fabb92 chore(i18n): update translations (#902)
Co-authored-by: Jenkins <sre+jenkins@edx.org>
2023-10-10 16:12:18 +05:00
sundasnoreen12
1257e81781 Merge pull request #901 from openedx/sundas/INF-1086
fix: fixed preferences toggle unpredictable behavior
2023-10-10 11:03:10 +05:00
renovate[bot]
b08890b794 fix(deps): update dependency @edx/frontend-component-header to v4.7.0 2023-10-09 09:33:28 +00:00
sundasnoreen12
7f1c7b86ef fix: fixed preferences toggle unpredictable behaviour 2023-10-09 11:52:58 +05:00
renovate[bot]
a5fd3a7f7e fix(deps): update dependency @edx/frontend-component-footer to v12.3.0 2023-10-09 06:15:44 +00:00
Deborah Kaplan
4513cc8834 feat: remove (long-disabled) coaching functionality (#891)
* Removes the coaching functionality
* no tests are referencing this
* Leaves behind a decision record referencing the creation of the
  coaching functionality (0002-coaching-addition)

FIXES: APER-2408-Remove-Coaching-functionality-from-the-Account-MFE
2023-10-04 18:36:09 +05:00
Mashal Malik
40103a2386 refactor: add @openedx in renovate automate configuration (#886) 2023-10-02 16:39:21 +05:00
edx-transifex-bot
b6c18bb439 chore(i18n): update translations (#888)
Co-authored-by: Jenkins <sre+jenkins@edx.org>
2023-10-02 11:32:15 +05:00
renovate[bot]
ee51939f2d fix(deps): update dependency core-js to v3.33.0 2023-10-02 02:28:08 -04:00
renovate[bot]
fc127ccd98 chore(deps): update dependency @edx/frontend-build to v12.9.17 2023-10-02 02:27:36 -04:00
edx-transifex-bot
22faebac50 chore(i18n): update translations (#872)
Co-authored-by: Jenkins <sre+jenkins@edx.org>
2023-10-02 11:27:02 +05:00
renovate[bot]
5ce3995f5b fix(deps): update dependency regenerator-runtime to v0.14.0 2023-09-25 03:59:00 -04:00
renovate[bot]
e181269703 fix(deps): update dependency @edx/frontend-component-header to v4.6.1 2023-09-25 03:58:25 -04:00
renovate[bot]
5d212ec6b5 fix(deps): update dependency @edx/frontend-platform to v5.4.0 2023-09-25 04:21:30 +00:00
renovate[bot]
363096c4f0 fix(deps): update dependency form-urlencoded to v6.1.4 2023-09-25 00:21:09 -04:00
Dmytro
85c5902559 feat: add toggling for the hardcode support link (for master) (#864)
* feat: add toggling for the hardcode support link (master)

Add a toggling mechanism for the "unlink all social media
accounts" text to show it as a link or text depending on
the MFE env setting.

* fix(deps): update dependency @edx/frontend-platform to v5.3.0

* Revert "fix(deps): update dependency @edx/frontend-platform to v5.3.0" (#870)

This reverts commit 757e446be7.

* feat: add toggling for the hardcode support link (master)

Add a toggling mechanism for the "unlink all social media
accounts" text to show it as a link or text depending on
the MFE env setting.

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Muhammad Abdullah Waheed <42172960+abdullahwaheed@users.noreply.github.com>
2023-09-21 13:28:45 +05:00
edx-transifex-bot
fca6da2df7 chore(i18n): update translations (#877)
Co-authored-by: Jenkins <sre+jenkins@edx.org>
2023-09-19 11:18:14 +05:00
sundasnoreen12
7a74e5d29b Merge pull request #876 from openedx/sundas/INF-1061
fix: removed underline from course name
2023-09-18 13:53:44 +05:00
renovate[bot]
4b94344dd9 fix(deps): update dependency core-js to v3.32.2 2023-09-18 02:04:27 -04:00
renovate[bot]
5245254de5 fix(deps): update dependency form-urlencoded to v6.1.3 2023-09-18 02:04:07 -04:00
SundasNoreen
afda76ff11 fix: removed underline from course name 2023-09-12 16:56:49 +05:00
Muhammad Abdullah Waheed
da52ddd35a fix: bumped frontend-platform to latest version to fix ci issues (#875) 2023-09-11 19:25:24 +05:00
renovate[bot]
bb2aef3878 fix(deps): update dependency @edx/frontend-platform to v5.3.1 2023-09-11 02:38:15 -04:00
renovate[bot]
4d6f76d9b3 fix(deps): update dependency form-urlencoded to v6.1.2 2023-09-11 02:37:54 -04:00
Awais Ansari
869e083a2a Merge pull request #869 from openedx/sundas/INF-1016
feat: Added link to documentation on preferences UI
2023-09-06 16:36:35 +05:00
SundasNoreen
ee876b5c84 refactor: added paragon class 2023-09-05 23:11:27 +05:00
Muhammad Abdullah Waheed
a2b25449de Revert "fix(deps): update dependency @edx/frontend-platform to v5.3.0" (#870)
This reverts commit 757e446be7.
2023-09-05 20:24:23 +05:00
SundasNoreen
16218252f1 feat: Added link to documentation on preferences UI 2023-09-05 13:39:46 +05:00
renovate[bot]
757e446be7 fix(deps): update dependency @edx/frontend-platform to v5.3.0 2023-09-04 03:06:25 -04:00
Mashal Malik
db0f8f80bc refactor: update lock file version (#855) 2023-08-31 12:43:54 +05:00
Jonas Burigo Martins
49bc817f2d feat: Allow disable account deletion (#817)
* feat: allow disable account deletion

* test: add disable account deletion to test case

* style: fix lint errors

* docs: Add ENABLE_ACCOUNT_DELETION to README.rst
2023-08-28 15:40:48 +05:00
Bilal Qamar
c31beeef96 Revert "fix(deps): update dependency @tensorflow-models/blazeface to v0.1.0" (#861)
This reverts commit 09970d7935.
2023-08-28 14:33:52 +05:00
renovate[bot]
09970d7935 fix(deps): update dependency @tensorflow-models/blazeface to v0.1.0 2023-08-28 06:25:25 +00:00
renovate[bot]
d15b0baf74 fix(deps): update dependency @edx/frontend-component-footer to v12.2.1 2023-08-28 06:25:03 +00:00
edx-transifex-bot
b9802a130e chore(i18n): update translations (#856)
Co-authored-by: Jenkins <sre+jenkins@edx.org>
2023-08-28 11:20:41 +05:00
Syed Ali Abbas Zaidi
7c0ea75e21 refactor: remove history pacakge (#853)
* refactor: remove history pacakge

* chore: improve test coverage
2023-08-23 18:21:29 +05:00
edX requirements bot
e1b02de7de chore: update browserslist DB (#850)
Co-authored-by: abdullahwaheed <abdullahwaheed@users.noreply.github.com>
2023-08-23 15:52:19 +05:00
Muhammad Adeel Tajamul
6c4dbc5db0 fix: changed overlay position and updated notification info text (#849) 2023-08-22 10:46:14 +05:00
Awais Ansari
d180626122 Merge pull request #848 from openedx/aansari/INF-1018
fix: moved feedback widget behind env variable
2023-08-21 14:49:29 +05:00
renovate[bot]
a9518b7388 fix(deps): update dependency @edx/frontend-platform to v5.1.0 2023-08-21 05:17:34 +00:00
renovate[bot]
51b18e9c52 chore(deps): update dependency @edx/frontend-build to v12.9.10 2023-08-21 05:17:18 +00:00
Syed Ali Abbas Zaidi
3d98558bf6 feat: upgrade react rotuer to v6 (#793)
* feat: upgrade react rotuer to v6

* fix: test cases

* build: update header and footer

* refactor: update jumpnav test case
2023-08-18 12:29:07 +05:00
edX requirements bot
e7e7f518bf chore: update browserslist DB (#840)
Co-authored-by: abdullahwaheed <abdullahwaheed@users.noreply.github.com>
2023-08-17 11:38:51 +05:00
Awais Ansari
cb06c8778a fix: notification courses failed test cases 2023-08-16 17:42:32 +05:00
Awais Ansari
ab5c205a7f fix: moved feedback widget behind env variable 2023-08-15 19:00:29 +05:00
edx-transifex-bot
2f85902d2c chore(i18n): update translations (#843)
Co-authored-by: Jenkins <sre+jenkins@edx.org>
2023-08-15 17:43:34 +05:00
renovate[bot]
b75e78bdda fix(deps): update dependency @edx/frontend-platform to v4.6.1 2023-08-14 14:01:36 +00:00
renovate[bot]
182a0251a4 fix(deps): update dependency @edx/paragon to v20.46.2 2023-08-14 02:14:20 -04:00
renovate[bot]
89881c64a6 fix(deps): update dependency @edx/frontend-component-header to v4.5.0 2023-08-14 02:13:51 -04:00
renovate[bot]
7321e2a159 chore(deps): update dependency @edx/frontend-build to v12.9.4 2023-08-14 00:22:28 -04:00
Awais Ansari
9b45aa3bc9 Merge pull request #842 from openedx/aansari/INF-990
style: change feedback widget location
2023-08-08 13:59:57 +05:00
Awais Ansari
dfadac08d3 style: change feedback widget location 2023-08-08 13:46:02 +05:00
Awais Ansari
2e87f0bd9f Merge pull request #839 from openedx/aansari/INF-990
feat: remove other channels then web preferences
2023-08-08 13:02:49 +05:00
Awais Ansari
7f8086545c test: define lightningjs in window object 2023-08-07 15:00:05 +05:00
Awais Ansari
de6e3c2010 feat: add feedback widget for notification preferences 2023-08-04 15:54:11 +05:00
edX requirements bot
eb6d0125c6 chore: update browserslist DB (#790)
Co-authored-by: abdullahwaheed <abdullahwaheed@users.noreply.github.com>
2023-08-04 15:52:16 +05:00
Awais Ansari
e8f754c10b style: update the notification preferences page style according to figma 2023-08-03 18:01:13 +05:00
Awais Ansari
a72bbf2f58 feat: remove other channels then web preferences 2023-08-03 16:15:47 +05:00
renovate[bot]
c465f51e66 chore(deps): update dependency @testing-library/jest-dom to v5.17.0 2023-07-31 00:24:18 -04:00
renovate[bot]
599e658742 chore(deps): update dependency @edx/frontend-build to v12.9.3 2023-07-31 00:24:02 -04:00
Omar Al-Ithawi
929a669cad feat: include paragon in atlas pull (#833)
This pull request is part of the [FC-0012 project](https://openedx.atlassian.net/l/cp/XGS0iCcQ) which is sparked by the [Translation Infrastructure update OEP-58](https://open-edx-proposals.readthedocs.io/en/latest/architectural-decisions/oep-0058-arch-translations-management.html#specification).
2023-07-25 18:23:08 +05:00
renovate[bot]
503b8b5176 chore(deps): update dependency @edx/frontend-build to v12.9.2 2023-07-24 07:57:00 +00:00
renovate[bot]
d2aa727c12 fix(deps): update dependency @edx/frontend-component-header to v4.4.4 2023-07-24 03:51:58 -04:00
Bilal Qamar
d66dcecd2f feat: update react & react-dom to v17 (#803)
* feat: update react & react-dom to v17

* feat: update react & react-dom to v17

* build: update lock file

* refactor: updated edx packages

* refactor: removed unnecessary lint disable

---------

Co-authored-by: mashal-m <mashal.malik@arbisoft.com>
2023-07-18 12:28:32 +05:00
edx-transifex-bot
80d0c44b40 chore(i18n): update translations (#829)
Co-authored-by: Jenkins <sre+jenkins@edx.org>
2023-07-17 17:53:27 +05:00
dependabot[bot]
0e5cd30d01 chore(deps): bump tough-cookie from 4.1.2 to 4.1.3 (#820)
Bumps [tough-cookie](https://github.com/salesforce/tough-cookie) from 4.1.2 to 4.1.3.
- [Release notes](https://github.com/salesforce/tough-cookie/releases)
- [Changelog](https://github.com/salesforce/tough-cookie/blob/master/CHANGELOG.md)
- [Commits](https://github.com/salesforce/tough-cookie/compare/v4.1.2...v4.1.3)

---
updated-dependencies:
- dependency-name: tough-cookie
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-17 10:49:01 +05:00
renovate[bot]
56b9fe3998 fix(deps): update dependency @edx/frontend-component-footer to v12.1.2 2023-07-17 01:08:58 -04:00
renovate[bot]
a22f1298eb chore(deps): update dependency @edx/frontend-build to v12.8.65 2023-07-17 05:08:34 +00:00
dependabot[bot]
b7bd6a2846 chore(deps): bump semver from 5.7.1 to 5.7.2 (#827)
Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-07-17 10:07:10 +05:00
Awais Ansari
a52ab171de Merge pull request #828 from openedx/aansari/INF-952
feat: implement showPreferences flag in notification preferences
2023-07-14 12:54:34 +05:00
Awais Ansari
13a7508f26 test: fix JumpNav test cases 2023-07-13 15:51:45 +05:00
Awais Ansari
a499aa4cc5 feat: implement showPreferences flag in notification preferences 2023-07-13 14:01:03 +05:00
Awais Ansari
e7207878d4 Merge pull request #825 from openedx/aansari/INF-948
refactor: update notEditable to nonEditable in notification preferences
2023-07-11 12:21:24 +05:00
Awais Ansari
47d64c1cf5 refactor: update notEditable to nonEditable in notification preferences 2023-07-10 18:12:11 +05:00
renovate[bot]
c75dc86263 fix(deps): update dependency @edx/frontend-component-header to v4.4.0 2023-07-10 05:01:18 -04:00
Mashal Malik
105a8d1a3c chore: add paragon messages (#818)
* fix(deps): update dependency @edx/paragon to v20.44.0

* chore: add paragon messages

* build: update lock file

* build: update lock file

* build: update lock file

* build: update lock file

* build: update lock file

* build: update lock file

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-07-10 13:59:46 +05:00
renovate[bot]
c74cf52e38 fix(deps): update dependency @edx/frontend-component-footer to v12.1.1 2023-07-10 02:43:35 -04:00
renovate[bot]
5c9b448b14 fix(deps): update dependency core-js to v3.31.1 2023-07-10 00:10:06 -04:00
renovate[bot]
4e95495dcc chore(deps): update dependency @edx/frontend-build to v12.8.60 2023-07-10 00:09:47 -04:00
renovate[bot]
82f86f5fbe fix(deps): update dependency @edx/frontend-component-header to v4.2.3 2023-07-03 10:56:56 +00:00
Awais Ansari
f737d6e158 Merge pull request #814 from openedx/aansari/INF-914
feat: load more courses button in notification courses list
2023-07-03 13:53:09 +05:00
renovate[bot]
f9feb94668 chore(deps): update dependency @edx/frontend-build to v12.8.57 2023-07-03 08:34:25 +00:00
Awais Ansari
112afa7e51 test: add load more courses test case 2023-06-26 19:34:05 +05:00
Awais Ansari
1694ea38ab feat: load more courses button in notification courses list 2023-06-26 18:46:06 +05:00
renovate[bot]
0f3b7caa0f fix(deps): update dependency reselect to v4.1.8 2023-06-19 21:29:42 +00:00
renovate[bot]
a85e1e1e15 fix(deps): update dependency redux-thunk to v2.4.2 2023-06-19 18:42:13 +00:00
renovate[bot]
55d00027a9 fix(deps): update dependency @edx/frontend-component-header to v4.1.0 2023-06-19 12:25:45 +00:00
renovate[bot]
bf012619a6 chore(deps): update dependency @edx/frontend-build to v12.8.54 2023-06-19 06:33:44 +00:00
renovate[bot]
f47795bf40 fix(deps): update dependency @edx/paragon to v20.44.0 (#806)
* fix(deps): update dependency @edx/paragon to v20.44.0

* fix: update snapshots

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: mashal-m <mashal.malik@arbisoft.com>
2023-06-13 19:50:46 +05:00
renovate[bot]
848e7a2f85 fix(deps): update dependency redux-saga to v1.2.3 2023-06-12 14:12:51 +00:00
renovate[bot]
ecc6138833 fix(deps): update dependency core-js to v3.31.0 2023-06-12 11:30:46 +00:00
Muhammad Adeel Tajamul
d6600eb876 test: added tests for notification preferences (#802) 2023-06-12 13:00:52 +05:00
renovate[bot]
bc237de755 chore(deps): update dependency @edx/frontend-build to v12.8.51 2023-06-12 07:38:35 +00:00
Jenkins
89abb51734 chore(i18n): update translations 2023-06-11 17:03:54 -04:00
Muhammad Adeel Tajamul
be8570edac feat: integrate notification preferences api (#794) 2023-06-06 11:04:23 +05:00
renovate[bot]
09cce6802d fix(deps): update dependency @edx/frontend-platform to v4.5.1 2023-06-05 17:17:50 +00:00
renovate[bot]
c164dd7dcf chore(deps): update dependency @edx/frontend-build to v12.8.40 2023-06-05 12:29:18 +00:00
renovate[bot]
00435fb27d fix(deps): update dependency @edx/frontend-platform to v4.5.0 2023-05-29 09:00:15 +00:00
renovate[bot]
0ebaa0b991 chore(deps): update dependency @edx/frontend-build to v12.8.38 2023-05-29 07:54:53 +00:00
Jenkins
0d7b529233 chore(i18n): update translations 2023-05-28 17:03:50 -04:00
renovate[bot]
2c2f7e8e98 fix(deps): update dependency qs to v6.11.2 2023-05-22 12:54:19 +00:00
renovate[bot]
768b8a2417 fix(deps): update dependency @edx/frontend-component-header to v4.0.3 2023-05-22 08:40:26 +00:00
Muhammad Adeel Tajamul
192714629c feat: added notification preference ui (#784) 2023-05-22 06:17:51 +05:00
edX requirements bot
f2f761e8db chore: update browserslist DB (#787)
Co-authored-by: abdullahwaheed <abdullahwaheed@users.noreply.github.com>
2023-05-18 18:37:57 +05:00
renovate[bot]
410aa14b28 fix(deps): update dependency core-js to v3.30.2 2023-05-15 14:10:07 +00:00
renovate[bot]
e857293414 fix(deps): update dependency @edx/paragon to v20.36.0 2023-05-15 10:49:43 +00:00
edX requirements bot
492f911930 chore: update browserslist DB (#775)
Co-authored-by: abdullahwaheed <abdullahwaheed@users.noreply.github.com>
2023-05-11 14:10:36 +05:00
renovate[bot]
0002d84a6c fix(deps): update dependency @edx/frontend-component-header to v4.0.2 2023-05-08 17:20:01 +00:00
renovate[bot]
9b49b38496 chore(deps): update dependency @edx/frontend-build to v12.8.27 2023-05-08 08:37:54 +00:00
renovate[bot]
7cf9294e09 chore(deps): update dependency @edx/browserslist-config to v1.2.0 2023-05-01 12:58:51 +00:00
renovate[bot]
129f73aa4f chore(deps): update dependency @edx/frontend-build to v12.8.16 2023-05-01 08:20:20 +00:00
Adolfo R. Brandes
7bdb93784b Merge pull request #779 from raccoongang/sagirov/tCRIL_GA-58
[FC-0014] update frontend-platform version to v4.2.0
2023-04-26 19:19:15 -03:00
Sagirov Eugeniy
b32c001fec chore: update frontend-platform version to v4.2.0 2023-04-21 14:21:48 +03:00
Omar Al-Ithawi
5f134d7b99 feat: use atlas in make pull_translations (#774)
Changes:
 - Bump frontend-platform to bring intl-imports.js script
 - Move all i18n imports into `src/i18n/index.js` so intl-imports.js can
   override it with latest translations
 - Add `atlas` into `make pull_translations` when `OPENEDX_ATLAS_PULL`
   environment variable is set.
 - Fixed lint rules for frontend-platform@4.1.0

Refs: [FC-0012 project](https://openedx.atlassian.net/l/cp/XGS0iCcQ) implementing Translation Infrastructure OEP-58.
2023-04-20 12:26:47 -04:00
Bilal Qamar
81b0823632 refactor: reverted platform to v2.6 to avoid dependency issues (#778) 2023-04-20 10:52:46 +05:00
renovate[bot]
95e3af7487 chore(deps): update dependency @edx/frontend-build to v12.8.10 (#776)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-17 14:13:28 +05:00
renovate[bot]
77047bab2a fix(deps): update dependency long to v5.2.3 (#777)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-04-17 14:12:55 +05:00
Jenkins
c1cf6d65de chore(i18n): update translations 2023-04-13 08:44:36 -04:00
renovate[bot]
f626fc2f89 fix(deps): update dependency @edx/frontend-component-footer to v11.7.2 2023-04-11 21:35:12 +00:00
renovate[bot]
f56b1b4530 fix(deps): update dependency @edx/frontend-component-header to v3.7.2 2023-04-11 05:33:19 +00:00
Bilal Qamar
1c423c4b6c feat: upgraded to node v18, added .nvmrc and updated workflows (#759)
* feat: upgraded to node v18, added .nvmrc and updated workflows

* refactor: updated workflow

* refactor: upgraded frontend-platform to v4

* build: updated frontend-build, frontend-platform, component-footer & component-header packages
2023-04-10 12:55:48 +05:00
edX requirements bot
7715b143d2 chore: update browserslist DB (#764)
Co-authored-by: abdullahwaheed <abdullahwaheed@users.noreply.github.com>
2023-04-10 11:03:12 +05:00
renovate[bot]
b200417903 fix(deps): update dependency @edx/frontend-component-header to v3.7.0 2023-04-03 12:43:34 +00:00
renovate[bot]
138d80d57d fix(deps): update dependency @edx/frontend-component-footer to v11.7.0 2023-04-03 10:44:28 +00:00
renovate[bot]
07a33447ad chore(deps): update dependency @edx/frontend-build to v12.7.0 2023-03-27 19:56:09 +00:00
renovate[bot]
4d11d28f96 fix(deps): update dependency @edx/frontend-component-header to v3.6.5 2023-03-27 12:09:12 +00:00
renovate[bot]
e61dc12eab chore(deps): update dependency @edx/frontend-build to v12.6.2 2023-03-24 10:46:08 +00:00
edX requirements bot
2c268d906c chore: update browserslist DB (#756)
Co-authored-by: abdullahwaheed <abdullahwaheed@users.noreply.github.com>
2023-03-22 17:23:44 +05:00
renovate[bot]
0acaebe067 fix(deps): update dependency qs to v6.11.1 2023-03-20 10:36:09 +00:00
renovate[bot]
9ea109f705 fix(deps): update dependency @edx/paragon to v20.28.5 2023-03-20 07:13:38 +00:00
Jenkins
be2964562f chore(i18n): update translations 2023-03-19 17:03:40 -04:00
Yoiber
6d7bf1b878 chore(i18n): add more languages (#747)
* chore(i18n): add more languages

* chore(i18n): Pylint fixes
2023-03-09 18:45:20 +05:00
renovate[bot]
3a7c963f3c fix(deps): update dependency @edx/paragon to v20.28.4 2023-03-07 01:06:59 +00:00
renovate[bot]
3728928a6d fix(deps): update dependency @edx/frontend-component-header to v3.6.4 2023-03-06 12:45:10 +00:00
Mashal Malik
596eeee59d refactor: remove unused tranisfex v2 url (#755) 2023-03-03 17:51:17 +05:00
Eugene Dyudyunov
ae2f8a384f feat: make password reset support URL configurable (#745)
* feat: make password reset support URL configurable

Replace the hardcoded `support.edx.org` value with the one from the env vars.

* fix: linting error
2023-03-02 11:46:32 +05:00
Sarina Canelake
ffe0989969 Merge pull request #750 from openedx/update-readme
docs: Remove old maintaining team from README
2023-02-28 09:56:10 -05:00
renovate[bot]
53eeb26e28 fix(deps): update dependency redux to v4.2.1 2023-02-27 16:05:10 +00:00
Feanil Patel
0325d2a7f6 Update standard workflow files. (#751)
* build: Creating a missing workflow file `self-assign-issue.yml`.

The .github/workflows/self-assign-issue.yml workflow is missing or needs an update to stay in
sync with the current standard for this workflow as defined in the
`.github` repo of the `openedx` GitHub org.

* build: Creating a missing workflow file `add-remove-label-on-comment.yml`.

The .github/workflows/add-remove-label-on-comment.yml workflow is missing or needs an update to stay in
sync with the current standard for this workflow as defined in the
`.github` repo of the `openedx` GitHub org.

* build: Updating a missing workflow file `add-depr-ticket-to-depr-board.yml`.

The .github/workflows/add-depr-ticket-to-depr-board.yml workflow is missing or needs an update to stay in
sync with the current standard for this workflow as defined in the
`.github` repo of the `openedx` GitHub org.
2023-02-27 13:46:43 +05:00
edX requirements bot
b83d568dfb chore: update browserslist DB (#752)
Co-authored-by: abdullahwaheed <abdullahwaheed@users.noreply.github.com>
2023-02-27 11:35:54 +05:00
edX requirements bot
2f839b3362 chore: update browserslist DB (#744)
Co-authored-by: abdullahwaheed <abdullahwaheed@users.noreply.github.com>
2023-02-24 13:30:19 -08:00
Sarina Canelake
bfc655d6ab docs: Remove old maintaining team from README 2023-02-22 18:35:02 -05:00
renovate[bot]
bd63ea9484 fix(deps): update dependency @edx/frontend-component-header to v3.6.3 2023-02-20 19:04:27 +00:00
renovate[bot]
6c825625fb fix(deps): update dependency @edx/frontend-component-footer to v11.6.3 2023-02-20 13:02:24 +00:00
Jenkins
be59c2da90 chore(i18n): update translations 2023-02-12 16:03:37 -05:00
edX requirements bot
8a4e6730a8 chore: update browserslist DB (#741)
Co-authored-by: abdullahwaheed <abdullahwaheed@users.noreply.github.com>
2023-02-07 18:47:58 +05:00
Jenkins
6053475c6f chore(i18n): update translations 2023-02-05 16:03:36 -05:00
edX requirements bot
ed1cfc28aa chore: update browserslist DB (#728)
Co-authored-by: abdullahwaheed <abdullahwaheed@users.noreply.github.com>
2023-02-01 14:29:59 +05:00
dependabot[bot]
1637aea0fa chore(deps): bump cookiejar from 2.1.3 to 2.1.4 (#737)
Bumps [cookiejar](https://github.com/bmeck/node-cookiejar) from 2.1.3 to 2.1.4.
- [Release notes](https://github.com/bmeck/node-cookiejar/releases)
- [Commits](https://github.com/bmeck/node-cookiejar/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-01 14:15:38 +05:00
Emad Rad
3c5aa05b48 Feature: Persian language Support (#666) 2023-02-01 11:36:29 +05:00
renovate[bot]
5a1dc5a992 fix(deps): update dependency form-urlencoded to v6 (#478)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-01-31 14:27:54 +05:00
dependabot[bot]
ccbf8201a0 chore(deps): bump json5 from 1.0.1 to 1.0.2 (#727)
Bumps [json5](https://github.com/json5/json5) from 1.0.1 to 1.0.2.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v1.0.1...v1.0.2)

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-31 14:12:51 +05:00
Jenkins
34be9f681a chore(i18n): update translations 2023-01-29 16:03:37 -05:00
Bilal Qamar
cfca229500 chore: update dependency @edx/frontend-build to v12.4.19 (#738) 2023-01-26 11:50:43 +05:00
renovate[bot]
d2396a8162 fix(deps): update dependency @edx/frontend-component-footer to v11.6.1 2023-01-23 13:52:01 +00:00
renovate[bot]
b630514906 chore(deps): update dependency @edx/frontend-build to v12.4.16 2023-01-23 09:53:56 +00:00
Jenkins
f8dbbf7757 chore(i18n): update translations 2023-01-22 16:08:39 -05:00
renovate[bot]
8eb26db72d fix(deps): update dependency redux to v4.2.0 2023-01-16 14:06:10 +00:00
renovate[bot]
499cbeaa62 fix(deps): update dependency @edx/paragon to v20.27.0 2023-01-16 09:11:35 +00:00
renovate[bot]
4b103161b9 fix(deps): update dependency qs to v6.11.0 2023-01-09 14:37:08 +00:00
renovate[bot]
fb8e4829c3 fix(deps): update dependency @edx/paragon to v20.26.3 2023-01-09 10:31:51 +00:00
renovate[bot]
4a81220aa3 fix(deps): update dependency @edx/brand to v1.2.0 2023-01-09 07:56:11 +00:00
renovate[bot]
85a0d751c0 fix(deps): update dependency prop-types to v15.8.1 2023-01-02 12:32:44 +00:00
renovate[bot]
859195be94 fix(deps): update dependency core-js to v3.27.1 2023-01-02 09:25:12 +00:00
renovate[bot]
8499002072 fix(deps): update dependency @edx/paragon to v20.25.0 2022-12-26 11:13:49 +00:00
renovate[bot]
6e85a2e1d9 fix(deps): update dependency @edx/frontend-component-header to v3.6.0 2022-12-26 08:37:21 +00:00
Jenkins
e4b4197a9a chore(i18n): update translations 2022-12-25 16:08:48 -05:00
renovate[bot]
60f34bf2ae chore(deps): update dependency @edx/frontend-build to v12.4.15 2022-12-22 00:44:44 +00:00
Muhammad Abdullah Waheed
63afc7d7be chore: auto update browserslist shared workflow (#720)
* chore: used a shared script to update broswerslist DB

* refactor: added required secrets param
2022-12-20 17:36:37 +05:00
renovate[bot]
0aec952fd2 fix(deps): update dependency @edx/frontend-component-footer to v11.6.0 2022-12-19 12:52:40 +00:00
renovate[bot]
f8b8374058 fix(deps): update dependency @edx/paragon to v20.21.5 2022-12-19 09:47:00 +00:00
Shahroz Ahmad
d62bb11a9e fix: added language codes to support affected languages (#708)
* fix: added language codes to support affected languages

* fix: temporary fix to unbreak Banner component on unexpected languages
2022-12-15 15:45:36 +05:00
renovate[bot]
af39ecbc5f fix(deps): update dependency @edx/paragon to v20.21.2 2022-12-12 09:24:49 +00:00
Jenkins
6c2330fc6c chore(i18n): update translations 2022-12-11 16:03:30 -05:00
Bilal Qamar
109da5fa38 feat: updated paragon & frontend-build version (#696)
* refactor: updated frontend-build version

* feat: updated paragon to v20

* refactor: updated snapshots
2022-12-09 14:56:55 +05:00
renovate[bot]
81ac063cf8 fix(deps): update dependency @edx/frontend-component-header to v3.5.0 2022-12-05 13:18:08 +00:00
renovate[bot]
50f2442512 fix(deps): update dependency @edx/frontend-component-footer to v11.5.2 2022-12-05 09:46:55 +00:00
Jenkins
be28f398d6 chore(i18n): update translations 2022-12-04 16:03:31 -05:00
alangsto
cd47862c1d Merge pull request #702 from openedx/alangsto/remove_dob_defaults
fix: prevent users from submitting DOB without update
2022-12-02 15:02:05 -05:00
Alie Langston
847e3e05b0 fix: prevent users from submitting DOB without update 2022-12-02 14:51:23 -05:00
Mashal Malik
e347b3fb23 refactor: migrate off modal paragon depreciation components (#654)
* refactor:  worked on modal paragon depreciation component and changed them into latest paragon modals

* refactor:  migrate off  modal paragon depreciation components

* fix: fix eslint and commit message

* fix: units tests were not working, its fixed

* test: add unit tests in modal

* test: add unit tests in id verfication modal

* refactor: convert test cases from enzyme to react testing library

* refactor: remove empty file
2022-12-01 11:51:33 +05:00
Abdullah Waheed
a6a1d94d92 refactor: updated renovate config to auto update minor and patch versions of edx dependencies 2022-11-30 13:25:15 +00:00
edX requirements bot
e54de547f6 fix: -t flag added in pull translation command (#695) 2022-11-30 16:46:54 +05:00
renovate[bot]
574cfb91c2 fix(deps): update dependency jslib-html5-camera-photo to v3.3.4 2022-11-28 12:07:44 +00:00
renovate[bot]
5bdf0a4978 chore(deps): update dependency @edx/frontend-build to v12.4.0 2022-11-28 08:40:57 +00:00
Jenkins
cd8dee1731 chore(i18n): update translations 2022-11-27 16:03:29 -05:00
Andrew Shultz
0e7d2c048d Merge pull request #694 from openedx/ashultz0/more-coppa-rewording
chore: trivial changes to compliance form text
2022-11-22 12:52:58 -05:00
Andy Shultz
79c4a14f6f chore: trivial changes to compliance form text 2022-11-22 10:06:56 -05:00
Muhammad Abdullah Waheed
de8977347a Merge pull request #692 from openedx/bilalqamar95/dependabot-vulnerabilities
refactor: bumped minimatch, recursive-readdir & loader-utils
2022-11-22 16:01:00 +05:00
renovate[bot]
783f78a9ef fix(deps): update dependency core-js to v3.26.1 2022-11-21 13:28:16 +00:00
Bilal Qamar
c1fa6efb30 refactor: bumped minimatch, recursive-readdir & loader-utils 2022-11-21 16:09:38 +05:00
renovate[bot]
b3cd370d8e fix(deps): update dependency regenerator-runtime to v0.13.11 2022-11-21 10:20:41 +00:00
renovate[bot]
8cb416d142 fix(deps): update dependency @edx/frontend-component-header to v3.4.1 2022-11-15 01:10:31 +00:00
renovate[bot]
dbd4faf558 fix(deps): update dependency @edx/frontend-component-footer to v11.5.1 2022-11-14 22:15:50 +00:00
Jenkins
92c8f17e2a chore(i18n): update translations 2022-11-13 16:03:26 -05:00
Muhammad Abdullah Waheed
c858966035 Supported Transifex languages in Makefile (#641)
* feat: added new translations in Makefile and updated all the translations

* refactor: updated i18n index file to fix language errors
2022-11-08 18:42:27 +05:00
Muhammad Abdullah Waheed
547d55a31f Paragon form component deprecations (#612)
* refactor: removed deprecated paragon components from CoachingToggle and used alternatives

* refactor: removed deprecated paragon components from ConfirmationModal and used alternatives

* refactor: removed deprecations from EditableField and created separate component for SelectField

* refactor: updated DemographicsSection to use new select component

* refactor: removed deprecations from EmailField and used alternatives

* refactor: removed deprecated Input from CoachingConsentForm

* refactor: removed deprecated Input from DemographicsSection

* refactor: removed deprecated Input from SummaryPanel component

* refactor: removed deprecated CheckBox and used Form.CheckBox

* refactor: fixed unit tests

* refactor: changes based on PR reviews

* fix: linting issue
2022-11-08 18:42:09 +05:00
renovate[bot]
f78420511e fix(deps): update dependency react-router-hash-link to v2 (#262)
* fix(deps): update dependency react-router-hash-link to v2

* refactor: update snapshot tests

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: mashal-m <mashal.malik@arbisoft.com>
2022-11-08 13:23:24 +05:00
renovate[bot]
e3ad6e6e54 fix(deps): update dependency form-urlencoded to v4.5.1 (#133)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-11-08 11:45:54 +05:00
renovate[bot]
c6ffc51a5d fix(deps): update dependency @tensorflow/tfjs-converter to v3.21.0 (#408)
* fix(deps): update dependency @tensorflow/tfjs-converter to v3.21.0

* fix: updated package-lock to resolve dependency tree issue

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Bilal Qamar <59555732+BilalQamar95@users.noreply.github.com>
Co-authored-by: Abdullah Waheed <abdullah.waheed@arbisoft.com>
2022-11-08 10:47:14 +05:00
renovate[bot]
9b28469afd fix(deps): update dependency @tensorflow/tfjs-core to v3.21.0 (#409)
* fix(deps): update dependency @tensorflow/tfjs-core to v3.21.0

* fix: updated package-lock to resolve dependency tree issue

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Bilal Qamar <59555732+BilalQamar95@users.noreply.github.com>
Co-authored-by: Abdullah Waheed <abdullah.waheed@arbisoft.com>
2022-11-08 10:17:16 +05:00
renovate[bot]
2bb1388a45 fix(deps): update dependency @fortawesome/react-fontawesome to v0.2.0 2022-11-07 15:15:03 +00:00
renovate[bot]
bf0d1379f6 fix(deps): update dependency long to v5.2.1 2022-11-07 13:56:08 +00:00
renovate[bot]
0d9bc5988d fix(deps): update dependency @edx/frontend-component-footer to v11.4.1 2022-11-07 10:56:46 +00:00
renovate[bot]
3a2075de9c fix(deps): update dependency @edx/frontend-component-footer to v11.3.1 (#672)
* fix(deps): update dependency @edx/frontend-component-footer to v11.3.1

* fix: updated package-lock to resolve dependency tree issue

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Bilal Qamar <59555732+BilalQamar95@users.noreply.github.com>
Co-authored-by: Abdullah Waheed <abdullah.waheed@arbisoft.com>
2022-11-07 12:30:53 +05:00
renovate[bot]
ef6239a807 chore(deps): update dependency enzyme-adapter-react-16 to v1.15.7 2022-11-07 07:19:43 +00:00
renovate[bot]
8cb9df6d89 fix(deps): update dependency @edx/paragon to v19.25.3 2022-10-31 13:28:35 +00:00
renovate[bot]
fa856b54bf fix(deps): update dependency @edx/frontend-component-header to v3.3.0 2022-10-31 10:16:07 +00:00
Muhammad Abdullah Waheed
e9f6acff27 Merge pull request #673 from openedx/abdullahwaheed/eslint-issues-fix
fix: migrated functions to arrow functions to fix linting
2022-10-24 18:02:37 +05:00
Abdullah Waheed
dc26da9cc1 fix: migrated functions to arrow functions to fix linting 2022-10-24 15:53:58 +05:00
renovate[bot]
9dd05761fd chore(deps): update dependency @edx/frontend-build to v12.3.0 2022-10-24 08:26:58 +00:00
renovate[bot]
0d5b86df57 fix(deps): update dependency regenerator-runtime to v0.13.10 2022-10-17 08:57:06 +00:00
renovate[bot]
d8eb6e9da5 fix(deps): update dependency react-redux to v7.2.9 2022-10-17 08:49:23 +00:00
renovate[bot]
d280062fe3 chore(deps): update dependency @edx/frontend-build to v12.0.6 2022-10-10 07:24:33 +00:00
renovate[bot]
f0366a98f4 chore(deps): update dependency @edx/browserslist-config to v1.1.1 2022-10-10 07:16:08 +00:00
Muhammad Abdullah Waheed
9d0577fd93 fix: removed env constants and used them directly (#657) 2022-10-04 18:23:56 +05:00
Muhammad Abdullah Waheed
dba70b557c Automate Browserlist DB Update (#642)
* feat: added cron github action to auto update brwoserlist DB periodically

* feat: added workflow_dispatch for manual testing

* fix: fixed error

* fix: fixed indentation issue

* fix: linting issue
2022-10-04 17:55:30 +05:00
Jenkins
f6840bc202 chore(i18n): update translations 2022-10-02 17:05:49 -04:00
renovate[bot]
1af5f0d179 fix(deps): update dependency classnames to v2.3.2 2022-09-26 12:40:22 +00:00
renovate[bot]
b3a91470f8 fix(deps): update dependency @edx/frontend-component-header to v3.2.1 2022-09-26 12:32:19 +00:00
Jenkins
9b18588e26 chore(i18n): update translations 2022-09-25 17:04:29 -04:00
renovate[bot]
3d2e7dc8a4 fix(deps): update dependency @edx/frontend-component-footer to v11.2.1 2022-09-19 12:29:38 +00:00
renovate[bot]
41f1552f9b chore(deps): update dependency @edx/frontend-build to v12.0.5 2022-09-19 12:22:14 +00:00
Jenkins
ffa8a36ce6 chore(i18n): update translations 2022-09-18 17:01:34 -04:00
alangsto
3505e645e3 Merge pull request #647 from openedx/alangsto/update_dob_language
fix: update dob form language
2022-09-14 16:15:01 -04:00
Alie Langston
88db6084df fix: update dob form language 2022-09-14 15:59:58 -04:00
Sarina Canelake
27c8ab28f3 Merge pull request #643 from openedx/tcril/fix-gh-org-url
Fix github url strings (org edx -> openedx)
2022-09-14 10:19:22 -04:00
renovate[bot]
e01b595a0c fix(deps): update dependency @edx/frontend-platform to v2.6.2 2022-09-12 11:37:36 +00:00
renovate[bot]
0d351f5173 chore(deps): update dependency @edx/frontend-build to v12.0.4 2022-09-12 11:29:56 +00:00
Jenkins
03dbfb5670 chore(i18n): update translations 2022-09-11 17:01:42 -04:00
Sarina Canelake
1e17fc9e32 fix: update path to .github workflows to read from openedx org 2022-09-10 18:05:35 -04:00
Sarina Canelake
da912f1e35 fix: fix github url strings (org edx -> openedx) 2022-09-07 08:57:12 -04:00
renovate[bot]
fb0f832832 fix(deps): update dependency @edx/frontend-component-header to v3.2.0 2022-09-05 18:05:19 +00:00
renovate[bot]
870dd631bb fix(deps): update dependency @edx/frontend-component-footer to v11.2.0 2022-09-05 17:57:37 +00:00
Jenkins
5b9f251e93 chore(i18n): update translations 2022-09-04 17:01:50 -04:00
renovate[bot]
98c14bbd3b chore(deps): update dependency @testing-library/jest-dom to v5.16.5 2022-08-29 12:14:47 +00:00
renovate[bot]
00ce8b927f fix(deps): update dependency react-transition-group to v4.4.5 2022-08-29 12:06:54 +00:00
renovate[bot]
ab215cd909 fix(deps): update dependency react-redux to v7.2.8 2022-08-22 11:04:54 +00:00
renovate[bot]
51e7773207 chore(deps): pin dependencies 2022-08-22 10:57:13 +00:00
Jenkins
6f5a1a8aa9 chore(i18n): update translations 2022-08-21 17:02:10 -04:00
Bilal Qamar
deb48fb9d2 Merge pull request #625 from openedx/bilalqamar95/revert-eslint-fragment-change
Reverted changes made to satisfy jsx-no-useless-fragment rule
2022-08-19 18:46:58 +05:00
Bilal Qamar
20b451afb6 refactor: reverted changes made to satisfy jsx-no-useless-fragment rule 2022-08-19 17:25:48 +05:00
alangsto
ad6f812974 Merge pull request #623 from openedx/alangsto/update_dob
feat: add DOB month and year field
2022-08-18 13:59:26 -04:00
Alie Langston
9573516b37 feat: add DOB month and year field 2022-08-18 13:40:30 -04:00
Bilal Qamar
f0d6a92ab2 Upgraded frontend-build version to v12 (#613)
* refactor: upgraded frontend-build version to v12

* refactor: updated frontend-build

* refactor: resolved eslint issues

* refactor: updated package-lock

* refactor: resolved eslint issues after master branch merge
2022-08-16 10:57:43 -04:00
renovate[bot]
dc4d4031e9 fix(deps): update dependency @edx/frontend-component-header to v3.1.3 2022-08-15 09:43:03 +00:00
renovate[bot]
84a5c7aaf1 fix(deps): update dependency @edx/frontend-component-footer to v11.1.2 2022-08-15 09:29:22 +00:00
edx-semantic-release
6ad666342d chore(i18n): update translations 2022-08-14 17:02:20 -04:00
Diana Olarte
1252498872 feat: allow runtime configuration (#603) 2022-08-09 13:44:05 -04:00
renovate[bot]
f9d04e4dd4 fix(deps): update dependency @edx/frontend-platform to v1.15.6 2022-08-08 12:23:08 +00:00
renovate[bot]
c96b9bb77d chore(deps): update dependency @testing-library/react to v12.1.5 2022-08-08 12:13:06 +00:00
edx-semantic-release
f3c672c5ae chore(i18n): update translations 2022-08-07 17:02:31 -04:00
renovate[bot]
63c396c03a chore(deps): update dependency node-forge to 1.3.0 [security] (#567)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2022-07-28 11:33:38 +05:00
Maman Khan
0981857062 fix: removed depreciated codecov package (#608) 2022-07-26 13:27:58 +05:00
Maman Khan
0527c73529 Update packages to remove security vulnerabilities (#605)
* fix: updated vulnerable packages

* fix: fixed failed tests after package update

* fix: linting issues failing ci tests

* fix: package lock update

* fix: snapshot updated to UTC

* fix: missing dependency 'long'
2022-07-26 13:05:31 +05:00
renovate[bot]
5c5dbc369b chore(deps): update dependency lodash to 4.17.21 [security] (#550)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-07-26 11:51:21 +05:00
Maman Khan
196719963f Merge pull request #607 from openedx/maman/add-browserslist-config
build: use shared browserslist configuration
2022-06-29 15:55:16 +05:00
maman
92ee4dfbb9 fix: removed es5 ci check 2022-06-13 18:11:05 +05:00
maman
9d0b3524cb feat: made browserslist setting configurable 2022-06-13 17:48:29 +05:00
edX requirements bot
c39fd332b6 chore!: Dropped support for Node 12 (#594) 2022-05-24 13:45:40 +05:00
Kshitij Sobti
04d515f554 feat: remove usage of scss variables (#601)
Remove usage of scss variables so that the MFE doesn't need scss variables at build-time.
2022-05-16 15:41:48 -04:00
David Joy
f425e9b94f build: adding nvmrc, bumping paragon to latest (#602)
Adding .nvmrc is a nicety.  Bumping paragon to latest is necessary to ensure compatiblity with the edX override header (frontend-component-header-edx).
2022-05-09 16:15:50 -04:00
edX requirements bot
7ec147fe6f feat: Add package-lock file version check (#599)
* feat: Add package-lock file version check

* fix: update name

Co-authored-by: Jawayria <39649635+Jawayria@users.noreply.github.com>
2022-05-09 19:19:39 +05:00
edx-semantic-release
7a8ae85b72 chore(i18n): update translations 2022-04-10 17:08:04 -04:00
Mohammad Ahtasham ul Hassan
0f8f5a1e9a fix: update packagelock (#596) 2022-04-08 15:05:24 +05:00
Usama Sadiq
757a9ac033 Merge pull request #593 from openedx/update-transifex-pull-translations-command
build: update pull_translations command
2022-04-04 14:29:45 +05:00
UsamaSadiq
9efc8d1290 build: update pull_translations command 2022-04-04 14:24:04 +05:00
Usama Sadiq
5f53270148 Merge pull request #591 from openedx/jenkins/transifex-client-migration-bb6390f
fix: transifex migration to new client
2022-04-04 14:06:38 +05:00
Ali Adnan
9716495951 Merge pull request #590 from openedx/aadnan/migrate-reactifex
feat: migrate translations to reactifex
2022-03-18 14:01:24 +05:00
edX requirements bot
f9b29948e7 fix: transifex migration to new client 2022-03-17 08:48:14 -04:00
aliadnan
bededb3912 feat: migrate translations to reactifex 2022-03-17 15:36:53 +05:00
David Joy
bb6390f9ae build: Delete CODEOWNERS (#589)
The community-engineering team no longer exists, so we don't want this CODEOWNERS file anymore.
2022-03-15 14:13:18 -04:00
Jawayria
d2300d2dfd feat: Added support for Node 16 (#588)
* feat: Added support for Node 16

* fix: run both npm 6 and 8
2022-03-15 19:15:11 +05:00
Renovate Bot
a501407907 fix(deps): update dependency qs to v6.10.3 2022-03-14 11:35:58 +00:00
Renovate Bot
88e63cd390 chore(deps): update dependency @testing-library/react to v12.1.4 2022-03-14 11:19:33 +00:00
Sarina Canelake
eaebe6980b Merge DEPR automation workflow
Add DEPR workflow automation
2022-02-24 15:21:56 -05:00
Sarina Canelake
d6efba63ca build: add DEPR workflow automation 2022-02-23 14:37:30 -05:00
edX Transifex Bot
257e425fd9 chore(i18n): update translations 2022-02-20 16:06:24 -05:00
Bianca Severino
02e3364874 Merge pull request #580 from openedx/bseverino/dashboard-language
[MST-1372] Remove dashboard reference from IDV submission confirmation
2022-02-16 14:21:07 -05:00
Bianca Severino
9ae74708fb fix: remove dashboard reference from IDV submitted 2022-02-16 11:40:58 -05:00
alangsto
e946e377c6 Merge pull request #579 from openedx/alangsto/remove_pending_language
fix: remove alert and update language if verified name comes from proctored exam attempt
2022-02-16 09:18:59 -05:00
Alie Langston
5dbe649b2c fix: remove alert if verified name comes from proctored exam attempt 2022-02-16 09:06:19 -05:00
Bianca Severino
2792902975 Merge pull request #577 from openedx/bseverino/idv-camera-language
[MST-1269] Change language referring to government ID
2022-02-14 15:11:57 -05:00
Bianca Severino
6f643070ea fix: change language referring to government ID 2022-02-14 11:45:54 -05:00
edX Transifex Bot
ce946f56b2 chore(i18n): update translations 2022-02-06 16:06:00 -05:00
Bianca Severino
de1e67f68d Merge pull request #573 from openedx/bseverino/remove-idv-experiment
[MST-869] Remove unused IDV experiment paths
2022-02-02 11:13:32 -05:00
Bianca Severino
79001bccd8 feat: remove unused IDV experiment paths 2022-01-27 14:21:43 -05:00
Bianca Severino
963884cc4c Merge pull request #570 from openedx/bseverino/idv-camera-focus
[MST-1075] Prevent focus jump when taking photo
2022-01-24 13:48:53 -05:00
Bianca Severino
e43c1bcc9e fix: prevent focus jump when taking photo 2022-01-20 17:09:15 -05:00
edX Transifex Bot
32cc2c7835 chore(i18n): update translations 2021-12-26 16:14:50 -05:00
Waheed Ahmed
5c39c279f3 Merge pull request #562 from edx/waheed/update-support-link
chore: update help center link for email confirmation
2021-12-07 21:23:40 +05:00
Waheed Ahmed
12bca9b771 chore: update help center link for email confirmation 2021-12-07 19:57:59 +05:00
Renovate Bot
5e10c2bc18 fix(deps): update dependency core-js to v3.19.3 2021-12-06 10:24:35 +00:00
Renovate Bot
f2fe22b8f7 fix(deps): update dependency core-js to v3.19.1 2021-11-29 08:52:52 +00:00
Renovate Bot
d5601a21fd chore(deps): update dependency @testing-library/jest-dom to v5.15.1 2021-11-29 08:30:03 +00:00
edX Transifex Bot
f3bd7a8589 chore(i18n): update translations 2021-11-29 02:04:09 +05:00
Renovate Bot
7643bbd6ba fix(deps): update dependency @edx/frontend-platform to v1.14.1 2021-11-22 12:34:37 +00:00
Renovate Bot
fd1044b531 chore(deps): update dependency es-check to v6.1.1 2021-11-22 12:11:24 +00:00
Uzair Rasheed
f25e5db422 Merge pull request #555 from edx/modify-message-text
refactor: modify activation text message
2021-11-22 13:02:58 +05:00
uzairr
0d569a060b refactor: modify activation text message
alter text of the activation message which appears when an inactive
user tries to delete its profile

VAN-778
2021-11-22 11:47:29 +05:00
Renovate Bot
350016cbe0 chore(deps): update dependency @testing-library/jest-dom to v5.15.0 2021-11-15 10:47:31 +00:00
Renovate Bot
1619dade50 chore(deps): update dependency @edx/frontend-build to v8.1.6 2021-11-15 10:22:11 +00:00
Renovate Bot
dafc34f535 chore(deps): update dependency @edx/frontend-build to v8.1.3 2021-11-08 11:11:25 +00:00
Renovate Bot
9bbab2620c fix(deps): update dependency redux to v4.1.2 2021-11-08 10:43:47 +00:00
edX Transifex Bot
81bef65cc2 chore(i18n): update translations 2021-11-08 02:03:29 +05:00
Michael Roytman
a18daecf8a Merge pull request #541 from edx/MST-1130-remove-verified-name-waffle-flag
feat: Remove Use of Verified Name Enabled Flag
2021-11-01 12:55:52 -04:00
Renovate Bot
449e4c0253 fix(deps): update dependency react-redux to v7.2.6 2021-11-01 10:23:31 +00:00
Renovate Bot
a66ba187ae fix(deps): update dependency @fortawesome/react-fontawesome to v0.1.16 2021-11-01 10:03:19 +00:00
edX Transifex Bot
2d710f7060 chore(i18n): update translations 2021-11-01 02:03:49 +05:00
michaelroytman
e109e5018e feat: Remove Use of Verified Name Enabled Flag
The VERIFIED_NAME_FLAG was added as part https://github.com/edx/edx-name-affirmation/pull/12, [MST-801](https://openedx.atlassian.net/browse/MST-801) in order to control the release of the Verified Name project. It was used for a phased roll out by percentage of users.

The release reached a percentage of 50% before it was observed that, due to the way percentage roll out works in django-waffle, the code to create or update VerifiedName records was not working properly. The code was written such that any change to a SoftwareSecurePhotoVerification model instance sent a signal, which was received and handled by the Name Affirmation application. If the VERIFIED_NAME_FLAG was on for the requesting user, a Celery task was launched from the Name Affirmation application to perform the creation of or update to the appropriate VerifiedName model instances based on the verify_student application signal. However, we observed that when SoftwareSecurePhotoVerification records were moved into the "created" or "ready" status, a Celery task in Name Affirmation was created, but when SoftwareSecurePhotoVerification records were moved into the "submitted" status, the corresponding Celery task in Name Affirmation was not created. This caused VerifiedName records to stay in the "pending" state.

The django-waffle waffle flag used by the edx-toggle library implements percentage rollout by setting a cookie in a learner's browser session to assign them to the enabled or disabled group.
It turns out that the code that submits a SoftwareSecurePhotoVerification record, which moves it into the "submitted" state, happens as part of a Celery task in the verify_student application in the edx-platform. Therefore, we believe that because there is no request object in a Celery task, the edx-toggle code is defaulting to the case where there is no request object. In this case, the code checks whether the flag is enabled for everyone when determining whether the flag is enabled. Because of the percentage rollout (i.e. waffle flag not enabled for everyone), the Celery task in Name Affirmation is not created. This behavior was confirmed by logging added as part of https://github.com/edx/edx-name-affirmation/pull/62.

We have determined that we do not need the waffle flag, as we are comfortable that enabling the waffle flag for everyone will fix the issue and are comfortable releasing the feature to all users. For this reason, we are removing references to the flag.

[MST-1130](https://openedx.atlassian.net/browse/MST-1130)
2021-10-29 11:06:55 -04:00
Waheed Ahmed
1444831833 Merge pull request #540 from edx/waheed/VAN-762
feat: remove `primary/elementary` option from education
2021-10-28 14:13:08 +05:00
Waheed Ahmed
4b4f29ae19 feat: remove primary/elementary option from education
If COPPA compliance feature flag is enabled, remove the `primary/elementary` option
from the level of education dropdown field on edit but keep the value showing for
the users who have already selected it.

VAN-762
2021-10-28 12:34:46 +05:00
Bianca Severino
cbc4123e78 Merge pull request #539 from edx/bseverino/name-change-input
[MST-1074] Remove radio buttons from ID name check if verified name is enabled
2021-10-27 09:00:38 -04:00
Bianca Severino
2c896f77d4 feat: remove radio buttons from ID name check if verified name is enabled 2021-10-26 13:19:14 -04:00
Renovate Bot
112ddf80e6 chore(deps): update dependency @edx/frontend-build to v8.0.6 2021-10-25 09:13:08 +00:00
Renovate Bot
6f2a69acc1 fix(deps): pin dependency lodash.camelcase to 4.3.0 2021-10-25 08:52:11 +00:00
edX Transifex Bot
d2c83b82f7 chore(i18n): update translations 2021-10-25 02:03:33 +05:00
Simon Chen
03501a8125 Merge pull request #535 from edx/schen/fix_access_block
fix: allow learners to access IDV by default
2021-10-21 11:09:16 -04:00
Simon Chen
6e17214476 fix: allow learners to access IDV by default 2021-10-21 10:58:04 -04:00
Michael Roytman
2c6cec7f8c Merge pull request #527 from edx/bseverino/idv-redirect
[MST-1104] Redirect user to original location after IDV
2021-10-20 14:33:29 -04:00
Michael Roytman
f76797cade Merge pull request #526 from edx/mroytman/MST-1099-fix-loading-issues
[MST-1099] Fix Flickering By Removing Unnecessary Re-Renders
2021-10-20 14:29:02 -04:00
Attiya Ishaque
59325bd412 Merge pull request #531 from edx/attiya/VAN-751/put_year_of_birth_behind_flag
feat: [VAN-751] Put user's year of birth behind the feature flag
2021-10-20 15:45:11 +05:00
attiyaIshaque
65a6bc5002 feat: [VAN-751] Put user's year of birth behind the feature flag 2021-10-20 14:56:55 +05:00
edX Transifex Bot
eff28d8b47 chore(i18n): update translations 2021-10-18 02:04:24 +05:00
Bianca Severino
51b758a18f Merge pull request #532 from edx/bseverino/modal-scroll
fix: remove scrollbars from name affirmation modals
2021-10-14 13:54:58 -04:00
Bianca Severino
30bd145bdd fix: remove scrollbars from name affirmation modals 2021-10-14 12:28:53 -04:00
Renovate Bot
1f15802bc6 fix(deps): update dependency core-js to v3.18.2 2021-10-11 05:26:13 +00:00
edX Transifex Bot
5ba476a570 chore(i18n): update translations 2021-10-11 02:09:21 +05:00
Bianca Severino
dfb13c4286 fix: convert duplicate useEffects to custom hook 2021-10-08 11:49:06 -04:00
Bianca Severino
aada46f6eb fix: redirect user to original location after IDV 2021-10-08 10:26:56 -04:00
Ned Batchelder
8c57140640 build: use the organization commitlint check 2021-10-07 13:57:41 -04:00
michaelroytman
9e967ba1ea perf: Fix Flickering By Removing Unnecessary Re-Renders
The use of useState and useEffect hooks in the IDVerificationContextProvider and VerificationContextProvider that were unnecessary was triggering many extra re-renders and inappropriate renders, causing a UI flicker across multiple re-renders.
2021-10-06 16:28:09 -04:00
Bianca Severino
8239209cd7 Merge pull request #525 from edx/bseverino/name-change-pending-message
[MST-1106] Display correct messages for name changes
2021-10-06 11:24:37 -04:00
Bianca Severino
65bb042443 fix: display correct messages for name changes
Removes confusing wording where alerts and help text would state that
the changed name would be used on certificates once approved,
even if it wasn't the name that was selected to be used on certificates.
2021-10-06 10:26:20 -04:00
Michael Roytman
4dd7529799 Merge pull request #524 from edx/mroytman/MST-1099-verified-name-inconsistent-panel
[MST-1099] Fix Flickering in IDV Panel Loading and Inconsistent Panel Loading
2021-10-05 10:15:51 -04:00
michaelroytman
e6684f5048 fix: Fix Flickering in IDV Panel Loading and Inconsistent Panel Loading
[MST-1099](https://openedx.atlassian.net/browse/MST-1099)

Due to a bug in the way the canVerify React state is being set, when the verified name feature is enabled, the code inconsistently loads either the AccessBlocked panel or the ReviewRequirementsPanel when the learner should be allowed to verify. If the learner is ineligible to complete IDV due to their IDV history (e.g. they already have an approved IDV), and the verified name feature is enabled, the learner should be able to verify and should not see the AccessBlocked panel. This code change fixes this bug.

This code change also fixes flickering that occurs when IDV is loading. This code change also includes some copy fixes.
2021-10-04 15:06:41 -04:00
Michael Roytman
e276d6a5c4 Merge pull request #520 from edx/mroytman/MST-1065-IDV-UI-and-copy-changes-verified-name
MST-1065: Add Copy and UI Treatment for Name Affirmation When Enabled
2021-10-04 13:16:37 -04:00
Renovate Bot
36f1d1dbfb fix(deps): update dependency formdata-polyfill to v4.0.10 2021-10-04 07:26:23 +00:00
Renovate Bot
6c324e85fc chore(deps): update dependency @testing-library/react to v12.1.2 2021-10-04 07:07:31 +00:00
edX Transifex Bot
16b5d066ff chore(i18n): update translations 2021-10-04 02:04:23 +05:00
michaelroytman
6e45abbe8b feat: Add Copy and UI Treatment for Name Affirmation When Enabled
[MST-1065](https://openedx.atlassian.net/browse/MST-1065)

When the Name Affirmation feature is enabled, a few panels in the IDV workflow should have different copy to reflect the changes associated with Name Affirmation. This is because entering a new during the IDV flow (GetNameIdPanel) will no longer modify the learner's "Account Name". Furthermore, "Account Name" is an ambiguous term; it's not clear that it refers to the learner's full name on their profile. These copy changes make the panel more generic and remove details about name changes. The SummaryPanel also has a minor copy change to change "Account Name" to "Name".

These code changes also include some UI changes.
2021-10-01 14:35:14 -04:00
Bianca Severino
b3e6335396 Merge pull request #519 from edx/bseverino/idv-failure-message
[MST-1091] Update IDV failure message to be more generic
2021-10-01 10:10:22 -04:00
Bianca Severino
08a2da5459 fix: update IDV failure message to be more generic 2021-09-30 17:34:41 -04:00
Bianca Severino
094361c689 Merge pull request #518 from edx/bseverino/modal-trigger-bug
[MST-1089] Only display name change modal when backend requires verification
2021-09-30 16:48:23 -04:00
Bianca Severino
b7b33ef597 fix: only display name change modal when backend requires verification
Fixes an issue where the name change modal would trigger for any error
related to the "full name" field, rather than just when the name change
requires ID verification.
2021-09-30 16:26:49 -04:00
alangsto
afa808ff5d Merge pull request #516 from edx/alangsto/update_dismissable_alert
fix: approved alert should appear for subsequent approved attempts
2021-09-29 14:36:02 -04:00
Alie Langston
5279f2a9c9 fix: approved alert should appear for subsequent approved attempts 2021-09-29 14:00:30 -04:00
Michael Roytman
4cc00fd7e3 Merge pull request #514 from edx/mroytman/MST-1073-pending-verified-name-display
fix: Display Last Approved Verified Name When Most Recent Verified Name is Pending
2021-09-29 13:03:08 -04:00
alangsto
8815626411 Merge pull request #515 from edx/alangsto/fix_certificate_checkbox
fix: Checkbox incorrectly tied to profile name
2021-09-29 12:57:51 -04:00
Alie Langston
db319b6cdf fix: Checkbox incorrectly tied to profile name
Fixed a bug that incorrectly rendered the certificate preference checkbox upon each page render. The bug caused the 1) the checkboxes to both be selected at the same time, 2) the checkbox to always be selected for the profile name, and 3) wouldn't allow the proper UI changes upon saving a verified name. The checkbox selection works correctly now, and the save button for the verified name field also works as expected.
2021-09-29 09:53:05 -04:00
michaelroytman
50edcb1c50 fix: Display Last Approved Verified Name When Most Recent Verified Name is Pending
When a learner's most recent Verified Name is in the pending state, we should display their most recent approved Verified Name. If such a Verified Name does not exist, do not display any Verified Name. In either case, do not disable or gray out either the Full name or Verified name fields.

This change is being made because we do not want to prevent a learner from editing the Verified name field if their most recent entry is pending. An entry remains in the pending state when a learner has created a Verified name by changing their name on the Account Settings page but has not followed through with submitting their IDV.
2021-09-28 13:07:46 -04:00
David Joy
d6519bc825 fix: update deps and modernize, fix favicon (#513) 2021-09-27 19:50:16 -04:00
Bianca Severino
b54aeb9446 Merge pull request #512 from edx/bseverino/idv-language
[MST-797] Update IDV instructions
2021-09-27 17:01:11 -04:00
Zachary Hancock
bb1bd6e648 fix: null check mostRecentVerifiedName 2021-09-27 14:33:46 -04:00
Bianca Severino
7df9f92dd8 fix: update IDV instructions
Update IDV instructions to be more explicit about
the need for a photo identification card, and provide
a few more examples.
2021-09-27 14:23:36 -04:00
Zach Hancock
3627915985 fix: null check mostRecentVerifiedName 2021-09-27 14:19:51 -04:00
Zachary Hancock
9fe1a04a0a feat: enter name change flow when submitting a new verified name 2021-09-27 11:33:30 -04:00
Zach Hancock
7455821500 fix: missing null check 2021-09-27 10:12:15 -04:00
Renovate Bot
817980be00 fix(deps): update dependency formdata-polyfill to v4.0.8 2021-09-27 08:56:10 +00:00
Michael Roytman
7d4e31f69d Merge pull request #508 from edx/mroytman/MST-1059-verified-name-not-sent-in-IDV
feat: send nameOnAccount as full_name with all IDV submissions when learner has no name change and verified name feature is enabled
2021-09-24 16:16:08 -04:00
Zach Hancock
34dde09ccc style: lint 2021-09-24 14:37:56 -04:00
Zach Hancock
aee4e44f8c feat: submit new verified name 2021-09-24 14:20:00 -04:00
michaelroytman
7381cfd3b6 feat: send nameOnAccount as full_name with all IDV submissions when learner has no name change and verified name feature is enabled
MST-1059: https://openedx.atlassian.net/browse/MST-1059

This code change changes how the full_name field is included in the form data sent during IDV submission.

Before, full_name was only included in the form data when the learner entered a different name in the GetNameIdPanel than what was displayed to them as the default (i.e. their full name on their profile). If a full_name was provided in the request, the server would use the supplied full_name when creating the IDV record and would update the learner's full name on their profile accordingly. If a full_name was not provided in the request, then the server would fall back to the current full name on their profile when creating the IDV record and no change to the full name on their profile would occur.

With the introduction of the Verified Name feature, things have changed. Assuming the feature is enabled, the name displayed to the learner is either the most recent pending or approved verified name or their full name on their profile if the former does not exist. This means that it is no longer correct for the server to create an IDV record using the current full name on the learner's profile when full_name is not provided, which, again, occurs if the learner does not change the name submitted with IDV. This is because, if the learner has a pending or approved verified name, that should be prioritzed over the full name on their profile.

This code change sends the full_name field whether or not the learner has modified it in the IDV flow, if the verified name feature is enabled. This allows the server to create a verified name with whatever value is submitted to it through IDV. The reason we only do this if the feature is enabled is that, when the feature is off, the server will change the learner's profile name to this value, as described above. If we send the idPhotoName on all requests, even ones where the learner does not change the idPhotoName, then the server will record that the full name on the learner's profile has a requested change, even if the name is the same. Their name may not change, but this will pollute the history by being logged as a requested change.
2021-09-24 12:22:19 -04:00
Michael Roytman
1d0bd3986c Merge pull request #505 from edx/mroytman/MST-1016-verified-name-in-account-name-check
feat: include pending or approved verified names in the name field if they exist
2021-09-24 12:03:07 -04:00
michaelroytman
1c0dc36907 feat: include pending or approved verified names in the name field if they exist
MST-1016: https://openedx.atlassian.net/browse/MST-1016

If a learner has a pending or approved verified name, the most recent should take precedence over the profile name on the GetNameIdPanel during the "Account Name Check". If the learner has no pending or approved verified names, then the learner's profile name should be used instead.
2021-09-23 11:44:51 -04:00
Bianca Severino
ac0ab9daea Merge pull request #504 from edx/bseverino/pending-name
[MST-1057] Display pending name when in "submitted" state
2021-09-21 11:43:08 -04:00
Bianca Severino
0d45d17cd3 fix: display pending name when in submitted state 2021-09-21 10:18:49 -04:00
Renovate Bot
a6d265b885 chore(deps): update dependency @testing-library/react to v12.1.0 2021-09-20 08:18:04 +00:00
Renovate Bot
3508bc6c34 fix(deps): update dependency @edx/frontend-platform to v1.12.7 2021-09-20 08:00:12 +00:00
edX Transifex Bot
28de621fc7 chore(i18n): update translations 2021-09-20 02:04:23 +05:00
Bianca Severino
e6df5e77ae Merge pull request #486 from edx/bseverino/change-name
[MST-803] Add name change modal
2021-09-16 10:09:47 -04:00
Bianca Severino
8bc5c1fae8 feat: add name change modal 2021-09-15 14:15:43 -04:00
Bianca Severino
6e48c9d2d1 Merge pull request #497 from edx/bseverino/certificate-name
feat: add certificate preference to name fields
2021-09-15 13:37:23 -04:00
Andrew Shultz
d3469d648f Merge pull request #500 from edx/ashultz0/idv_redo_ok
feat: if verified name is on, we can redo ID verification
2021-09-15 11:03:56 -04:00
Bianca Severino
cc65ffc96f feat: add certificate preference to name fields
Adds a checkbox under "Name" and "Verified Name" fields,
signifying which name the user prefers to display on their
certificates. When checking the checkbox, the user can save
this choice along with their name. When unchecking the check-
box, a modal appears, prompting the user to choose a name
to display on their certificate.
2021-09-15 11:01:20 -04:00
Andy Shultz
5640fb95c2 fix: use correct initial state type 2021-09-15 09:36:09 -04:00
Andy Shultz
7bbb889258 feat: if verified name is on, we can redo ID verification
The verified_name endpoint provides is_enabled only it a name exists,
so we have to request the enabled flag separately.

MST-1026
2021-09-13 13:17:14 -04:00
Renovate Bot
53b59231cb fix(deps): update dependency @edx/frontend-platform to v1.12.6 2021-09-13 10:16:42 +00:00
Renovate Bot
8fb25fd89b fix(deps): update dependency formdata-polyfill to v4.0.7 2021-09-13 09:59:52 +00:00
Bianca Severino
15d2bf60f9 Merge pull request #493 from edx/mroytman/MST-954-failure-name-change-additional-changes
fix: Fix Broken Editing of Verified Name Field
2021-09-08 10:57:46 -04:00
Renovate Bot
135826bc52 fix(deps): update react-router monorepo 2021-09-06 08:39:07 +00:00
Renovate Bot
d7251e6aec fix(deps): update dependency react-redux to v7.2.5 2021-09-06 08:18:30 +00:00
edX Transifex Bot
0b0846fb00 fix(i18n): update translations 2021-09-06 02:04:24 +05:00
michaelroytman
b1cd1b1995 fix: Fix Broken Editing of Verified Name Field
This code change fixes a bug where the verified name field was not editable. This was due to the fact that the verified name was not wired up to the Redux store, so draft verified names were not stored in the Redux store.
2021-09-03 11:44:14 -04:00
alangsto
50c468857a Merge pull request #492 from edx/alangsto/pending_alert
feat: add alert and correct messaging for submitted verified name
2021-08-31 15:47:07 -04:00
Alie Langston
c940d3463c feat: add alert and correct messaging for pending verified name
MST-954 (https://openedx.atlassian.net/browse/MST-954)
2021-08-31 15:35:31 -04:00
Michael Roytman
376deba866 Merge pull request #491 from edx/mroytman/MST-954-failure-name-change-display
feat: Change Which VerifiedName Record is Displayed When Most Recent VerifiedName is Denied
2021-08-31 13:52:43 -04:00
michaelroytman
37d0e6e0fb feat: Change Which VerifiedName Record is Displayed When Most Recent VerifiedName is Denied
If a learner requests a profile name change or verified name change that requires ID verification, their ID verification submission may be rejected. This code change changes the verified name that is displayed in this case. If a learner's most recent verified name record has been denied, and the learner has a previously approved verified name, display that. If If a learner's most recent verified name record has been denied, and the learner either has no previously verified name records or no previous verified name records at all, do not show a verified name field.

It also refactors the code to better support UI treatments for other verified name statuses, like pending and submitted.
MST-954 (https://openedx.atlassian.net/browse/MST-954)
2021-08-30 17:05:08 -04:00
Michael Roytman
9261711d4a Merge pull request #490 from edx/mroytman/MST-954-failure-name-change-alert
feat: Add Dismissible Alert Informing Learner of Failed Name Verification
2021-08-30 17:04:24 -04:00
michaelroytman
3e42d42ad7 feat: Add Dismissible Alert Informing Learner of Failed Name Verification
If a learner requests a profile name change or verified name change that requires ID verification, their ID verification submission may be rejected. This code change adds a dismissible error alert to the Account Settings page informing the learner of the failure and redirecting them to a help article about ID verification failures. The local storage key used to determine whether to show the alert or not uses the created time of the name verification object to ensure that subsequent ID verification failures still trigger this alert, even for the same name.

MST-954 (https://openedx.atlassian.net/browse/MST-954)
2021-08-30 13:18:02 -04:00
Simon Chen
7de4edc002 Merge pull request #487 from edx/schen/pending_name
feat: Make VerifiedName not editable and show proper icon when the Name is in pending status
2021-08-30 12:58:39 -04:00
Simon Chen
2936498b02 feat: Make VerifiedName not editable and show proper icon when the Name is in pending status 2021-08-30 11:50:45 -04:00
Renovate Bot
44d26c444b chore(deps): update dependency husky to v7.0.2 2021-08-30 09:28:00 +00:00
Michael Roytman
b1c1c6502d Merge pull request #485 from edx/mroytman/MST-956-pending-name-change-alert
feat: Replace use of verified_name API with verified_name_history API.
2021-08-27 13:50:04 -04:00
michaelroytman
21dda3f25b feat: Replace use of verified_name API with verified_name_history API.
This replaces the use of the verified_name API ({LMS_BASE}/api/edx_name_affirmation/v1/verified_name) with the verified_name_history API ({LMS_BASE}/api/edx_name_affirmation/v1/verified_name/history). The verified_name API returns the most recent verified name for a learner. However, we need access to the entire learner's verified name history in order to view verified name records with other statuses (i.e. pending, submitted, denied) as well as to be able to display previously verified names in the event that the most recent verified name record is denied.

MST-954 (https://openedx.atlassian.net/browse/MST-954)
2021-08-27 12:24:21 -04:00
Andrew Shultz
f87b5040a3 Merge pull request #484 from edx/ashultz0/verified-status
switch account status page to use verified name status
2021-08-26 11:38:52 -04:00
Andy Shultz
0dc1df07d4 feat: gate render verified name success message outside of function
this frees the function from doing logic we've already done elsewhere
2021-08-26 11:13:43 -04:00
Andy Shultz
efa682092f feat: specify shape of verified name object
at least the fields that we are interested in
2021-08-26 11:09:33 -04:00
Andy Shultz
0d9e6f8b87 feat: do not smear verified name fields into general fields
smooshing an unattached field name status into the general set of
fields is a recipie for future bugs
2021-08-26 10:08:28 -04:00
Andy Shultz
26d2b50859 feat: update verified name display to look at status not is_verified bool
note that there is no way to trigger pending display currently without
code manipulation because the verified name endpoint in use only
returns approved names
2021-08-26 10:07:13 -04:00
Simon Chen
3eb63cd624 Merge pull request #482 from edx/schen/fix_name_error
fix: Should not display error message when the account name matches the ID name
2021-08-23 11:06:55 -04:00
Simon Chen
ba0774c5c4 fix: Should not display error message when the account name matches the ID name
MST-990, we should fix the problem where the ID name error message still show up with Account name filled in or Verified name filled in.
2021-08-23 11:03:19 -04:00
Simon Chen
ac47d0b180 Merge pull request #481 from edx/renovate/formdata-polyfill-4.x
fix(deps): update dependency formdata-polyfill to v4
2021-08-23 09:26:25 -04:00
Simon Chen
02038b8ac9 Merge pull request #471 from edx/renovate/edx-frontend-build-8.x
chore(deps): update dependency @edx/frontend-build to v8
2021-08-23 09:16:02 -04:00
Simon Chen
458f9f7e3d Merge pull request #477 from edx/renovate/es-check-6.x
chore(deps): update dependency es-check to v6
2021-08-23 09:15:10 -04:00
Simon Chen
c7d9c270f9 Merge pull request #479 from edx/schen/idv_use_verified_name
feat: Update IDV workflow to use the VerifiedName
2021-08-23 09:13:12 -04:00
Renovate Bot
d0ecbbfb8a fix(deps): update dependency formdata-polyfill to v4 2021-08-23 08:24:31 +00:00
Renovate Bot
22db0d9202 chore(deps): update dependency es-check to v6 2021-08-23 08:19:01 +00:00
Renovate Bot
34a142f55f chore(deps): update dependency @edx/frontend-build to v8 2021-08-23 08:17:25 +00:00
Renovate Bot
6e00915f98 fix(deps): update dependency @edx/frontend-platform to v1.12.4 2021-08-23 08:09:49 +00:00
Simon Chen
4cfa1707de feat: Update IDV workflow to use the VerifiedName instead of the profile name
If the Verified Name feature is turned on, and the user do have a VerifiedName, use that name on the Account Name Check page of the IDV flow instead of account profile name
2021-08-20 11:08:43 -04:00
Renovate Bot
a721887886 fix(deps): update dependency @edx/frontend-platform to v1.12.3 2021-08-16 06:59:33 +00:00
Renovate Bot
9cdbb93bf3 fix(deps): update dependency @edx/frontend-component-footer to v10.1.6 2021-08-16 06:41:37 +00:00
stvn
f9e7519e26 merge(#454): renovate/actions-setup-node-2.x
commits
=======
- chore(deps): update actions/setup-node action to v2
2021-08-10 10:08:50 -07:00
Renovate Bot
ff8d5a4d09 chore(deps): update actions/setup-node action to v2 2021-08-10 07:29:09 +00:00
stvn
f14c71c4fb merge(#470): renovate/codecov-codecov-action-2.x
commits
=======
- chore(deps): update codecov/codecov-action action to v2
2021-08-09 21:41:29 -07:00
Renovate Bot
43caac8430 chore(deps): update codecov/codecov-action action to v2 2021-08-10 00:38:25 +00:00
stvn
ee1ecb8ab9 merge(#354): adzuci/update-owner-to-tnl
commits
=======
- docs: remove owner from openedx.yml
2021-08-09 16:40:36 -07:00
Adam Blackwell
020aa84986 docs: remove owner from openedx.yml 2021-08-09 16:27:51 -07:00
Renovate Bot
24459daf6d fix(deps): update font awesome 2021-08-09 10:31:52 +00:00
Renovate Bot
587533703e fix(deps): update dependency redux to v4.1.1 2021-08-09 10:14:52 +00:00
Renovate Bot
866746d1c6 fix(deps): update dependency @edx/frontend-platform to v1.11.3 2021-08-02 19:45:00 +00:00
Albert (AJ) St. Aubin
4c618a55c0 Merge pull request #472 from edx/aj/fix_demographics
fix: Demographics section will now show when enabled
2021-08-02 14:42:50 -04:00
Albert (AJ) St. Aubin
c9f6cf708e fix: Demographics section will now show when enabled 2021-08-02 14:29:39 -04:00
edX Transifex Bot
5f314ee65f fix(i18n): update translations 2021-08-02 02:04:23 +05:00
Michael Roytman
7e35b23b36 Merge pull request #466 from edx/mroytman/MST-800-verified-name-field
[MST-800] Add Verified Name field and success alert to the Account Settings page
2021-07-30 11:02:57 -04:00
Michael Roytman
842bd11d89 feat: Add Verified Name field and success alert to the Account Settings page
MST-800: https://openedx.atlassian.net/browse/MST-800

This code change adds a Verified Name editable field to the Account Settings page. When the verified name API in the LMS returns that a learner has a verified name, and that the name affirmation feature flag is turned on, the Account Settings page displays the Verified Name field. If the learner's name is verified (i.e. it is not pending verification), then a green checkmark is displayed to the right of the field label.

The first time the learner visits the Account Settings page after verification, a dismissible alert is displayed indicating that their vreified name was verified. Whether the alert has been dismissed is stored in localStorage, and the alert no longer reappears after it is dismissed.
2021-07-30 10:16:40 -04:00
Renovate Bot
b1e11dfb36 chore(deps): update dependency @edx/frontend-build to v7.1.0 2021-07-26 07:27:17 +00:00
Renovate Bot
aa57b69924 chore(deps): update dependency codecov to v3.8.3 2021-07-26 07:15:09 +00:00
stvn
e7769b37e9 merge(#461): renovate/husky-7.x
commits
=======
- chore(deps): update dependency husky to v7
2021-07-21 11:32:53 -07:00
Renovate Bot
fcc7b26c28 chore(deps): update dependency husky to v7 2021-07-19 07:55:01 +00:00
Renovate Bot
16d844528d fix(deps): update dependency jslib-html5-camera-photo to v3.1.8 2021-07-19 07:49:54 +00:00
Renovate Bot
223234f623 chore(deps): update dependency @edx/frontend-build to v7.0.6 2021-07-19 07:37:33 +00:00
Renovate Bot
d8a1c0ca8c fix(deps): update dependency @edx/paragon to v16.1.0 2021-07-12 07:08:14 +00:00
Renovate Bot
1da8f630eb fix(deps): update dependency @edx/frontend-platform to v1.11.1 2021-07-12 06:55:19 +00:00
Renovate Bot
228eec0afa chore(deps): update dependency @edx/frontend-build to v7.0.3 2021-07-12 06:40:54 +00:00
David Joy
cf62b4b82c fix: upgrade frontend-build to v7 and paragon to v16 (#457)
* build: bumping to Paragon 16.x

Test snapshots needed to be updated because the Hyperlink external link icon is now SVG instead of font-awesome, and because it added some new classes.

* build: bump frontend-build to v7
2021-07-07 16:10:09 -04:00
renovate[bot]
0184c1fa25 chore(deps): update dependency @edx/frontend-build to v6 (#455)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-07-06 14:44:31 -04:00
Renovate Bot
8ed103b2ad chore(deps): update dependency @testing-library/react to v12 2021-07-05 06:59:43 +00:00
Renovate Bot
84a9de44a5 chore(deps): update dependency es-check to v5.2.4 2021-06-28 07:27:56 +00:00
Renovate Bot
84df0a0b3e fix(deps): update dependency redux to v4.1.0 2021-06-21 07:21:17 +00:00
Renovate Bot
a3917ae550 fix(deps): update dependency qs to v6.10.1 2021-06-21 07:09:01 +00:00
Renovate Bot
bfd6a07a2c fix(deps): update dependency memoize-one to v5.2.1 2021-06-21 06:55:37 +00:00
Renovate Bot
1df570989b fix(deps): update dependency classnames to v2.3.1 2021-06-21 06:40:54 +00:00
stvn
b7e433876e merge(#396): renovate/testing-library-react-11.x
commits
=======
- chore(deps): update dependency @testing-library/react to v11
2021-06-17 02:48:11 -07:00
Renovate Bot
1669d577f6 chore(deps): update dependency @testing-library/react to v11 2021-06-15 09:09:28 +00:00
Renovate Bot
d1ca7decce fix(deps): update dependency @edx/paragon to v13.17.5 2021-06-15 09:04:04 +00:00
Renovate Bot
79a43ae713 fix(deps): update dependency @edx/frontend-component-footer to v10.1.5 2021-06-15 08:49:54 +00:00
Renovate Bot
9d0b315714 fix(deps): update dependency @edx/frontend-component-header to v2.3.0 2021-06-14 23:34:42 +00:00
Renovate Bot
4b2bc11378 fix(deps): update dependency @edx/frontend-platform to v1.11.0 2021-06-14 07:10:19 +00:00
Renovate Bot
fc7ce6b91e chore(deps): update dependency @testing-library/jest-dom to v5.14.1 2021-06-14 06:51:50 +00:00
Renovate Bot
39a25fe5bc fix(deps): update font awesome 2021-06-08 07:57:01 +00:00
Renovate Bot
307cb1541b fix(deps): update dependency redux-devtools-extension to v2.13.9 2021-06-07 07:02:48 +00:00
Renovate Bot
03e026ce4e fix(deps): update reactrouter monorepo to v5.2.0 2021-06-05 07:06:40 +00:00
Renovate Bot
a29876aff0 fix(deps): update dependency react-redux to v7.2.4 2021-06-05 06:48:36 +00:00
Renovate Bot
ab77246015 fix(deps): update dependency react-transition-group to v4.4.2 2021-06-05 06:31:19 +00:00
stvn
572b05e7f1 merge(#444): build/renovate
commits
=======
- build(renovate): fix json syntax
2021-06-04 23:05:09 -07:00
stvn
3e4de47ba6 build(renovate): fix json syntax 2021-06-04 22:09:16 -07:00
stvn
6c6cedd422 merge(#442): build/renovate
commits
=======
- build(renovate): be more selective about automerging devDependencies
2021-06-04 13:20:35 -07:00
stvn
43694921ca build(renovate): be more selective about automerging devDependencies
to avoid bumping the major version, besides linters and testers.
2021-06-04 13:15:46 -07:00
Renovate Bot
06ded1e66e fix(deps): update react monorepo to v16.14.0 2021-06-04 02:03:50 +00:00
Renovate Bot
aba1bb3382 fix(deps): update dependency @tensorflow/tfjs-core to v1.7.4 2021-06-04 00:13:07 +00:00
Renovate Bot
d67b880028 fix(deps): update dependency @tensorflow/tfjs-converter to v1.7.4 2021-06-03 21:48:51 +00:00
Renovate Bot
29692add53 chore(deps): update dependency enzyme to v3.11.0 2021-06-03 19:45:17 +00:00
Renovate Bot
7f53bf32ca chore(deps): update dependency codecov to v3.8.2 2021-06-03 19:18:50 +00:00
Renovate Bot
add22d9756 fix(deps): update dependency bowser to v2.11.0 2021-06-03 18:50:57 +00:00
Renovate Bot
bef9bf76fd chore(deps): update dependency husky to v3.1.0 2021-06-03 18:22:21 +00:00
Renovate Bot
bf6b2fb8b8 chore(deps): update dependency @edx/frontend-build to v5.6.14 2021-06-03 17:52:24 +00:00
Renovate Bot
a77cd6d91a chore(deps): update dependency es-check to v5.2.3 2021-06-03 17:21:04 +00:00
stvn
9b3f222191 merge(#441): build/renovate
commits
=======
- build(renovate): remove linters/testers automerge presets
- build(renovate): automerge lockFileMaintenence
- build(renovate): use enableVulnerabilityAlerts preset
- build(renovate): allow unscheduled updates
2021-06-03 09:40:38 -07:00
stvn
90f2ed8393 merge(#440): build/remove-travis
commits
=======
- build(ci): run ci tests via matrix
- build(ci): always run `npm ci` via Makefile
- build(ci): convert travis-ci to github actions
2021-06-03 09:38:51 -07:00
stvn
95c53ad380 build(renovate): remove linters/testers automerge presets
on the assumption that our blanket policy for `devDependencies` will overlap.
2021-06-02 13:47:16 -07:00
stvn
3023cd3d55 build(renovate): automerge lockFileMaintenence 2021-06-02 12:33:07 -07:00
stvn
50f85674b1 build(renovate): use enableVulnerabilityAlerts preset 2021-06-02 12:33:06 -07:00
stvn
9437ee36f3 build(renovate): allow unscheduled updates
so we don't need to wait until next week to see changes.
2021-06-02 12:32:57 -07:00
stvn
463944012c merge(#438): build/renovate
commits
=======
- build(renovate): automerge devDependencies
- build(renovate): use preset to schedule weekly
- build(renovate): use preset to rebaseStalePrs
2021-06-02 11:40:56 -07:00
stvn
2d297aa7be build(renovate): automerge devDependencies
tickets
=======
- Fixes CENG-109
- Fixes stvstnfrd/openedx-meta#157
2021-06-02 11:33:17 -07:00
stvn
1ab5901d24 build(ci): run ci tests via matrix 2021-06-02 11:28:11 -07:00
stvn
ba678d92f7 build(ci): always run npm ci via Makefile 2021-06-02 11:28:11 -07:00
stvn
884651a702 build(ci): convert travis-ci to github actions
tickets
=======
- Fixes CENG-108
- Fixes stvstnfrd/openedx-meta#156
2021-06-02 11:26:53 -07:00
stvn
a152c631da build(renovate): use preset to schedule weekly 2021-06-01 23:36:43 -07:00
stvn
fbe91ce7e4 build(renovate): use preset to rebaseStalePrs 2021-06-01 23:33:07 -07:00
stvn
13681c1360 merge: stvn/own/code 2021-05-26 13:42:09 -07:00
stvn
43435f8ff3 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:14:32 -07:00
David Joy
830fb05819 docs: update README with environment variable details. (#436)
* build: defaulting coaching and demographics to false

Given that coaching and demographics are private, internal features to edx.org, we default them to false.

* docs: documenting custom MFE environment variables

Also adjusting the group to tag while I’m in here, and linking directly to the common env variables in read the docs.

* docs: improve formatting and fix sentence fragment in SUPPORT_URL
2021-05-26 13:41:24 -04:00
David Joy
f62bd5ad76 chore: let renovate be more liberal about what it merges (#435)
This repo was allowing both patch/minor updates of Paragon, but I've opened that up a bit with this PR.  This is based on similar config we've used in other repositories.

It loses the "stability days" that were here, but I don't think we need that for patch/minor updates.
2021-05-18 16:40:15 -04:00
edX Transifex Bot
e2f9edd623 fix(i18n): update translations 2021-05-10 02:11:44 +05:00
renovate[bot]
0b63613736 fix(deps): update dependency @edx/frontend-platform to v1.9.5 (#190)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-07 14:06:41 -04:00
David Joy
940828ff18 fix: Resolving i18n violations, hard-coded edx.org-specific strings (#433)
* fix: removing hard-coded edX from DemographicsSection messages

* fix: removing hard-coded “edX” strings from IDV messages

* fix: updating ThirdPartyAuth message description to remove hard-coded “edX” references.

* fix: replacing hard-coded “edX” strings with SITE_NAME in AccountSettingsPage

* fix: conditionalizing edx-specific strings in ConfirmationModal

If the SITE_NAME is ‘edX’, then edx.org-specific strings will be used.  Otherwise, more general, Open edX-appropriate strings will be used.

* fix: conditionalizing edX-specific strings in delete account components.

If the SITE_NAME is ‘edX’, then edx.org-specific strings will be used.  Otherwise, more general, Open edX-appropriate strings will be used.

* fix: replacing hard-coded ‘edX’ strings with SITE_NAME in ReviewRequirementsPanel

I missed a few because the messages were re-used.

* fix: review feedback, improving messages

- Removing unnecessary {siteName} references
- Improving some message descriptions
2021-05-07 14:00:21 -04:00
David Joy
296f68f7dd fix: remove hard-coded edX from page title (#431)
This uses variable substitution to insert SITE_NAME into index.html, rather than hard-coding “edX” into the file.  It also updates the .env files to use “localhost” as the default SITE_NAME.
2021-05-03 15:52:55 -04:00
Sarina Canelake
e59ada660c Merge pull request #427 from edx/sarina/update-README
update README
2021-04-16 15:45:58 -04:00
alangsto
c123daacd6 Merge pull request #430 from edx/alangsto/add_photo_mode_to_submit
Add additional post parameters to track photo modes
2021-04-15 13:36:19 -04:00
Alie Langston
7440cd367f Add additional post parameters to track photo modes 2021-04-15 10:50:22 -04:00
sarina
1d639c4a57 doc: Update README
- New, clearer installation instructions
- Additional details regarding MFE domain & roadmap
2021-04-14 12:29:41 -04:00
alangsto
6db789d6ac Merge pull request #429 from edx/alangsto/add_event_tracking
MST IDV Experiment Event Tracking
2021-04-13 07:40:35 -04:00
Alie Langston
4bbff91ad7 MST IDV Experiment
Add event tracking for initial choice in photo mode, and additional events when a user toggles between modes on the photo capture pages
2021-04-12 17:23:54 -04:00
edX Transifex Bot
24e32cd0c5 fix(i18n): update translations 2021-04-11 17:11:05 -04:00
Bianca Severino
d1548b7287 Merge pull request #426 from edx/bseverino/fix-reroute
[MST-717] Reroute user to summary panel in A/B test
2021-04-07 11:12:03 -04:00
Bianca Severino
d1de13469f fix: reroute user to summary panel in a/b test
The new reroutes introduced in the current A/B test override the
summary panel reroute, resulting in incorrect behavior. This fix
ensures that, if a user has reached the summary panel once,
they are properly rerouted from the camera/upload panels when
going back to retake or reupload their photos.
2021-04-06 16:03:50 -04:00
alangsto
62418db3ba Merge pull request #425 from edx/alangsto/enable_camera_experiment
feat: Allow users in experiment to continue with upload if no camera access
2021-04-05 14:51:41 -04:00
Alie Langston
24f70972d8 feat: Allow users in experiment to continue with upload if no camera access
MST-716. If users are part of the A/B experiment for photo upload on the IDV flow, they should be allowed to continue with photo upload if they deny camera access or camera access is unsupported. If they have already denied camera access, they will not be allowed to switch between upload and photo capture mode throughout the flow. Instead the user will only be allowed to complete the flow by uploading photos from their device.
2021-04-05 13:42:38 -04:00
edX Transifex Bot
a7c783888f fix(i18n): update translations 2021-04-04 17:11:06 -04:00
Bianca Severino
00b1bf58a5 Merge pull request #422 from edx/bseverino/validate-file-type
[MST-718] Add extra validation to IDV image upload
2021-04-02 11:01:28 -04:00
Bianca Severino
b2c08193f7 fix: add extra validation to IDV image upload 2021-04-01 15:21:53 -04:00
alangsto
2bbe7dc3f1 Merge pull request #423 from edx/alangsto/update_context_pages_for_experiment
Added upload help text to photo context panels
2021-03-31 15:02:34 -04:00
Alie Langston
4d93fd6c0f Added upload help text to photo context panels
updated messages
2021-03-31 14:22:29 -04:00
edX Transifex Bot
613229bbd1 fix(i18n): update translations 2021-03-28 17:05:42 -04:00
alangsto
39db13435f Merge pull request #419 from edx/alangsto/remove_summary_upload_for_experiment
Remove option to upload if user is in experiment
2021-03-26 11:41:38 -04:00
Alie Langston
994f22ab41 MST-654. Remove option to upload on summary page if user is in experiment
remove import
2021-03-26 11:22:18 -04:00
alangsto
579654c092 Merge pull request #416 from edx/alangsto/add_upload_option
MST-652: Add option to upload photos if user is in experiment
2021-03-23 08:15:03 -04:00
Alie Langston
6094509679 allow upload options if user is in experiment
allow upload options if user is in experiment

testing

removed unused import

fixed unit test and corrected button text

combined assignment line for values coming from context

fixed error in message

added testing for photo mode panel

restructured

added checks for portrait and ID panels

removed unused import, added checks for reroutes

added tests and corrected messages

fix
2021-03-22 16:36:04 -04:00
Bianca Severino
82170f383f Merge pull request #418 from edx/bseverino/idv-profile-manager
[MST-535] Prevent IDV name change if name is managed by a third party
2021-03-22 11:21:42 -04:00
Bianca Severino
addc3640cc Prevent user from attempting name change during IDV if their account is managed by a third party 2021-03-22 11:11:45 -04:00
edX Transifex Bot
b9c0069117 fix(i18n): update translations 2021-03-21 17:15:38 -04:00
Bianca Severino
11c31ba216 Merge pull request #415 from edx/revert-414-bseverino/idv-enterprise-alert
Revert "[MST-535] Prevent IDV name change if name is managed by a third party"
2021-03-18 15:56:14 -04:00
Bianca Severino
ccd2a5c074 Revert "[MST-535] Prevent IDV name change if name is managed by a third party" 2021-03-18 15:51:22 -04:00
Bianca Severino
ff2e86fa13 Merge pull request #414 from edx/bseverino/idv-enterprise-alert
[MST-535] Prevent IDV name change if name is managed by a third party
2021-03-18 10:45:53 -04:00
Bianca Severino
f71033232e Prevent user from attempting name change during IDV if their account is managed by a third party 2021-03-18 10:14:10 -04:00
Bianca Severino
1ff20786ff Merge pull request #413 from edx/bseverino/optional-name
[MST-704] Make name parameter optional in IDV submission
2021-03-12 13:18:15 -05:00
Bianca Severino
232bead0c9 Only submit full_name with IDV photos if the account name should be changed 2021-03-12 13:05:55 -05:00
Bianca Severino
bd0c879a51 Merge pull request #411 from edx/bseverino/optimizely
[MST-655] Add Optimizely to be used with IDV experiments
2021-03-11 11:56:20 -05:00
Bianca Severino
a52e1ed2a5 Add optimizely to be used with IDV 2021-03-10 09:58:00 -05:00
Awais Jibran
b8356bc962 Revert "Workaround an issue between React and Google Translate (#397)" (#412)
This reverts commit fa4c5ef872.
2021-03-10 13:14:10 +05:00
Adam Butterworth
240e589310 Update renovate.json (#410) 2021-03-01 10:49:50 -05:00
Renovate Bot
152d3230d1 fix(deps): update dependency @tensorflow-models/blazeface to v0.0.7 2021-02-18 03:27:15 +00:00
Renovate Bot
95bd1cc66f chore(deps): update dependency @edx/frontend-build to v5.6.9 2021-02-17 23:45:23 +00:00
Renovate Bot
5db6d2b319 fix(deps): update dependency @edx/frontend-component-header to v2.2.4 2021-02-08 21:06:42 +00:00
edX Transifex Bot
368bc00321 fix(i18n): update translations 2021-02-07 16:04:18 -05:00
Awais Jibran
fa4c5ef872 Workaround an issue between React and Google Translate (#397)
* Workaround an issue between React and Google Translate

* Adding comments for disabling chrome translate

* linting changes

* linting changes

* linting changes
2021-02-05 22:19:58 +05:00
David Joy
01f7180bb9 Bumping footer version to 10.1.4 (#400) 2021-02-04 15:16:32 -05:00
David Joy
ce79213b21 Bumping frontend-platform to 1.8.4 (#399)
Trying to resolve peer dependency issues with paragon 13.
2021-02-04 13:25:27 -05:00
Renovate Bot
3870732da8 fix(deps): update dependency jslib-html5-camera-photo to v3.1.6 2021-02-04 13:14:51 +00:00
Renovate Bot
60d7a469a7 fix(deps): update dependency @edx/frontend-platform to v1.8.2 2021-02-04 12:37:42 +00:00
Renovate Bot
921107354c fix(deps): update dependency @edx/frontend-component-header to v2.2.3 2021-02-04 11:00:35 +00:00
Renovate Bot
9ea6120ded fix(deps): update dependency @edx/frontend-component-footer to v10.1.3 2021-02-04 10:08:30 +00:00
Renovate Bot
31493af69a chore(deps): update dependency @testing-library/react to v10.4.9 2021-02-04 09:45:00 +00:00
Renovate Bot
732cf11d96 chore(deps): update dependency @testing-library/jest-dom to v5.11.9 2021-02-04 08:24:01 +00:00
David Joy
42c1c49a85 Rebrand: Update to Paragon 13, use brand package, and LINT. (#386)
* Mild style whitespace linting

* Updating to latest paragon and brand.

Needed to update Button/StatefulButton props to use variant, primarily.

* Adding new environment variables.

* Fixing prop-types warning.

* Updating snapshots.  Modal and Button changed primarily.

I’ve reviewed the various snapshots and determined they’re all correct.  The button prop changes are in line with what I’ve seen elsewhere with the new react-bootstrap-based Button component replacing our own button.  The modal changes make sense, as I think we added some focus lock handling.

* Locking dependency versions in package.json

* Removing dataUtils functions, extraneous deps, and updating frontend-build

Committing all these at the same time since they affect package-lock.json together and splitting them out is nearly impossible now.

* Turning the linter on.

Hold on to your butts!

After this commit, there will be ~1600 linting errors to fix in subsequent commits.

* Main app auto-linting.

Not including coaching, demographics, or ID verification.

This is all the whitespace/syntax linting that my auto-formatter fixed.  I did a few small whitespace cleanups after it, but this commit is 95% automatic.

* Removing HeaderFooterLayout

The HeaderFooterLayout was only used in one place.  Collapsing it down again; also means we don’t have to have prop types for it or split it out into a separate file or anything.

* Main app propTypes cleanup

We were missing some propTypes in AccountSettingsPage and EditableField.

JumpNav had a default that was unused, since displayDemographicsLink is Required.

* Main app manual linting

AccountSettingsPage had some function-ordering issues, and some weak equality checking.

EditableField had an if without curly braces, followed by a variable named “value” which obscured a variable with the same name in the parent scope.  I renamed it to “finalValue” to avoid the name reuse.

* Coaching auto-fixed lint errors

These are general whitespace and syntax linting errors that my IDE automatically fixed.  I went in after and tweaked the whitespace a bit cause it didn’t finish the job, but this is 95% automatic.

* Coaching unused prop in CoachingConsent.

* Demographics auto-fixed lint errors.

Again, 95% auto-fixed linting errors, done by my IDE.  I tweaked a few here and there for spacing and such where it didn’t do a perfecto job.

* Demographics Checkboxes manual linting fixes.

A few things here:

- We were double-exporting Checkboxes, once as the default, once as a named export.  I removed the named export.
- Now uses === when checking string equality.
- id prop was always set, so I made it required
- PropTypes.array is not specific enough for the linter, so I found out what “values” and “options” were being set to and made some arrayOf PropTypes declarations that were more specific.
- onChange is also always set, so now it’s required.

* Demographics manual linting fixes for DemographicsSection

Bunch of things here:

- Reordered hasRetrievedDemographicsOptions and addDefaultOption to later in the file where the linter told me to put them.
- ethnicityFieldDisplay did not consistently return - only in the conditional did it return.  Now it uses the conditional to set a value for enthicities and then consistently returns at the end, processing the ethnicities array.
- handleSubmit didn’t use its “values” prop
- handleSubmit was iterating over an object using a for…in loop, which iterates over ALL properties of an object, not just the keys you might expect.  Probably not a problem, but not a good practice either.  It now uses Object.entries to get the iterable properties of the object.
- renderDemographicsServiceIssueWarning should use a || instead of a | for an OR.  It can also use ! instead of == false
- Using === for string equality in showSelfDescribe and showWorkStatusDescribe
- Using the Paragon Hyperlink component instead of an a tag.  It decorates links with additional metadata noting that the link will be external.
- Adding rel=“noopener noreferrer”, which is a security fix.
- Adding missing propTypes for formValues, drafts, forwardRef, and saveMultipleSettings.

* ID Verification auto-fixed linting errors

This commit includes whitespace and syntax linting errors that were auto-fixed by my IDE, with a little manual whitespace fixing by me where it didn’t get it quite right.  95% automated.

* ID Verification circular dependency between IdVerificationContext and AccessBlocked

This commit resolves a circular dependencybetween IdVerificationContext and the AccessBlocked component.

AccessBlocked imported ERROR_REASONS from IdVerificationContext and IdVerificationContext imported AccessBlocked itself.

We resolve this by moving IdVerificationContextProvider out into its own file.  Then it can safely import from AccessBlocked, and both can safely import the context and constants from IdVerificationContext.

This also sets IdVerificationContext as the default export from its file, which is responsible for the majority of the file changes in this commit, where the import shape changed.

* ID verification removing an unused import in SubmittedPanel

* Ignoring @tensorflow-models/blazeface as an unresolved import

We’re depending on an alias for @tensorflow-models/blazeface which confuses eslint.  I’ve just told it to ignore the problem, since the code is valid.

* ID Verification misc manual linting fixes

There’s a number of things in this commit:

- In Camera, we’re using the function version of setState so we can ensure we’re getting the right version of shouldDetect - you’re not supposed to set state from state directly, as there’s no guarantee that it’s still correct, and you might be setting it from stale data.
- In Camera, the takePhoto function inconsistently returned a value.  Returning after calling this.reset() makes it consistently return undefined.
- In Camera, the <button> was missing a type.
- In Camera, it’s also violating two accessibility rules - the video media has no caption, and there’s apparently not supposed to be an accessKey on buttons.  I don’t know how to fix either of those for this code so I’m punting - I’m leaving it to the owning teams.
- IdVerificationPage should be calling Object.prototype.hasOwnProperty instead of assuming a variable has Object prototype functions.
- ImagePreview didn’t set a default prop value for the id prop - I’ve set it to undefined.
- RequestCameraAccessPanel needed a button type on the “Enable” button.
- RequestCameraAccessPanel should use === for string equality.

* ID Verification manual linting fix to rearrange methods in Camera

The linter complained about the order of methods in the Camera component.  This commit rearranges them to suit it.

* Ignoring module.config.js file.

* Fixing package-lock.json after rebase.
2021-02-03 12:37:48 -05:00
alangsto
976652539c Merge pull request #387 from edx/alangsto/update_blazeface
Updated blazeface to es5 compatible version
2021-02-03 09:50:01 -05:00
Alie Langston
1e0b273579 updated blazeface to es5 compatible version 2021-02-03 09:29:14 -05:00
Bianca Severino
5995434b97 Merge pull request #385 from edx/bseverino/retake-photo
[MST-604] Change 'Retake Photo' to 'Retake Photo?'
2021-02-01 09:35:21 -05:00
edX Transifex Bot
2a854d53b6 fix(i18n): update translations 2021-01-31 16:04:06 -05:00
Bianca Severino
695a9e4297 Change 'Retake Photo' to 'Retake Photo?' to lessen learner confusion 2021-01-29 15:42:13 -05:00
Jawayria
8a79046f66 Updated the build status badge to point to travis-ci.com (#352) 2021-01-28 17:22:52 +05:00
Michael Roytman
27b2d3f853 Merge pull request #379 from edx/mroytman/MST-519-update-camera-directions
add a new component to handle camera unsupported errors in the IDV pr…
2021-01-26 16:06:50 -05:00
Michael Roytman
11a6d6b0ee add a new component to handle camera unsupported errors in the IDV process 2021-01-26 11:30:07 -05:00
Renovate Bot
0fd470ad5e fix(deps): update font awesome 2021-01-26 00:39:18 +00:00
Renovate Bot
251c799e18 fix(deps): update dependency qs to v6.9.6 2021-01-25 23:02:18 +00:00
Renovate Bot
69b902081e fix(deps): update dependency @edx/frontend-component-header to v2.2.2 2021-01-25 22:35:13 +00:00
Renovate Bot
b30012fd7b fix(deps): update dependency @edx/frontend-component-footer to v10.1.2 2021-01-25 21:52:13 +00:00
Renovate Bot
8dcf228faa chore(deps): update dependency enzyme-adapter-react-16 to v1.15.6 2021-01-25 20:51:35 +00:00
edX Transifex Bot
3db3f87375 fix(i18n): update translations 2021-01-17 16:13:41 -05:00
David Joy
6b51999890 Bumping frontend-platform to latest. (#372) 2021-01-05 16:47:48 -05:00
edX Transifex Bot
65b6b30f01 fix(i18n): update translations 2021-01-03 16:13:58 -05:00
edX Transifex Bot
00be4845b8 fix(i18n): update translations 2020-12-27 16:13:45 -05:00
visortelle
4a3270d0a8 Edit translation message on account page. (#361)
* Edit translation message on account page. 

Change the translation message from "Spoken languages" to "Spoken language". 
The plural form can confuse a user because select input allows selecting only a single value.

* Edit translation message on account page.

* Force travis build

* Revert src/i18n/messages content

according to https://github.com/edx/frontend-app-account/pull/361#issuecomment-749222209

* Revert "Revert src/i18n/messages content"

This reverts commit eff1daac03.

* Revert src/i18n/messages content

according to
https://github.com/edx/frontend-app-account/pull/361#issuecomment-749222209
2020-12-22 13:36:09 -05:00
edX Transifex Bot
fa91f25ad3 fix(i18n): update translations 2020-12-20 16:13:32 -05:00
Bianca Severino
f7bb06e109 Merge pull request #368 from edx/bseverino/idv-example-card
[MST-494] Clarify photo ID instructions with example image
2020-12-16 15:25:52 -05:00
Bianca Severino
340ec87522 Add example img for ID card and update copy 2020-12-16 13:37:31 -05:00
Renovate Bot
9e6a74c633 fix(deps): update dependency @fortawesome/react-fontawesome to v0.1.13 2020-12-16 01:10:38 +00:00
Adam Stankiewicz
7c05188e5f fix: bump header and footer versions to use logo from config (#365) 2020-12-15 18:42:53 -05:00
Bianca Severino
a19c37fa85 Merge pull request #364 from edx/bseverino/idv-errors
[MST-502] Add more descriptive errors for IDV submission
2020-12-15 13:14:16 -05:00
Bianca Severino
600a2b8fe2 Add more descriptive errors for IDV submission 2020-12-14 14:29:21 -05:00
Bianca Severino
402dbae44f Merge pull request #360 from edx/bseverino/parse-query-string
[MST-567] Allow decoding of query string in IDV flow
2020-12-14 11:40:46 -05:00
edX Transifex Bot
75fddb9b69 fix(i18n): update translations 2020-12-13 16:03:56 -05:00
Bianca Severino
6fff8630d4 Allow parsing of special characters in querystring 2020-12-11 13:13:51 -05:00
Bianca Severino
06b4fc641e Merge pull request #359 from edx/bseverino/remove-course-key
Remove use of course key temporarily
2020-12-09 16:49:22 -05:00
Bianca Severino
19d5411402 Remove course key 2020-12-09 16:43:08 -05:00
Bianca Severino
14160eaeb3 Merge pull request #358 from edx/bseverino/parse-course-key
Parse course_id query string correctly
2020-12-07 16:56:41 -05:00
Bianca Severino
b06138f96d Parse course_id query string correctly 2020-12-07 16:20:44 -05:00
Bianca Severino
01d0b3645b Merge pull request #357 from edx/revert-355-revert-351-bseverino/idv-block-audit
Revert "Revert "Prevent IDV access from audit courses""
2020-12-07 10:30:22 -05:00
Bianca Severino
0a4dcb4eaa Revert "Revert "Prevent IDV access from audit courses"" 2020-12-07 10:18:40 -05:00
edX Transifex Bot
37e338df0f fix(i18n): update translations 2020-12-06 16:13:59 -05:00
Bianca Severino
6a0e27c816 Merge pull request #355 from edx/revert-351-bseverino/idv-block-audit
Revert "Prevent IDV access from audit courses"
2020-12-04 13:05:15 -05:00
Bianca Severino
289341dd91 Revert "Prevent IDV access from audit courses" 2020-12-04 11:59:52 -05:00
edX Transifex Bot
45f9a15885 fix(i18n): update translations 2020-11-22 16:03:56 -05:00
Bianca Severino
f982187be6 Merge pull request #351 from edx/bseverino/idv-block-audit
Prevent IDV access from audit courses
2020-11-16 15:13:04 -05:00
Bianca Severino
9d0d03c402 Prevent IDV access from audit learners 2020-11-16 14:20:39 -05:00
morenol
8602561e55 fix: Update frontend-build (#340)
Update frontend-platform
2020-11-10 17:18:02 -05:00
Renovate Bot
b310574f18 fix(deps): update font awesome 2020-11-10 02:00:05 +00:00
Renovate Bot
73badaf916 fix(deps): update dependency universal-cookie to v4.0.4 2020-11-10 00:58:25 +00:00
Renovate Bot
f02cc43078 chore(deps): update dependency enzyme-adapter-react-16 to v1.15.5 2020-11-09 23:36:50 +00:00
edX Transifex Bot
bd5c2343be fix(i18n): update translations 2020-11-08 16:03:56 -05:00
alangsto
6349d487e4 Merge pull request #343 from edx/alangsto/camera_permissions
Ensure that first stream is stopped
2020-11-05 14:40:40 -05:00
Alie Langston
9c6137e668 testing to see if we need to request front/back camera
stopping initial stream
2020-11-05 14:30:39 -05:00
alangsto
2cf01270d7 Merge pull request #342 from edx/alangsto/fix_camera_access
Remove browser based resolution
2020-11-04 13:15:26 -05:00
Alie Langston
403df8926d removed browser based resolution, and instead optimized photo for all browsers
completed comment
2020-11-04 13:01:14 -05:00
Bianca Severino
4bd96c70af Merge pull request #341 from edx/bseverino/name-change-fix
Fix onSubmit for IDV name change form
2020-10-27 12:32:30 -04:00
Bianca Severino
d2a835f560 Fix submit functionality on name change form 2020-10-27 12:13:47 -04:00
Bianca Severino
d0b5d54d0a Merge pull request #339 from edx/bseverino/id-resolution
Increase camera resolution to 1080p for ID verification
2020-10-20 14:17:51 -04:00
Bianca Severino
26299eed65 Increase camera resolution to 1080p 2020-10-20 13:53:01 -04:00
adeelehsan
648bea8d84 Merge pull request #338 from edx/aehsan/suppressred_pii_for_accounts
suppressed pii for account
2020-10-09 01:59:13 +05:00
adeelehsan
7409f02056 suppressed pii for account 2020-10-09 00:54:07 +05:00
alangsto
f5dd409816 Merge pull request #337 from edx/alangsto/add_tracking_events
Add tracking events for face detection
2020-10-01 15:32:36 -04:00
Alie Langston
6ddac11dc0 added tracking events
added testing
2020-10-01 14:19:07 -04:00
adeelehsan
7019aea4fb Merge pull request #333 from edx/aehsan/van-23/suppressed_pii_for_accounts
suppressed pii for hot jar
2020-09-25 17:33:37 +05:00
adeelehsan
5424434599 suppressed pii for hot jar
VAN-23
2020-09-25 17:22:13 +05:00
edX Transifex Bot
8ca9dc78a9 fix(i18n): update translations 2020-09-20 17:06:06 -04:00
alangsto
30e25b96bb Merge pull request #332 from edx/alangsto/add_realtime_feedback
Add realtime feedback for screenreaders during face detection
2020-09-16 15:31:00 -04:00
Alie Langston
1d01abc7da added feedback for screenreaders
moved settings state back to original

fixed status updates

updated message for more clarity

renamed variables for clarity

added comment

fixed variables

fixed variable again

decreased delay between feedbacks

updated comment
2020-09-16 15:22:04 -04:00
alangsto
917152df22 Merge pull request #330 from edx/alangsto/remove_survey
Removed SurveyMonkey survey
2020-09-15 16:35:45 -04:00
Alie Langston
961c0feb78 removed SurveyMonkey survey 2020-09-15 16:04:46 -04:00
edX Transifex Bot
7d57d86729 fix(i18n): update translations 2020-09-13 17:04:11 -04:00
alangsto
34c5de1340 Merge pull request #324 from edx/alangsto/add_idv_survey
Added SurveyMonkey Survey to IDV submitted page
2020-09-10 15:42:59 -04:00
Alie Langston
81d604d046 adding second round of survey to IDV summary page 2020-09-10 15:22:21 -04:00
Michael Roytman
e6f7e83cf5 Merge pull request #322 from edx/mroytman/use-better-camera-mobile
use 'environment' facing mode when using the camera in the ID photo c…
2020-09-10 14:45:30 -04:00
Michael Roytman
a970e17070 use 'environment' facing mode when using the camera in the ID photo context; this will open the rear facing camera on mobile 2020-09-10 14:33:42 -04:00
alangsto
f471ae0aa7 Merge pull request #323 from edx/alangsto/add_error_messaging
Added error messaging for image upload and idv submission
2020-09-10 14:29:20 -04:00
Alie Langston
b9efe6faee added error messaging for image upload and idv submission
updated testing

wrapped button click

moved maximum file size to a const variable
2020-09-10 14:23:35 -04:00
edX Transifex Bot
2dbccec1f1 fix(i18n): update translations 2020-09-06 17:03:58 -04:00
Michael Roytman
9f38b975d9 Merge pull request #318 from edx/mroytman/fix-iOS-camera-bug-popout
add playsInline attribute to video element to ensure it does not pop out into full screen video on iOS Safari
2020-09-04 14:54:29 -04:00
Michael Roytman
ae355cefcf add playsInline attribute to video element to ensure it does not pop out into full screen video on iOS Safari 2020-09-04 14:03:29 -04:00
alangsto
d63dfc929f Merge pull request #317 from edx/alangsto/fix_camera_resolution
Added logic for adjusting image resolution
2020-09-03 14:11:13 -04:00
Alie Langston
64be9edeac adjusted size factor based on camera resolution
added additional check so that tests pass

updates for requested changes
2020-09-03 14:05:36 -04:00
alangsto
5f4f82eae1 Merge pull request #315 from edx/alangsto/blazeface_fix
Fix for blazeface/camera issue
2020-09-02 10:43:40 -04:00
Alie Langston
c8c7352549 updated camera and canvas ratio to match, and updated ranges for landmarks 2020-09-02 10:34:23 -04:00
Renovate Bot
88206e4282 fix(deps): update dependency @tensorflow/tfjs-core to v1.6.1 2020-09-01 22:56:01 +00:00
Renovate Bot
d8e23b1a02 fix(deps): update dependency @tensorflow/tfjs-converter to v1.6.1 2020-09-01 21:58:04 +00:00
alangsto
5db21d2483 Merge pull request #302 from edx/alangsto/add_object_tracking
added object tracking
2020-09-01 16:48:39 -04:00
Alie Langston
526d6114f2 added object tracking
moved load of library

updated test

removed async

trying to retest

Retesting

added test back

fixed errors due to next button

removed try catch so errors occur

added try catch back

added in ignore

readded libraries

stops detection when photo is taken, stops erroring issue

added help text

added spinner and better mocked blazeface

moved img element back to correct place

updated for requested changes

updates for requested changes

added timeout for test

updated blazeface to pull from forked repo, and added changes based on accessibility feedback
2020-09-01 15:54:20 -04:00
Renovate Bot
0cc38e2dc6 chore(deps): update dependency enzyme-adapter-react-16 to v1.15.4 2020-08-31 06:47:57 +00:00
edX Transifex Bot
380ca7c816 fix(i18n): update translations 2020-08-30 17:03:45 -04:00
Justin Hynes
bcb20234ab Merge pull request #296 from edx/jhynes/microba-534_demographics-i18n
MICROBA-534 | Remove hardcoded Demographics options, pull choices from Demographics API
2020-08-28 07:43:14 -04:00
Justin Hynes
87ff50ace8 MICROBA-534 | Remove hardcoded Demographics options, pull choices from Demographics API
[MICROBA-534]
- Pull Demographics field choices from Demographics API to support I18N/L10N effors so that we don't need duplicated choices for the fields in two places (backend and frontend)
- Refactor error detection code in DemographicsSection component
2020-08-27 14:31:06 -04:00
alangsto
b409aff6b4 Merge pull request #301 from edx/alangsto/fix_focus_on_permissions_panel
Fixed HTML element focus on camera permissions panel
2020-08-25 12:50:51 -04:00
Michael Roytman
2b67d037bf Merge pull request #293 from edx/mroytman/MST-361-stop-camera-after-submit
stop camera once user successfully submits photos to IDVerification s…
2020-08-25 12:19:16 -04:00
Alie Langston
4137996a91 fixed focus issue
removed test id

removed space
2020-08-25 12:16:17 -04:00
Michael Roytman
1319bd6377 stop camera once user successfully submits photos to IDVerification service 2020-08-25 12:07:58 -04:00
alangsto
a579e86e98 Merge pull request #300 from edx/alangsto/add_image_upload_toggle
Added option for ID image upload
2020-08-24 15:51:18 -04:00
Alie Langston
74bb2fb45f added option for image upload
added friction to allowing users to upload id image

only shows picture within component if it has been uploaded, not if it was captured through the webcam
2020-08-24 15:34:53 -04:00
edX Transifex Bot
11dbbad20b fix(i18n): update translations 2020-08-23 17:03:33 -04:00
alangsto
a0227f1dbc Merge pull request #298 from edx/alangsto/add_instructions_for_camera_permissions
Added instructions for camera permissions
2020-08-20 10:44:17 -04:00
Alie Langston
f6a7a6063c added instructions for enabling camera
updated message

created separate component
2020-08-20 10:39:27 -04:00
Zachary Hancock
0aa02687e6 Merge pull request #297 from edx/zhancock/camera-fix
Limit camera to 720p
2020-08-19 09:02:59 -04:00
Zach Hancock
303f6a5d3f limit camera to 720p 2020-08-18 16:45:48 -04:00
edX Transifex Bot
b262f42c8d fix(i18n): update translations 2020-08-16 17:07:29 -04:00
Bianca Severino
b92794b72c Merge pull request #294 from edx/bseverino/idv-tests
Add tests for new IDV functionality
2020-08-14 12:35:34 -04:00
Bianca Severino
3f754fa114 Add tests for new IDV functionality 2020-08-14 11:39:26 -04:00
Thomas Tracy
bf274e5186 Fix extra call to LMS when PATCHing demographics (#291)
We were making an extra call to the LMS when making a PATCH to the
demographics service which we don't need to do.

The keys for demographics fields we not being removed from the
accountSettings object properly, which made them be seen as changes,
which triggered the call to the LMS. I've added a constant
DEMOGRAPHICS_FIELDS that keys out those fields, removing the call.
2020-08-13 15:00:24 -04:00
Bianca Severino
be9cf70c5c Merge pull request #292 from edx/bseverino/idv-improvements
[MST-353] IDV a11y and UX improvements
2020-08-13 10:01:44 -04:00
Bianca Severino
c00ea15920 IDV a11y and UX improvements 2020-08-12 16:10:30 -04:00
Bianca Severino
5a8bd309e7 Merge pull request #289 from edx/bseverino/idv-improvements
Add i18n and UX improvements to IDV
2020-08-11 11:43:23 -04:00
Bianca Severino
d83ea54272 Add i18n and UX improvements to IDV 2020-08-11 10:19:21 -04:00
Justin Hynes
eae18d9c63 Merge pull request #290 from edx/jhynes/microba-405_add-missing-income-option
MICROBA-405 | Add missing demographics income option
2020-08-11 08:48:23 -04:00
Justin Hynes
a18da61cec MICROBA-405 | Add missing demographics income option
[MICROBA-405]
- Add missing demographics income option
2020-08-10 15:18:43 -04:00
Renovate Bot
80d5fd2a34 chore(deps): update dependency enzyme-adapter-react-16 to v1.15.3 2020-08-08 03:41:58 +00:00
Jeff LaJoie
d4e9ba0420 Merge pull request #287 from edx/jlajoie/ENT-3302
fix: ENTERPRISE_LEARNER_PORTAL_HOSTNAME added for header updates
2020-08-06 07:25:56 -04:00
Jeff LaJoie
5add376c31 fix: ENTERPRISE_LEARNER_PORTAL_HOSTNAME added for header updates 2020-08-06 07:13:04 -04:00
Bianca Severino
559c9aa1a9 Merge pull request #284 from edx/bseverino/idv-testing
Add React Testing Library and write unit tests for IDV
2020-08-03 09:13:08 -04:00
Bianca Severino
ea8a6d29d0 Add React Testing Library and write unit tests for IDV 2020-08-03 09:05:44 -04:00
edX Transifex Bot
4689482137 fix(i18n): update translations 2020-08-02 17:06:56 -04:00
Renovate Bot
d48be79e53 Update dependency @edx/paragon to v9.1.1 2020-07-31 19:14:09 +00:00
Thomas Tracy
ed94cc68e3 Force content type header to 'application/json' (#282) 2020-07-29 15:06:36 -04:00
Bianca Severino
05740b37ff Merge pull request #281 from edx/bseverino/remove-survey
Remove SurveyMonkey widget from IDV
2020-07-29 11:07:55 -04:00
Bianca Severino
b2c8164cd1 Remove SurveyMonkey widget from IDV 2020-07-29 09:58:17 -04:00
Bianca Severino
47b369f797 Merge pull request #280 from edx/bseverino/idv-spinner
Update Paragon and add loading state + spinner to IDV summary page
2020-07-24 16:16:58 -04:00
Bianca Severino
88239f2700 Update Paragon and add loading state + spinner to IDV summary page 2020-07-24 16:07:53 -04:00
Bianca Severino
602c4b484c Merge pull request #279 from edx/bseverino/message-fix
Fixed IDV submitted message to estimate 5 days rather than 1-2 days
2020-07-24 12:16:45 -04:00
Bianca Severino
45ec573ff9 Fixed IDV cubmitted message to estimate 5 days rather than 1-2 days 2020-07-24 11:39:56 -04:00
Justin Hynes
e0befb8b60 Merge pull request #272 from edx/jhynes/microba-488_improve-error-handling
MICROBA-488 | Improve error handling in DemographicsSection
2020-07-24 08:58:43 -04:00
Justin Hynes
9f4d944670 MICROBA-488 | Improve error handling in DemographicsSection
[MICROBA-488]
- Improve error handling in DemographicsSection
2020-07-24 08:49:12 -04:00
Bianca Severino
60d26649dd Merge pull request #278 from edx/bseverino/tracking-fix
Changed sendTrackingLogEvent to sendTrackEvent
2020-07-23 19:21:04 -04:00
Bianca Severino
55f25c73ca Changed sendTrackingLogEvent to sendTrackEvent 2020-07-23 19:03:11 -04:00
Renovate Bot
29dbdf6ad0 Update dependency react-scrollspy to v3.4.3 2020-07-23 02:05:03 +00:00
Bianca Severino
5bfa834563 Merge pull request #276 from edx/bseverino/css-fix
Remove PurgeCSS
2020-07-22 16:04:52 -04:00
Bianca Severino
5e3af50e3b Remove PurgeCSS 2020-07-22 12:51:24 -04:00
Bianca Severino
e30a20b185 Merge pull request #273 from edx/bseverino/idv-analytics
Add tracking log events to IDV
2020-07-22 11:14:46 -04:00
Renovate Bot
2743e05890 Update dependency codecov to v3.7.2 2020-07-22 03:26:31 +00:00
Olivia Ruiz-Knott
71c0563e3a MICROBA-487 Fix legal text spacing on demographics section (#274) 2020-07-21 15:58:55 -04:00
Bianca Severino
5d8d327a48 Added tracking log events to IDV 2020-07-21 13:57:09 -04:00
Albert (AJ) St. Aubin
15ba7c087e Merge pull request #266 from edx/aj/MICROBA-484
[MICROBA-484] Scroll to section based on URL
2020-07-21 13:45:27 -04:00
Olivia Ruiz-Knott
6df7ad243b MICROBA-463 Collapse phone number field when coaching toggled on (#271) 2020-07-21 13:09:45 -04:00
Bianca Severino
594d3ff9f1 Merge pull request #264 from edx/bseverino/hide-image-upload
ID Verification: Hide image upload component
2020-07-21 10:16:09 -04:00
Renovate Bot
7bb9d09dae fix(deps): update dependency @fortawesome/fontawesome-svg-core to v1.2.30 2020-07-21 13:48:44 +00:00
dependabot[bot]
0e6233b693 chore(deps-dev): bump codecov from 3.6.5 to 3.7.1 (#269)
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:24:35 -04:00
Renovate Bot
22087d2d2c fix(deps): update dependency @edx/frontend-component-footer to v10.0.11 2020-07-20 19:58:22 +00:00
Albert (AJ) St. Aubin
64798cdc80 Remove jumpy scrolling 2020-07-20 15:12:51 -04:00
Albert (AJ) St. Aubin
f53ba967e5 Simplification 2020-07-20 14:28:23 -04:00
Albert (AJ) St. Aubin
2deb47d542 [MICROBA-484] Scroll to section based on URL
Added in a check for a locationHash and loaded state to
scroll to the section in the URL so that the page would load to a Nav
Section after loading data.
2020-07-20 14:02:48 -04:00
edX Transifex Bot
1b9dd3bdf5 fix(i18n): update translations 2020-07-19 17:06:34 -04:00
Bianca Severino
fd3c8ede6d Hide image upload flow 2020-07-17 14:11:58 -04:00
Justin Hynes
c1fdace72d Merge pull request #261 from edx/jhynes/microba-474_determine-demographics-visibility
MICROBA-474 | Gate Demographics questions and link via LMS API call
2020-07-16 08:27:03 -04:00
Justin Hynes
4ca4d55796 MICROBA-474 | Gate Demographics questions and link via LMS API call
[MICROBA-474]
- Add logic to show (or hide) the "Optional Information" (Demographics questions) section on the Account settings page via call to new LMS API
2020-07-15 12:01:52 -04:00
Bianca Severino
2da606bf6f Merge pull request #260 from edx/bseverino/idv-course-key
Persist courseRunKey in ID verification flow
2020-07-13 16:45:19 -04:00
Bianca Severino
997a3c0b98 Add ability to store and retrieve course run key
Add course run key to POST request
Change text based on return location
Minor style fixes
2020-07-13 14:41:35 -04:00
Justin Hynes
f627257a1c Merge pull request #258 from edx/jhynes/microba-471_add-self-employed-choice
MICROBA-471 | Add 'self-employed' option to work status options
2020-07-13 08:32:31 -04:00
edX Transifex Bot
9aa2a816b4 fix(i18n): update translations 2020-07-12 17:06:13 -04:00
Justin Hynes
fc54dd528f MICROBA-471 | Add 'self-employed' option to work status options
Add support for self-employed option added to the work status options on the UserDemographics model.
2020-07-10 15:50:06 -04:00
Albert (AJ) St. Aubin
53fc1b325c Merge pull request #257 from edx/aj/MICROBA-445
Adding a link to the legal copy for demographics
2020-07-09 13:06:22 -04:00
Albert (AJ) St. Aubin
48b02cd2de Adding a link to the legal copy for demographics 2020-07-09 12:45:12 -04:00
edX Transifex Bot
490274b2ed fix(i18n): update translations 2020-07-05 17:06:23 -04:00
Renovate Bot
10dc9aabde Update dependency @edx/frontend-component-footer to v10.0.10 2020-07-04 11:16:45 +00:00
Bianca Severino
74ec75781e Merge pull request #253 from edx/bseverino/surveymonkey
Add SurveyMonkey widget to IDV submitted page
2020-07-02 10:52:47 -04:00
Justin Hynes
c428d3044f Merge pull request #254 from edx/jhynes/microba-465_fix-demographics-alert-behavior
MICROBA-465 | Fix Demographics alert behavior
2020-07-01 12:46:19 -04:00
Justin Hynes
d9777fe48e MICROBA-465 | Fix alert behavior on Accounts to only display on Demographics errors
[MICROBA-465]
- Fix Demographics alert behavior on Accounts page to only display on Demographics errors
2020-07-01 12:07:28 -04:00
Bianca Severino
6b7ab05dd5 Add SurveyMonkey widget to IDV submitted page 2020-07-01 11:07:59 -04:00
Justin Hynes
ce79cd7f5a Merge pull request #251 from edx/jhynes/microba-309_demographics
MICROBA-309 | DemographicsSection component tests and error handling
2020-07-01 08:29:26 -04:00
Justin Hynes
b8ab0a2150 MICROBA-309 | DemographicsSection component tests and error handling
[MICROBA-309]
- Fix defect in ethnicityFieldDisplay function. Check to make sure key/field exists before accessing
- Add additional error handling. Add ability to show an Alert to the user if a call to the Demographics API fails
- Add tests
2020-07-01 08:11:47 -04:00
edX Transifex Bot
56569c717c fix(i18n): update translations 2020-06-28 17:06:01 -04:00
Albert (AJ) St. Aubin
45511860c2 Merge pull request #249 from edx/aj/MICROBA-368_coaching_text
[MICROBA-368] Updated text for the Coaching consent to align with
2020-06-26 15:00:14 -04:00
Albert (AJ) St. Aubin
11f7d56e75 [MICROBA-368] Updated text for the Coaching consent to align with
messaging timeline
2020-06-26 14:28:09 -04:00
Thomas Tracy
f19380ebac Hide demographics sidenav link when demographics is off (#248) 2020-06-25 12:56:34 -04:00
Thomas Tracy
5eb43871c7 MB-17: Refactor coaching_consent form (#244)
We were having issues with how the coaching consent form was sending
data. Previously, we were hitting 2 endpoints - one from the coaching
plugin, and one from the LMS. This changes fixes a few issues:

* CoachingConsent now hits one api that handles saving the phone_number,
full_name, and coaching_consent.
* This component does not need to share any of this data, so it is not
connected to Redux. Everything to do with patching is done in the
CoachingConsent component.
* Fetching data is still done through actions provided by redux.
* This change does not effect the fields in the root account settings
page

* Add tests for coaching consent form
2020-06-24 10:37:58 -04:00
Bianca Severino
a2388bffc2 Merge pull request #245 from edx/bseverino/idv-ui
IDV UI improvements
2020-06-22 16:46:59 -04:00
Bianca Severino
ebe6af0913 Implemented UI improvements
Fixed padding on 'next' button and collapsed camera help text

Added functionality to submit button

Styling edit to differentiate cards

Removed todo comments
2020-06-22 14:11:20 -04:00
edX Transifex Bot
fd6ba7847a fix(i18n): update translations 2020-06-21 17:05:29 -04:00
Renovate Bot
f051905da1 fix(deps): update dependency @fortawesome/fontawesome-svg-core to v1.2.29 2020-06-18 21:45:19 +00:00
Olivia Ruiz-Knott
5eddb35e0b Merge pull request #242 from edx/ork/MICROBA-309_collect-demographics-on-account-page
MICROBA-309 Collect demographics on account page
2020-06-18 17:17:12 -04:00
Olivia Ruiz-Knott
0196245c13 MICROBA-309 Collect demographics on account page
Add new fields to account page for demographics collection

MICROBA-309
2020-06-18 17:07:54 -04:00
Renovate Bot
0c53a29094 fix(deps): update dependency formdata-polyfill to v3.0.20 2020-06-17 21:13:19 +00:00
Renovate Bot
60643f6215 fix(deps): update dependency @fortawesome/react-fontawesome to v0.1.11 2020-06-16 23:14:18 +00:00
Renovate Bot
b0aa91fc98 fix(deps): update dependency qs to v6.9.4 2020-06-16 20:21:47 +00:00
Bianca Severino
09813fa689 Merge pull request #210 from edx/hack/idv
Hackathon XXIV -- Add ID verification page
2020-06-16 15:57:16 -04:00
Bianca Severino
b05a40d0aa Code cleanup and i18n compatibility
Replaced hardcoded text with i18n components

Added title to confirmation button for a/b testing purposes

Redirect the user to the beginning of the process on reload to avoid losing camera access, also removed unused code

Update src/id-verification/IdVerificationContext.jsx, make children prop required

Co-authored-by: Kyle McCormick <kmccormick@edx.org>

Remove todo comment in ImageFileUpload.jsx

Added comment to service.js and removed unused code from index.js
2020-06-16 15:46:19 -04:00
Kyle McCormick
1a76468587 Add ID verification page
Fix 404 page from displaying when it shouldn't

Was displaying below the settings page itself

Add panels for each step of the verification flow

Add IdVerificationContext

Add BasePanel

Revert "Add BasePanel"

This reverts commit 2ddca14b4b8b35cd23f0525f49070753c09d361d.

Add BasePanel

Focus heading elements on mount

Add current account name to GetNameIdPanel

Fix nested button focus problem

Add a friendly camera permission request

Remove double ctas

lint

Add initial service.js file; not tested

Add image file uploads

fix temp heading of photo context panel

Add photo id name input

Add redirect logic for users who find themselves too far in the flow

add wip SubmittedPanel

Wire up Submit button

Update routing-utilities.js

Preview image on summarypenl; still buggy

Add some content and styles

update first panel content

Update headings

Use ImagePreview component on SummaryPanel

Fix service.js; it works now

More content and fix for name input

ImagePreview: Change 'name' to 'alt'

Add content to camera request

Fix ImagePreview alt text

Iterating on SummaryPage

Add privacy info modal

Update name entry

Make the footer sticky to the bottom

Update upload photo screens

Add photo to name panel

camera component

overlay camera button

implement camera component

remove camera tracking

Add retake photo buttons

Add service for verification status endpoint

delete old css

lint fix

Better name edit state

portrait photo page

Add edit name from summary

Add content to submitted screen

lint

Rename status service function

Add clarity to summary view text

First pass at gating based on existing idv

Clean up IDv-status-gating screens

finish up webcam/photo pages

remove unused tracking library
2020-06-10 15:02:27 -04:00
Renovate Bot
93ef8d2b04 fix(deps): update dependency @fortawesome/react-fontawesome to v0.1.10 2020-06-02 18:19:14 +00:00
edX Transifex Bot
98efe2649a fix(i18n): update translations 2020-05-31 17:04:50 -04:00
AsadAzam
80cc8f156f Merge pull request #234 from edx/asad/prod-1542
Added invalid password check on delete
2020-05-29 17:27:33 +05:00
Asad
1364ca5711 Added invalid password check on delete 2020-05-29 14:28:20 +05:00
edX Transifex Bot
c337e03a4d fix(i18n): update translations 2020-05-24 17:04:37 -04:00
228 changed files with 36170 additions and 20641 deletions

56
.env
View File

@@ -1,17 +1,39 @@
ACCESS_TOKEN_COOKIE_NAME=null
BASE_URL=null
CREDENTIALS_BASE_URL=null
CSRF_TOKEN_API_PATH=null
ECOMMERCE_BASE_URL=null
LANGUAGE_PREFERENCE_COOKIE_NAME=null
LMS_BASE_URL=null
LOGIN_URL=null
LOGOUT_URL=null
MARKETING_SITE_BASE_URL=null
NODE_ENV=null
ORDER_HISTORY_URL=null
REFRESH_ACCESS_TOKEN_ENDPOINT=null
SEGMENT_KEY=null
SITE_NAME=null
SUPPORT_URL=null
USER_INFO_COOKIE_NAME=null
ACCESS_TOKEN_COOKIE_NAME=''
ACCOUNT_PROFILE_URL=''
BASE_URL=''
CREDENTIALS_BASE_URL=''
CSRF_TOKEN_API_PATH=''
DISCOVERY_API_BASE_URL=''
ECOMMERCE_BASE_URL=''
FAVICON_URL=''
LANGUAGE_PREFERENCE_COOKIE_NAME=''
LMS_BASE_URL=''
LOGIN_URL=''
LOGO_TRADEMARK_URL=''
LOGO_URL=''
LOGO_WHITE_URL=''
SHOW_PUSH_CHANNEL=''
SHOW_EMAIL_CHANNEL=''
LOGOUT_URL=''
MARKETING_SITE_BASE_URL=''
NODE_ENV='production'
ORDER_HISTORY_URL=''
PUBLISHER_BASE_URL=''
REFRESH_ACCESS_TOKEN_ENDPOINT=''
SEGMENT_KEY=''
SITE_NAME=''
STUDIO_BASE_URL=''
SUPPORT_URL=''
USER_INFO_COOKIE_NAME=''
ENABLE_COPPA_COMPLIANCE=''
ENABLE_ACCOUNT_DELETION=''
ENABLE_DOB_UPDATE=''
MARKETING_EMAILS_OPT_IN=''
APP_ID=
MFE_CONFIG_API_URL=
PASSWORD_RESET_SUPPORT_LINK=''
LEARNER_FEEDBACK_URL=''
SUPPORT_URL_TO_UNLINK_SOCIAL_MEDIA_ACCOUNT='https://help.edx.org/edxlearner/s/article/How-do-I-link-or-unlink-my-edX-account-to-a-social-media-account'
COUNTRIES_WITH_DELETE_ACCOUNT_DISABLED='[]'
# Fallback in local style files
PARAGON_THEME_URLS={}

View File

@@ -1,20 +1,40 @@
ACCESS_TOKEN_COOKIE_NAME='edx-jwt-cookie-header-payload'
ACCOUNT_PROFILE_URL='http://localhost:1995'
BASE_URL='localhost:1997'
CREDENTIALS_BASE_URL='http://localhost:18150'
CSRF_TOKEN_API_PATH='/csrf/api/v1/token'
DISCOVERY_API_BASE_URL=''
ECOMMERCE_BASE_URL='http://localhost:18130'
FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico
LANGUAGE_PREFERENCE_COOKIE_NAME='openedx-language-preference'
LMS_BASE_URL='http://localhost:18000'
LOGIN_URL='http://localhost:18000/login'
LOGO_TRADEMARK_URL=https://edx-cdn.org/v3/default/logo-trademark.svg
LOGO_URL=https://edx-cdn.org/v3/default/logo.svg
LOGO_WHITE_URL=https://edx-cdn.org/v3/default/logo-white.svg
LOGOUT_URL='http://localhost:18000/logout'
MARKETING_SITE_BASE_URL='http://localhost:18000'
MARKETING_SITE_BASE_URL='http://localhost:5335'
NODE_ENV='development'
ORDER_HISTORY_URL='localhost:1996/orders'
ORDER_HISTORY_URL='http://localhost:1996/orders'
PORT=1997
PUBLISHER_BASE_URL=''
REFRESH_ACCESS_TOKEN_ENDPOINT='http://localhost:18000/login_refresh'
SEGMENT_KEY=null
SITE_NAME='edX'
SEGMENT_KEY=''
SITE_NAME=localhost
STUDIO_BASE_URL=''
SUPPORT_URL='http://localhost:18000/support'
USER_INFO_COOKIE_NAME='edx-user-info'
# Temporary, Remove this once we are ready to release the feature.
COACHING_ENABLED=true
ENABLE_COPPA_COMPLIANCE=''
ENABLE_ACCOUNT_DELETION=''
ENABLE_DOB_UPDATE=''
MARKETING_EMAILS_OPT_IN=''
SHOW_PUSH_CHANNEL='true'
SHOW_EMAIL_CHANNEL='true'
APP_ID=
MFE_CONFIG_API_URL=
PASSWORD_RESET_SUPPORT_LINK='mailto:support@example.com'
LEARNER_FEEDBACK_URL=''
SUPPORT_URL_TO_UNLINK_SOCIAL_MEDIA_ACCOUNT='https://help.edx.org/edxlearner/s/article/How-do-I-link-or-unlink-my-edX-account-to-a-social-media-account'
COUNTRIES_WITH_DELETE_ACCOUNT_DISABLED='[]'
# Fallback in local style files
PARAGON_THEME_URLS={}

View File

@@ -2,17 +2,35 @@ ACCESS_TOKEN_COOKIE_NAME='edx-jwt-cookie-header-payload'
BASE_URL='localhost:1997'
CREDENTIALS_BASE_URL='http://localhost:18150'
CSRF_TOKEN_API_PATH='/csrf/api/v1/token'
DISCOVERY_API_BASE_URL=''
ECOMMERCE_BASE_URL='http://localhost:18130'
FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico
LANGUAGE_PREFERENCE_COOKIE_NAME='openedx-language-preference'
LMS_BASE_URL='http://localhost:18000'
LOGIN_URL='http://localhost:18000/login'
LOGO_TRADEMARK_URL=https://edx-cdn.org/v3/default/logo-trademark.svg
LOGO_URL=https://edx-cdn.org/v3/default/logo.svg
LOGO_WHITE_URL=https://edx-cdn.org/v3/default/logo-white.svg
LOGOUT_URL='http://localhost:18000/logout'
MARKETING_SITE_BASE_URL='http://localhost:18000'
NODE_ENV=null
ORDER_HISTORY_URL='localhost:1996/orders'
MARKETING_SITE_BASE_URL='http://localhost:5335'
NODE_ENV=''
ORDER_HISTORY_URL='http://localhost:1996/orders'
PUBLISHER_BASE_URL=''
REFRESH_ACCESS_TOKEN_ENDPOINT='http://localhost:18000/login_refresh'
SEGMENT_KEY=null
SITE_NAME='edX'
SEGMENT_KEY=''
SITE_NAME=localhost
STUDIO_BASE_URL=''
SUPPORT_URL='http://localhost:18000/support'
USER_INFO_COOKIE_NAME='edx-user-info'
COACHING_ENABLED=''
ENABLE_COPPA_COMPLIANCE=''
ENABLE_ACCOUNT_DELETION=''
SHOW_PUSH_CHANNEL=''
SHOW_EMAIL_CHANNEL=''
ENABLE_DOB_UPDATE=''
MARKETING_EMAILS_OPT_IN=''
APP_ID=
MFE_CONFIG_API_URL=
LEARNER_FEEDBACK_URL=''
SUPPORT_URL_TO_UNLINK_SOCIAL_MEDIA_ACCOUNT='https://help.edx.org/edxlearner/s/article/How-do-I-link-or-unlink-my-edX-account-to-a-social-media-account'
COUNTRIES_WITH_DELETE_ACCOUNT_DISABLED='[]'
PARAGON_THEME_URLS={}

View File

@@ -3,3 +3,4 @@ dist/
node_modules/
__mocks__/
__snapshots__/
src/i18n/messages/

View File

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

24
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,24 @@
### Description
Include a description of your changes here, along with a link to any relevant Jira tickets and/or GitHub issues.
#### How Has This Been Tested?
Please describe in detail how you tested your changes.
#### Screenshots/sandbox (optional):
Include a link to the sandbox for design changes or screenshot for before and after. **Remove this section if it's not applicable.**
|Before|After|
|-------|-----|
| | |
#### Merge Checklist
* [ ] If your update includes visual changes, have they been reviewed by a designer? Send them a link to the Sandbox, if applicable.
* [ ] Is there adequate test coverage for your changes?
#### Post-merge Checklist
* [ ] Deploy the changes to prod after verifying on stage or ask **@jacobo-dominguez-wgu** to do it.
* [ ] 🎉 🙌 Celebrate! Thanks for your contribution.

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 }}

View File

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

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

@@ -0,0 +1,29 @@
name: ci
on:
push:
branches:
- master
pull_request:
jobs:
tests:
runs-on: ubuntu-latest
strategy:
matrix:
npm-test:
- i18n_extract
- lint
- test
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version-file: '.nvmrc'
- run: make requirements
- run: make test NPM_TESTS=build
- run: make test NPM_TESTS=${{ matrix.npm-test }}
- name: Coverage
if: matrix.npm-test == 'test'
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true

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,13 @@
#check package-lock file version
name: Lockfile Version check
on:
push:
branches:
- master
pull_request:
jobs:
version-check:
uses: openedx/.github/.github/workflows/lockfileversion-check-v3.yml@master

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

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

View File

@@ -0,0 +1,12 @@
name: Update Browserslist DB
on:
schedule:
- cron: '0 0 * * 1'
workflow_dispatch:
jobs:
update-browserslist:
uses: openedx/.github/.github/workflows/update-browserslist-db.yml@master
secrets:
requirements_bot_github_token: ${{ secrets.requirements_bot_github_token }}

2
.gitignore vendored
View File

@@ -16,3 +16,5 @@ temp/babel-plugin-react-intl
*~
/temp
/.vscode
/module.config.js
src/i18n/messages/

1
.nvmrc Normal file
View File

@@ -0,0 +1 @@
24

View File

@@ -1,15 +0,0 @@
language: node_js
node_js: 12
before_install:
- npm install -g npm@6
install:
- npm ci
script:
- make validate-no-uncommitted-package-lock-changes
- npm run i18n_extract
- npm run lint
- npm run test
- npm run build
- npm run is-es5
after_success:
- codecov

View File

@@ -1,8 +0,0 @@
[main]
host = https://www.transifex.com
[edx-platform.frontend-app-account]
file_filter = src/i18n/messages/<lang>.json
source_file = src/i18n/transifex_input.json
source_lang = en
type = KEYVALUEJSON

52
Makefile Executable file → Normal file
View File

@@ -1,17 +1,29 @@
transifex_resource = frontend-app-account
transifex_langs = "ar,fr,es_419,zh_CN"
intl_imports = ./node_modules/.bin/intl-imports.js
transifex_utils = ./node_modules/.bin/transifex-utils.js
i18n = ./src/i18n
transifex_input = $(i18n)/transifex_input.json
tx_url1 = https://www.transifex.com/api/2/project/edx-platform/resource/$(transifex_resource)/translation/en/strings/
tx_url2 = https://www.transifex.com/api/2/project/edx-platform/resource/$(transifex_resource)/source/
# This directory must match .babelrc .
transifex_temp = ./temp/babel-plugin-react-intl
transifex_temp = ./temp/babel-plugin-formatjs
requirements:
npm install
NPM_TESTS=build i18n_extract lint test
.PHONY: test
test: $(addprefix test.npm.,$(NPM_TESTS)) ## validate ci suite
.PHONY: test.npm.*
test.npm.%: validate-no-uncommitted-package-lock-changes
test -d node_modules || $(MAKE) requirements
npm run $(*)
.PHONY: requirements
precommit:
npm run lint
npm audit
requirements: ## install ci requirements
npm ci
i18n.extract:
# Pulling display strings from .jsx files into .json files...
@@ -29,20 +41,18 @@ detect_changed_source_translations:
# Checking for changed translations...
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/reactifex/bash_scripts/get_hashed_strings.sh $(tx_url1)
# Writing out comments to file...
$(transifex_utils) $(transifex_temp) --comments
# Pushing comments to Transifex...
./node_modules/reactifex/bash_scripts/put_comments.sh $(tx_url2)
# Pulls translations from Transifex.
pull_translations:
tx pull -f --mode reviewed --language=$(transifex_langs)
rm -rf src/i18n/messages
mkdir src/i18n/messages
cd src/i18n/messages \
&& atlas pull $(ATLAS_OPTIONS) \
translations/frontend-platform/src/i18n/messages:frontend-platform \
translations/paragon/src/i18n/messages:paragon \
translations/frontend-component-footer/src/i18n/messages:frontend-component-footer \
translations/frontend-component-header/src/i18n/messages:frontend-component-header \
translations/frontend-app-account/src/i18n/messages:frontend-app-account
$(intl_imports) frontend-platform paragon frontend-component-header frontend-component-footer frontend-app-account
# This target is used by Travis.
validate-no-uncommitted-package-lock-changes:

View File

@@ -1,55 +1,224 @@
|Build Status| |Codecov| |npm_version| |npm_downloads| |license| |semantic-release|
####################
frontend-app-account
====================
####################
This is a micro-frontend application responsible for the display and updating of a user's account information. Please tag **@edx/arch-team** on any PRs or issues.
|ci-badge| |Codecov| |npm_version| |npm_downloads| |license| |semantic-release|
Development
-----------
Start Devstack
^^^^^^^^^^^^^^
********
Purpose
********
To use this application `devstack <https://github.com/edx/devstack>`__ must be running and you must be logged into it.
This is a micro-frontend application responsible for the display and updating of a user's account information.
- Start devstack
- Log in (http://localhost:18000/login)
What is the domain of this MFE?
Start the development server
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
In this MFE: Private user settings UIs. Public facing profile is in a `separate MFE (Profile) <https://github.com/openedx/frontend-app-profile>`_
In this project, install requirements and start the development server by running:
- Account settings page
- IDV (Identity Verification)
.. code:: bash
***************
Getting Started
***************
npm install
npm start # The server will run on port 1997
Prerequisites
=============
Once the dev server is up visit http://localhost:1997.
`Tutor`_ is currently recommended as a development environment for your
new MFE. Please refer
to the `relevant tutor-mfe documentation`_ to get started using it.
Configuration and Deployment
----------------------------
.. _Tutor: https://github.com/overhangio/tutor
This MFE is configured via node environment variables supplied at build time. See the .env file for the list of required environment variables. Example build syntax with a single environment variable:
.. _relevant tutor-mfe documentation: https://github.com/overhangio/tutor-mfe?tab=readme-ov-file#mfe-development
Plugins
=======
This MFE can be customized using `Frontend Plugin Framework <https://github.com/openedx/frontend-plugin-framework>`_.
The parts of this MFE that can be customized in that manner are documented `here </src/plugin-slots>`_.
Environment Variables/Setup Notes
=================================
This MFE is configured via the ``frontend-platform`` configuration module. For more information on MFE configuration see the `Configuration documentation`_.
The account settings micro-frontend also supports the following additional variable:
``SUPPORT_URL``
Example: ``https://support.example.com``
The fully-qualified URL to the support page in the target environment.
``PASSWORD_RESET_SUPPORT_LINK``
Examples:
- ``https://support.edx.org/hc/en-us/articles/206212088-What-if-I-did-not-receive-a-password-reset-message-``
- ``mailto:support@example.com``
The fully-qualified URL to the support page or email to request the support from in the target environment.
``ENABLE_ACCOUNT_DELETION``
Example: ``'false'`` | ``''`` (empty strings are true)
Enable the account deletion option, defaults to true.
To disable account deletion set ``ENABLE_ACCOUNT_DELETION`` to ``'false'`` (string), otherwise it will default to true.
Example build syntax with a single environment variable:
.. code:: bash
NODE_ENV=development ACCESS_TOKEN_COOKIE_NAME='edx-jwt-cookie-header-payload' npm run build
For more information see the document: `Configuration documentation`_
For more information see the document: `Micro-frontend applications in Open
edX <https://github.com/edx/edx-developer-docs/blob/5191e800bf16cf42f25c58c58f983bdaf7f9305d/docs/micro-frontends-in-open-edx.rst>`__.
.. _Configuration documentation: https://openedx.github.io/frontend-platform/module-Config.html
Notes
-----
Cloning and Startup
===================
The production Webpack configuration for this repo uses `Purgecss <https://www.purgecss.com/>`__ to remove unused CSS from the production css file. In ``webpack.prod.config.js`` the Purgecss plugin is configured to scan directories to determine what css selectors should remain. Currently the src/ directory is scanned along with all ``@edx/frontend-component*`` node modules and ``@edx/paragon``. **If you add and use a component in this repo that relies on HTML classes or ids for styling you must add it to the Purgecss configuration or it will be unstyled in the production build.**
.. code-block::
.. |Build Status| image:: https://api.travis-ci.org/edx/frontend-app-account.svg?branch=master
:target: https://travis-ci.org/edx/frontend-app-account
1. Clone your new repo:
``git clone https://github.com/openedx/frontend-app-account.git``
2. Use the version of Node specified in the ``.nvmrc`` file.
The current version of the micro-frontend build scripts supports the version of Node found in ``.nvmrc``.
Using other major versions of node *may* work, but this is unsupported. For
convenience, this repository includes an .nvmrc file to help in setting the
correct node version via `nvm <https://github.com/nvm-sh/nvm>`_.
3. Install npm dependencies:
``cd frontend-app-account && npm ci``
4. Start the dev server:
``npm start``
Or for local development with custom configuration:
``npm run dev``
This runs the dev server with PUBLIC_PATH=/account/, MFE_CONFIG_API_URL pointing to localhost:8000, and hosts on apps.local.openedx.io.
Local module development
=========================
To develop locally on modules that are installed into this app, you'll need to create a ``module.config.js``
file (which is git-ignored) that defines where to find your local modules, for instance:
.. code-block:: js
module.exports = {
/*
Modules you want to use from local source code. Adding a module here means that when this app
runs its build, it'll resolve the source from peer directories of this app.
moduleName: the name you use to import code from the module.
dir: The relative path to the module's source code.
dist: The sub-directory of the source code where it puts its build artifact. Often "dist", though you
may want to use "src" if the module installs React as a peer/dev dependency.
*/
localModules: [
{ moduleName: '@openedx/paragon/scss', dir: '../paragon', dist: 'scss' },
{ moduleName: '@openedx/paragon', dir: '../paragon', dist: 'dist' },
{ moduleName: '@openedx/frontend-enterprise', dir: '../frontend-enterprise', dist: 'src' },
{ moduleName: '@openedx/frontend-platform', dir: '../frontend-platform', dist: 'dist' },
],
};
See https://github.com/openedx/frontend-build#local-module-configuration-for-webpack for more details.
Known Issues
===========
None
Development Roadmap
===================
We don't have anything planned for the core of the MFE (the account settings page) - this MFE is currently in maintenance mode.
There may be a replacement for IDV coming down the pipe, so that may be DEPRed.
License
=======
The code in this repository is licensed under the AGPLv3 unless otherwise
noted.
Please see `LICENSE <LICENSE>`_ for details.
Contributing
============
Contributions are very welcome. Please read `How To Contribute`_ for details.
.. _How To Contribute: https://openedx.org/r/how-to-contribute
This project is currently accepting all types of contributions, bug fixes,
security fixes, maintenance work, or new features. However, please make sure
to have a discussion about your new feature idea with the maintainers prior to
beginning development to maximize the chances of your change being accepted.
You can start a conversation by creating a new issue on this repo summarizing
your idea.
Getting Help
===========
If you're having trouble, we have discussion forums at
https://discuss.openedx.org where you can connect with others in the community.
Our real-time conversations are on Slack. You can request a `Slack
invitation`_, then join our `community Slack workspace`_. Because this is a
frontend repository, the best place to discuss it would be in the `#wg-frontend
channel`_.
For anything non-trivial, the best path is to open an issue in this repository
with as many details about the issue you are facing as you can provide.
https://github.com/openedx/frontend-app-account/issues
For more information about these options, see the `Getting Help`_ page.
.. _Slack invitation: https://openedx.org/slack
.. _community Slack workspace: https://openedx.slack.com/
.. _#wg-frontend channel: https://openedx.slack.com/archives/C04BM6YC7A6
.. _Getting Help: https://openedx.org/community/connect
The Open edX Code of Conduct
============================
All community members are expected to follow the `Open edX Code of Conduct`_.
.. _Open edX Code of Conduct: https://openedx.org/code-of-conduct/
People
======
The assigned maintainers for this component and other project details may be found in Backstage or from inspecting catalog-info.yaml.
Reporting Security Issues
=========================
Please do not report security issues in public. Please email security@openedx.org.
==============================
.. |ci-badge| image:: https://github.com/openedx/edx-developer-docs/actions/workflows/ci.yml/badge.svg
:target: https://github.com/openedx/edx-developer-docs/actions/workflows/ci.yml
:alt: Continuous Integration
.. |Codecov| image:: https://img.shields.io/codecov/c/github/edx/frontend-app-account
:target: https://codecov.io/gh/edx/frontend-app-account
:target: https://codecov.io/gh/openedx/frontend-app-account/
.. |npm_version| image:: https://img.shields.io/npm/v/@edx/frontend-app-account.svg
:target: @edx/frontend-app-account
.. |npm_downloads| image:: https://img.shields.io/npm/dt/@edx/frontend-app-account.svg

19
catalog-info.yaml Normal file
View File

@@ -0,0 +1,19 @@
# 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-app-account'
description: "Open edX micro-frontend application for managing user account information."
links:
- url: "https://github.com/openedx/frontend-app-account"
title: "Frontend app account"
icon: "Web"
annotations:
openedx.org/arch-interest-groups: ""
openedx.org/release: "master"
spec:
owner: jacobo-dominguez-wgu
type: 'website'
lifecycle: 'production'

15
codecov.yml Normal file
View File

@@ -0,0 +1,15 @@
coverage:
status:
project:
default:
enabled: yes
target: auto
threshold: 0%
patch:
default:
enabled: yes
target: auto
threshold: 0%
ignore:
- "src/i18n"
- "src/index.jsx"

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

View File

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

View File

@@ -1,7 +0,0 @@
# This file describes this Open edX repo, as described in OEP-2:
# https://open-edx-proposals.readthedocs.io/en/latest/oep-0002-bp-repo-metadata.html#specification
nick: acct
oeps: {}
owner: edx/arch-team
openedx-release: {ref: master}

39885
package-lock.json generated

File diff suppressed because it is too large Load Diff

108
package.json Executable file → Normal file
View File

@@ -6,44 +6,51 @@
"license": "AGPL-3.0",
"repository": {
"type": "git",
"url": "git+https://github.com/edx/frontend-app-account.git"
"url": "git+https://github.com/openedx/frontend-app-account.git"
},
"scripts": {
"build": "fedx-scripts webpack",
"i18n_extract": "BABEL_ENV=i18n fedx-scripts babel src --quiet > /dev/null",
"is-es5": "es-check es5 ./dist/*.js",
"lint": "fedx-scripts eslint",
"dev": "PUBLIC_PATH=/account/ MFE_CONFIG_API_URL='http://localhost:8000/api/mfe_config/v1' fedx-scripts webpack-dev-server --progress --host apps.local.openedx.io",
"i18n_extract": "fedx-scripts formatjs extract",
"lint": "fedx-scripts eslint --ext .js --ext .jsx .",
"lint:fix": "npm run lint -- --fix",
"snapshot": "fedx-scripts jest --updateSnapshot",
"start": "fedx-scripts webpack-dev-server --progress",
"test": "fedx-scripts jest --coverage --passWithNoTests"
"test": "TZ=UTC fedx-scripts jest --coverage --passWithNoTests"
},
"bugs": {
"url": "https://github.com/edx/frontend-app-account/issues"
"url": "https://github.com/openedx/frontend-app-account/issues"
},
"homepage": "https://github.com/edx/frontend-app-account#readme",
"homepage": "https://github.com/openedx/frontend-app-account#readme",
"publishConfig": {
"access": "public"
},
"browserslist": [
"last 2 versions",
"ie 11"
"extends @edx/browserslist-config"
],
"dependencies": {
"@edx/frontend-component-footer": "10.0.9",
"@edx/frontend-component-header": "2.0.5",
"@edx/frontend-platform": "1.1.14",
"@edx/paragon": "7.1.5",
"@fortawesome/fontawesome-svg-core": "1.2.28",
"@fortawesome/free-brands-svg-icons": "5.8.2",
"@fortawesome/free-regular-svg-icons": "5.7.2",
"@fortawesome/free-solid-svg-icons": "5.8.2",
"@fortawesome/react-fontawesome": "0.1.9",
"babel-polyfill": "6.26.0",
"classnames": "2.2.6",
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
"@edx/frontend-component-footer": "^14.6.0",
"@edx/frontend-component-header": "^6.2.0",
"@edx/frontend-platform": "^8.4.0",
"@edx/openedx-atlas": "^0.7.0",
"@fortawesome/fontawesome-svg-core": "^6.6.0",
"@fortawesome/free-brands-svg-icons": "^6.6.0",
"@fortawesome/free-regular-svg-icons": "^6.6.0",
"@fortawesome/free-solid-svg-icons": "^6.6.0",
"@fortawesome/react-fontawesome": "0.2.6",
"@openedx/frontend-plugin-framework": "^1.7.0",
"@openedx/paragon": "^23.4.5",
"@tensorflow-models/blazeface": "0.1.0",
"@tensorflow/tfjs-converter": "4.22.0",
"@tensorflow/tfjs-core": "4.22.0",
"bowser": "2.14.1",
"classnames": "2.5.1",
"core-js": "3.48.0",
"font-awesome": "4.7.0",
"form-urlencoded": "4.0.1",
"formdata-polyfill": "3.0.19",
"history": "4.10.1",
"form-urlencoded": "6.1.6",
"formdata-polyfill": "4.0.10",
"jslib-html5-camera-photo": "3.3.4",
"lodash.camelcase": "4.3.0",
"lodash.debounce": "4.0.8",
"lodash.findindex": "4.6.0",
@@ -52,37 +59,36 @@
"lodash.merge": "4.6.2",
"lodash.omit": "4.5.0",
"lodash.pick": "4.4.0",
"lodash.pickby": "4.6.0",
"lodash.snakecase": "4.1.1",
"memoize-one": "5.1.1",
"newrelic": "5.13.1",
"prop-types": "15.7.2",
"react": "16.10.2",
"react-dom": "16.10.2",
"react-redux": "7.1.3",
"react-router": "5.1.2",
"react-router-dom": "5.1.2",
"react-router-hash-link": "1.2.2",
"react-scrollspy": "3.4.2",
"react-transition-group": "4.3.0",
"redux": "4.0.5",
"redux-devtools-extension": "2.13.8",
"long": "5.3.2",
"memoize-one": "^6.0.0",
"prop-types": "15.8.1",
"qs": "6.15.0",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-helmet": "6.1.0",
"react-redux": "7.2.9",
"react-router": "^6.25.1",
"react-router-dom": "^6.25.1",
"react-router-hash-link": "2.4.3",
"react-scrollspy": "3.4.3",
"react-transition-group": "4.4.5",
"redux": "4.2.1",
"redux-devtools-extension": "2.13.9",
"redux-logger": "3.0.6",
"redux-saga": "1.1.3",
"redux-thunk": "2.3.0",
"reselect": "4.0.0",
"universal-cookie": "4.0.3"
"redux-saga": "1.4.2",
"redux-thunk": "2.4.2",
"regenerator-runtime": "0.14.1",
"reselect": "^5.1.1",
"universal-cookie": "7.2.2"
},
"devDependencies": {
"@edx/frontend-build": "2.0.6",
"codecov": "3.6.5",
"enzyme": "3.10.0",
"enzyme-adapter-react-16": "1.15.2",
"es-check": "5.0.0",
"glob": "7.1.6",
"husky": "3.0.9",
"purgecss-webpack-plugin": "1.6.0",
"react-test-renderer": "16.8.6",
"reactifex": "1.1.1",
"redux-mock-store": "1.5.4"
"@edx/browserslist-config": "1.5.1",
"@openedx/frontend-build": "^14.6.2",
"@testing-library/jest-dom": "6.9.1",
"@testing-library/react": "14.3.1",
"react-test-renderer": "^18.3.1",
"redux-mock-store": "1.5.5"
}
}

View File

@@ -1,12 +1,148 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en-us">
<head>
<title>Account | edX</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
<title>Account | <%= process.env.SITE_NAME %></title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
rel="shortcut icon"
href="<%=htmlWebpackPlugin.options.FAVICON_URL%>"
type="image/x-icon"
/>
<% if (process.env.OPTIMIZELY_PROJECT_ID) { %>
<script src="<%= process.env.MARKETING_SITE_BASE_URL %>/optimizelyjs/<%= process.env.OPTIMIZELY_PROJECT_ID %>.js"></script>
<% } %>
</head>
<body>
<!-- begin usabilla live embed code -->
<script defer type="text/javascript">
window.lightningjs ||
(function (n) {
var e = "lightningjs";
function t(e, t) {
var r, i, a, o, d, c;
return (
t && (t += (/\?/.test(t) ? "&" : "?") + "lv=1"),
n[e] ||
((r = window),
(i = document),
(a = e),
(o = i.location.protocol),
(d = "load"),
(c = 0),
(function () {
n[a] = function () {
var t = arguments,
i = this,
o = ++c,
d = (i && i != r && i.id) || 0;
function s() {
return (s.id = o), n[a].apply(s, arguments);
}
return (
(e.s = e.s || []).push([o, d, t]),
(s.then = function (n, t, r) {
var i = (e.fh[o] = e.fh[o] || []),
a = (e.eh[o] = e.eh[o] || []),
d = (e.ph[o] = e.ph[o] || []);
return (
n && i.push(n), t && a.push(t), r && d.push(r), s
);
}),
s
);
};
var e = (n[a]._ = {});
function s() {
e.P(d), (e.w = 1), n[a]("_load");
}
(e.fh = {}),
(e.eh = {}),
(e.ph = {}),
(e.l = t
? t.replace(/^\/\//, ("https:" == o ? o : "http:") + "//")
: t),
(e.p = { 0: +new Date() }),
(e.P = function (n) {
e.p[n] = new Date() - e.p[0];
}),
e.w && s(),
r.addEventListener
? r.addEventListener(d, s, !1)
: r.attachEvent("onload", s);
var l = function () {
function n() {
return [
"<!DOCTYPE ",
o,
"><",
o,
"><head></head><",
t,
"><",
r,
' src="',
e.l,
'"></',
r,
"></",
t,
"></",
o,
">",
].join("");
}
var t = "body",
r = "script",
o = "html",
d = i[t];
if (!d) return setTimeout(l, 100);
e.P(1);
var c,
s = i.createElement("div"),
h = s.appendChild(i.createElement("div")),
u = i.createElement("iframe");
(s.style.display = "none"),
(d.insertBefore(s, d.firstChild).id = "lightningjs-" + a),
(u.frameBorder = "0"),
(u.id = "lightningjs-frame-" + a),
/MSIE[ ]+6/.test(navigator.userAgent) &&
(u.src = "javascript:false"),
(u.allowTransparency = "true"),
h.appendChild(u);
try {
u.contentWindow.document.open();
} catch (n) {
(e.domain = i.domain),
(c =
"javascript:var d=document.open();d.domain='" +
i.domain +
"';"),
(u.src = c + "void(0);");
}
try {
var p = u.contentWindow.document;
p.write(n()), p.close();
} catch (e) {
u.src =
c +
'd.write("' +
n().replace(/"/g, String.fromCharCode(92) + '"') +
'");d.close();';
}
e.P(2);
};
e.l && l();
})()),
(n[e].lv = "1"),
n[e]
);
}
var r = (window.lightningjs = t(e));
(r.require = t), (r.modules = n);
})({});
</script>
<!-- end usabilla live embed code -->
<div id="root"></div>
</body>
</html>

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", "@openedx"],
"matchUpdateTypes": ["minor", "patch"],
"automerge": true
}
],
"timezone": "America/New_York"
}

View File

@@ -1,5 +1,5 @@
import { AppContext } from '@edx/frontend-platform/react';
import { getConfig, history, getQueryParameters } from '@edx/frontend-platform';
import { getConfig, getQueryParameters } from '@edx/frontend-platform';
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
@@ -13,50 +13,72 @@ import {
getCountryList,
getLanguageList,
} from '@edx/frontend-platform/i18n';
import { Hyperlink } from '@edx/paragon';
import {
Container, Hyperlink, Icon, Alert,
} from '@openedx/paragon';
import { CheckCircle, Error, WarningFilled } from '@openedx/paragon/icons';
import messages from './AccountSettingsPage.messages';
import { fetchSettings, saveSettings, updateDraft } from './data/actions';
import {
fetchSettings,
saveMultipleSettings,
saveSettings,
updateDraft,
beginNameChange,
} from './data/actions';
import { accountSettingsPageSelector } from './data/selectors';
import PageLoading from './PageLoading';
import Alert from './Alert';
import JumpNav from './JumpNav';
import DeleteAccount from './delete-account';
import EditableField from './EditableField';
import EditableSelectField from './EditableSelectField';
import ResetPassword from './reset-password';
import NameChange from './name-change';
import ThirdPartyAuth from './third-party-auth';
import BetaLanguageBanner from './BetaLanguageBanner';
import EmailField from './EmailField';
import OneTimeDismissibleAlert from './OneTimeDismissibleAlert';
import DOBModal from './DOBForm';
import {
YEAR_OF_BIRTH_OPTIONS,
EDUCATION_LEVELS,
GENDER_OPTIONS,
COUNTRY_WITH_STATES,
COPPA_COMPLIANCE_YEAR,
WORK_EXPERIENCE_OPTIONS,
getStatesList,
FIELD_LABELS,
} from './data/constants';
import { fetchSiteLanguages } from './site-language';
import CoachingToggle from './coaching/CoachingToggle';
import { fetchNotificationPreferences } from '../notification-preferences/data/thunks';
import NotificationSettings from '../notification-preferences/NotificationSettings';
import { withLocation, withNavigate } from './hoc';
import AdditionalProfileFieldsSlot from '../plugin-slots/AdditionalProfileFieldsSlot';
class AccountSettingsPage extends React.Component {
constructor(props, context) {
super(props, context);
// If there is a "duplicate_provider" query parameter, that's the backend's
// way of telling us that the provider account the user tried to link is already linked
// to another Open edX account. We use this to display a message to that effect, and remove the
// parameter from the URL.
const duplicateTpaProvider = getQueryParameters().duplicate_provider;
if (duplicateTpaProvider !== undefined) {
history.replace(history.location.pathname);
}
this.state = {
duplicateTpaProvider,
};
this.navLinkRefs = {
'#basic-information': React.createRef(),
'#profile-information': React.createRef(),
'#social-media': React.createRef(),
'#notifications': React.createRef(),
'#site-preferences': React.createRef(),
'#linked-accounts': React.createRef(),
'#delete-account': React.createRef(),
};
}
componentDidMount() {
this.props.fetchNotificationPreferences();
this.props.fetchSettings();
this.props.fetchSiteLanguages();
this.props.fetchSiteLanguages(this.props.navigate);
sendTrackingLogEvent('edx.user.settings.viewed', {
page: 'account',
visibility: null,
@@ -64,6 +86,20 @@ class AccountSettingsPage extends React.Component {
});
}
componentDidUpdate(prevProps) {
if (prevProps.loading && !prevProps.loaded && this.props.loaded) {
const locationHash = global.location.hash;
// Check for the locationHash in the URL and then scroll to it if it is in the
// NavLinks list
if (typeof locationHash !== 'string') {
return;
}
if (Object.keys(this.navLinkRefs).includes(locationHash) && this.navLinkRefs[locationHash].current) {
window.scrollTo(0, this.navLinkRefs[locationHash].current.offsetTop);
}
}
}
// NOTE: We need 'locale' for the memoization in getLocalizedTimeZoneOptions. Don't remove it!
// eslint-disable-next-line no-unused-vars
getLocalizedTimeZoneOptions = memoize((timeZoneOptions, countryTimeZoneOptions, locale) => {
@@ -88,7 +124,15 @@ class AccountSettingsPage extends React.Component {
countryOptions: [{
value: '',
label: this.props.intl.formatMessage(messages['account.settings.field.country.options.empty']),
}].concat(getCountryList(locale).map(({ code, name }) => ({ value: code, label: name }))),
}].concat(
this.removeDisabledCountries(
getCountryList(locale).map(({ code, name }) => ({
value: code,
label: name,
disabled: this.isDisabledCountry(code),
})),
),
),
stateOptions: [{
value: '',
label: this.props.intl.formatMessage(messages['account.settings.field.state.options.empty']),
@@ -109,8 +153,83 @@ class AccountSettingsPage extends React.Component {
value: key,
label: this.props.intl.formatMessage(messages[`account.settings.field.gender.options.${key || 'empty'}`]),
})),
workExperienceOptions: WORK_EXPERIENCE_OPTIONS.map(key => ({
value: key,
label: key === '' ? this.props.intl.formatMessage(messages['account.settings.field.work.experience.options.empty']) : key,
})),
}));
canDeleteAccount = () => {
const { committedValues } = this.props;
return !getConfig().COUNTRIES_WITH_DELETE_ACCOUNT_DISABLED.includes(committedValues.country);
};
removeDisabledCountries = (countryList) => {
const { countriesCodesList, committedValues } = this.props;
const committedCountry = committedValues?.country;
if (!countriesCodesList.length) {
return countryList;
}
return countryList.filter(({ value }) => value === committedCountry || countriesCodesList.find(x => x === value));
};
handleEditableFieldChange = (name, value) => {
this.props.updateDraft(name, value);
};
handleSubmit = (formId, values) => {
if (formId === FIELD_LABELS.COUNTRY && this.isDisabledCountry(values)) {
return;
}
const { formValues } = this.props;
let extendedProfileObject = {};
if ('extended_profile' in formValues && formValues.extended_profile.some((field) => field.field_name === formId)) {
extendedProfileObject = {
extended_profile: formValues.extended_profile.map(field => (field.field_name === formId
? { ...field, field_value: values }
: field)),
};
}
this.props.saveSettings(formId, values, extendedProfileObject);
};
handleSubmitProfileName = (formId, values) => {
if (Object.keys(this.props.drafts).includes('useVerifiedNameForCerts')) {
this.props.saveMultipleSettings([
{
formId,
commitValues: values,
},
{
formId: 'useVerifiedNameForCerts',
commitValues: this.props.formValues.useVerifiedNameForCerts,
},
], formId);
} else {
this.props.saveSettings(formId, values);
}
};
handleSubmitVerifiedName = (formId, values) => {
if (Object.keys(this.props.drafts).includes('useVerifiedNameForCerts')) {
this.props.saveSettings('useVerifiedNameForCerts', this.props.formValues.useVerifiedNameForCerts);
}
if (values !== this.props.committedValues?.verified_name) {
this.props.beginNameChange(formId);
} else {
this.props.saveSettings(formId, values);
}
};
isDisabledCountry = (country) => {
const { countriesCodesList } = this.props;
return countriesCodesList.length > 0 && !countriesCodesList.find(x => x === country);
};
isEditable(fieldName) {
return !this.props.staticFields.includes(fieldName);
}
@@ -121,28 +240,27 @@ class AccountSettingsPage extends React.Component {
return Boolean(this.props.profileDataManager);
}
handleEditableFieldChange = (name, value) => {
this.props.updateDraft(name, value);
};
handleSubmit = (formId, values) => {
this.props.saveSettings(formId, values);
};
renderDuplicateTpaProviderMessage() {
if (!this.state.duplicateTpaProvider) {
return null;
}
// If there is a "duplicate_provider" query parameter, that's the backend's
// way of telling us that the provider account the user tried to link is already linked
// to another user account on the platform. We use this to display a message to that effect,
// and remove the parameter from the URL.
this.props.navigate(this.props.location, { replace: true });
return (
<div>
<Alert className="alert alert-danger" role="alert">
<Alert variant="danger">
<FormattedMessage
id="account.settings.message.duplicate.tpa.provider"
defaultMessage="The {provider} account you selected is already linked to another edX account."
description="alert message informing the user that the third-party account they attempted to link is already linked to another edX account"
defaultMessage="The {provider} account you selected is already linked to another {siteName} account."
description="alert message informing the user that the third-party account they attempted to link is already linked to another account"
values={{
provider: <b>{this.state.duplicateTpaProvider}</b>,
siteName: getConfig().SITE_NAME,
}}
/>
</Alert>
@@ -157,7 +275,7 @@ class AccountSettingsPage extends React.Component {
return (
<div>
<Alert className="alert alert-primary" role="alert">
<Alert variant="info">
<FormattedMessage
id="account.settings.message.managed.settings"
defaultMessage="Your profile settings are managed by {managerTitle}. Contact your administrator or {support} for help."
@@ -180,6 +298,160 @@ class AccountSettingsPage extends React.Component {
);
}
renderFullNameHelpText = (status, proctoredExamId) => {
if (!this.props.verifiedNameHistory) {
return this.props.intl.formatMessage(messages['account.settings.field.full.name.help.text']);
}
let messageString = 'account.settings.field.full.name.help.text';
if (status === 'submitted') {
messageString += '.submitted';
if (proctoredExamId) {
messageString += '.proctored';
}
} else {
messageString += '.default';
}
if (!this.props.committedValues.useVerifiedNameForCerts) {
messageString += '.certificate';
}
return this.props.intl.formatMessage(messages[messageString]);
};
renderVerifiedNameSuccessMessage = (verifiedName, created) => {
const dateValue = new Date(created).valueOf();
const id = `dismissedVerifiedNameSuccessMessage-${verifiedName}-${dateValue}`;
return (
<OneTimeDismissibleAlert
id={id}
variant="success"
icon={CheckCircle}
header={this.props.intl.formatMessage(messages['account.settings.field.name.verified.success.message.header'])}
body={this.props.intl.formatMessage(messages['account.settings.field.name.verified.success.message'])}
/>
);
};
renderVerifiedNameFailureMessage = (verifiedName, created) => {
const dateValue = new Date(created).valueOf();
const id = `dismissedVerifiedNameFailureMessage-${verifiedName}-${dateValue}`;
return (
<OneTimeDismissibleAlert
id={id}
variant="danger"
icon={Error}
header={this.props.intl.formatMessage(messages['account.settings.field.name.verified.failure.message.header'])}
body={
(
<div className="d-flex flex-row">
{this.props.intl.formatMessage(messages['account.settings.field.name.verified.failure.message'])}
</div>
)
}
/>
);
};
renderVerifiedNameSubmittedMessage = (willCertNameChange) => (
<Alert
variant="warning"
icon={WarningFilled}
>
<Alert.Heading>
{this.props.intl.formatMessage(messages['account.settings.field.name.verified.submitted.message.header'])}
</Alert.Heading>
<p>
{this.props.intl.formatMessage(messages['account.settings.field.name.verified.submitted.message'])}{' '}
{
willCertNameChange
&& this.props.intl.formatMessage(messages['account.settings.field.name.verified.submitted.message.certificate'])
}
</p>
</Alert>
);
renderVerifiedNameMessage = verifiedNameRecord => {
const {
created,
status,
profile_name: profileName,
verified_name: verifiedName,
proctored_exam_attempt_id: proctoredExamId,
} = verifiedNameRecord;
let willCertNameChange = false;
if (
(
// User submitted a profile name change, and uses their profile name on certificates
this.props.committedValues.name !== profileName
&& !this.props.committedValues.useVerifiedNameForCerts
)
|| (
// User submitted a verified name change, and uses their verified name on certificates
this.props.committedValues.name === profileName
&& this.props.committedValues.useVerifiedNameForCerts
)
) {
willCertNameChange = true;
}
if (proctoredExamId) {
return null;
}
switch (status) {
case 'approved':
return this.renderVerifiedNameSuccessMessage(verifiedName, created);
case 'denied':
return this.renderVerifiedNameFailureMessage(verifiedName, created);
case 'submitted':
return this.renderVerifiedNameSubmittedMessage(willCertNameChange);
default:
return null;
}
};
renderVerifiedNameIcon = (status) => {
switch (status) {
case 'approved':
return (<Icon src={CheckCircle} className="ml-1" style={{ height: '18px', width: '18px', color: 'green' }} />);
case 'submitted':
return (<Icon src={WarningFilled} className="ml-1" style={{ height: '18px', width: '18px', color: 'yellow' }} />);
default:
return null;
}
};
renderVerifiedNameHelpText = (status, proctoredExamId) => {
let messageStr = 'account.settings.field.name.verified.help.text';
// add additional string based on status
if (status === 'approved') {
messageStr += '.verified';
} else if (status === 'submitted') {
messageStr += '.submitted';
} else {
return null;
}
// add additional string if verified name came from a proctored exam attempt
if (proctoredExamId) {
messageStr += '.proctored';
}
// add additional string based on certificate name use
if (this.props.committedValues.useVerifiedNameForCerts) {
messageStr += '.certificate';
}
return this.props.intl.formatMessage(messages[messageStr]);
};
renderEmptyStaticFieldMessage() {
if (this.isManagedProfile()) {
return this.props.intl.formatMessage(messages['account.settings.static.field.empty'], {
@@ -189,8 +461,15 @@ class AccountSettingsPage extends React.Component {
return this.props.intl.formatMessage(messages['account.settings.static.field.empty.no.admin']);
}
renderNameChangeModal() {
if (this.props.nameChangeModal && this.props.nameChangeModal.formId) {
return <NameChange targetFormId={this.props.nameChangeModal.formId} />;
}
return null;
}
renderSecondaryEmailField(editableFieldProps) {
if (!Boolean(this.props.formValues.secondary_email_enabled)) {
if (!this.props.formValues.secondary_email_enabled) {
return null;
}
@@ -220,10 +499,15 @@ class AccountSettingsPage extends React.Component {
yearOfBirthOptions,
educationLevelOptions,
genderOptions,
workExperienceOptions,
} = this.getLocalizedOptions(this.context.locale, this.props.formValues.country);
// Show State field only if the country is US (could include Canada later)
const showState = this.props.formValues.country == COUNTRY_WITH_STATES;
const { country } = this.props.formValues;
const showState = country === COUNTRY_WITH_STATES && !this.isDisabledCountry(country);
const { verifiedName } = this.props;
const hasWorkExperience = !!this.props.formValues?.extended_profile?.find(field => field.field_name === 'work_experience');
const timeZoneOptions = this.getLocalizedTimeZoneOptions(
this.props.timeZoneOptions,
@@ -233,110 +517,193 @@ class AccountSettingsPage extends React.Component {
const hasLinkedTPA = findIndex(this.props.tpaProviders, provider => provider.connected) >= 0;
// if user is under 13 and does not have cookie set
const shouldUpdateDOB = (
getConfig().ENABLE_COPPA_COMPLIANCE
&& getConfig().ENABLE_DOB_UPDATE
&& this.props.formValues.year_of_birth.toString() >= COPPA_COMPLIANCE_YEAR.toString()
&& !localStorage.getItem('submittedDOB')
);
return (
<React.Fragment>
<div className="account-section" id="basic-information">
<h2 className="section-heading">
<>
{ shouldUpdateDOB
&& (
<DOBModal
{...editableFieldProps}
/>
)}
<div className="account-section pt-3 mb-5" id="basic-information" ref={this.navLinkRefs['#basic-information']}>
{
this.props.mostRecentVerifiedName
&& this.renderVerifiedNameMessage(this.props.mostRecentVerifiedName)
}
{localStorage.getItem('submittedDOB')
&& (
<OneTimeDismissibleAlert
id="updated-dob"
variant="success"
icon={CheckCircle}
header={this.props.intl.formatMessage(messages['account.settings.field.dob.form.success'])}
body=""
/>
)}
<h2 className="section-heading h4 mb-3">
{this.props.intl.formatMessage(messages['account.settings.section.account.information'])}
</h2>
<p>{this.props.intl.formatMessage(messages['account.settings.section.account.information.description'])}</p>
{this.renderManagedProfileMessage()}
{this.renderNameChangeModal()}
<EditableField
name="username"
type="text"
value={this.props.formValues.username}
label={this.props.intl.formatMessage(messages['account.settings.field.username'])}
helpText={this.props.intl.formatMessage(messages['account.settings.field.username.help.text'])}
helpText={this.props.intl.formatMessage(
messages['account.settings.field.username.help.text'],
{ siteName: getConfig().SITE_NAME },
)}
isEditable={false}
{...editableFieldProps}
/>
<EditableField
name="name"
type="text"
value={this.props.formValues.name}
value={
verifiedName?.status === 'submitted'
&& this.props.formValues.pending_name_change
? this.props.formValues.pending_name_change
: this.props.formValues.name
}
label={this.props.intl.formatMessage(messages['account.settings.field.full.name'])}
emptyLabel={
this.isEditable('name') ?
this.props.intl.formatMessage(messages['account.settings.field.full.name.empty']) :
this.renderEmptyStaticFieldMessage()
this.isEditable('name')
? this.props.intl.formatMessage(messages['account.settings.field.full.name.empty'])
: this.renderEmptyStaticFieldMessage()
}
helpText={this.props.intl.formatMessage(messages['account.settings.field.full.name.help.text'])}
isEditable={this.isEditable('name')}
{...editableFieldProps}
helpText={
verifiedName
? this.renderFullNameHelpText(verifiedName.status, verifiedName.proctored_exam_attempt_id)
: this.props.intl.formatMessage(messages['account.settings.field.full.name.help.text'])
}
isEditable={
verifiedName
? this.isEditable('verifiedName') && this.isEditable('name')
: this.isEditable('name')
}
isGrayedOut={
verifiedName && !this.isEditable('verifiedName')
}
onChange={this.handleEditableFieldChange}
onSubmit={this.handleSubmitProfileName}
/>
{verifiedName
&& (
<EditableField
name="verified_name"
type="text"
value={this.props.formValues.verified_name}
label={
(
<div className="d-flex">
{this.props.intl.formatMessage(messages['account.settings.field.name.verified'])}
{
this.renderVerifiedNameIcon(verifiedName.status)
}
</div>
)
}
helpText={this.renderVerifiedNameHelpText(verifiedName.status, verifiedName.proctored_exam_attempt_id)}
isEditable={this.isEditable('verifiedName')}
isGrayedOut={!this.isEditable('verifiedName')}
onChange={this.handleEditableFieldChange}
onSubmit={this.handleSubmitVerifiedName}
/>
)}
<EmailField
name="email"
label={this.props.intl.formatMessage(messages['account.settings.field.email'])}
emptyLabel={
this.isEditable('email') ?
this.props.intl.formatMessage(messages['account.settings.field.email.empty']) :
this.renderEmptyStaticFieldMessage()
this.isEditable('email')
? this.props.intl.formatMessage(messages['account.settings.field.email.empty'])
: this.renderEmptyStaticFieldMessage()
}
value={this.props.formValues.email}
confirmationMessageDefinition={messages['account.settings.field.email.confirmation']}
helpText={this.props.intl.formatMessage(messages['account.settings.field.email.help.text'])}
helpText={this.props.intl.formatMessage(
messages['account.settings.field.email.help.text'],
{ siteName: getConfig().SITE_NAME },
)}
isEditable={this.isEditable('email')}
{...editableFieldProps}
/>
{this.renderSecondaryEmailField(editableFieldProps)}
<ResetPassword email={this.props.formValues.email} />
<EditableField
name="year_of_birth"
type="select"
label={this.props.intl.formatMessage(messages['account.settings.field.dob'])}
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.dob.empty'])}
value={this.props.formValues.year_of_birth}
options={yearOfBirthOptions}
{...editableFieldProps}
/>
<EditableField
{(!getConfig().ENABLE_COPPA_COMPLIANCE)
&& (
<EditableSelectField
name="year_of_birth"
type="select"
label={this.props.intl.formatMessage(messages['account.settings.field.dob'])}
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.dob.empty'])}
value={this.props.formValues.year_of_birth}
options={yearOfBirthOptions}
{...editableFieldProps}
/>
)}
<EditableSelectField
name="country"
type="select"
value={this.props.formValues.country}
options={countryOptions}
label={this.props.intl.formatMessage(messages['account.settings.field.country'])}
emptyLabel={
this.isEditable('country') ?
this.props.intl.formatMessage(messages['account.settings.field.country.empty']) :
this.renderEmptyStaticFieldMessage()
this.isEditable('country')
? this.props.intl.formatMessage(messages['account.settings.field.country.empty'])
: this.renderEmptyStaticFieldMessage()
}
isEditable={this.isEditable('country')}
{...editableFieldProps}
/>
{showState &&
<EditableField
{showState
&& (
<EditableSelectField
name="state"
type="select"
value={this.props.formValues.state}
options={stateOptions}
label={this.props.intl.formatMessage(messages['account.settings.field.state'])}
emptyLabel={
this.isEditable('state') ?
this.props.intl.formatMessage(messages['account.settings.field.state.empty']) :
this.renderEmptyStaticFieldMessage()
this.isEditable('state')
? this.props.intl.formatMessage(messages['account.settings.field.state.empty'])
: this.renderEmptyStaticFieldMessage()
}
isEditable={this.isEditable('state')}
{...editableFieldProps}
/>
}
)}
</div>
<div className="account-section" id="profile-information">
<h2 className="section-heading">
<div className="account-section pt-3 mb-5" id="profile-information" ref={this.navLinkRefs['#profile-information']}>
<h2 className="section-heading h4 mb-3">
{this.props.intl.formatMessage(messages['account.settings.section.profile.information'])}
</h2>
<EditableField
<EditableSelectField
name="level_of_education"
type="select"
value={this.props.formValues.level_of_education}
options={educationLevelOptions}
options={getConfig().ENABLE_COPPA_COMPLIANCE
? educationLevelOptions.filter(option => option.value !== 'el')
: educationLevelOptions}
label={this.props.intl.formatMessage(messages['account.settings.field.education'])}
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.education.empty'])}
{...editableFieldProps}
/>
<EditableField
<EditableSelectField
name="gender"
type="select"
value={this.props.formValues.gender}
@@ -345,7 +712,19 @@ class AccountSettingsPage extends React.Component {
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.gender.empty'])}
{...editableFieldProps}
/>
<EditableField
{hasWorkExperience
&& (
<EditableSelectField
name="work_experience"
type="select"
value={this.props.formValues?.extended_profile?.find(field => field.field_name === 'work_experience')?.field_value}
options={workExperienceOptions}
label={this.props.intl.formatMessage(messages['account.settings.field.work.experience'])}
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.work.experience.empty'])}
{...editableFieldProps}
/>
)}
<EditableSelectField
name="language_proficiencies"
type="select"
value={this.props.formValues.language_proficiencies}
@@ -354,21 +733,19 @@ class AccountSettingsPage extends React.Component {
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.language.proficiencies.empty'])}
{...editableFieldProps}
/>
{getConfig().COACHING_ENABLED &&
this.props.formValues.coaching.eligible_for_coaching &&
<CoachingToggle
name="coaching"
phone_number={this.props.formValues.phone_number}
coaching={this.props.formValues.coaching}
/>
}
</div>
<div className="account-section" id="social-media">
<h2 className="section-heading">
<AdditionalProfileFieldsSlot />
</div>
<div className="account-section pt-3 mb-6" id="social-media">
<h2 className="section-heading h4 mb-3">
{this.props.intl.formatMessage(messages['account.settings.section.social.media'])}
</h2>
<p>{this.props.intl.formatMessage(messages['account.settings.section.social.media.description'])}</p>
<p>
{this.props.intl.formatMessage(
messages['account.settings.section.social.media.description'],
{ siteName: getConfig().SITE_NAME },
)}
</p>
<EditableField
name="social_link_linkedin"
@@ -387,22 +764,25 @@ class AccountSettingsPage extends React.Component {
{...editableFieldProps}
/>
<EditableField
name="social_link_twitter"
name="social_link_x"
type="text"
value={this.props.formValues.social_link_twitter}
label={this.props.intl.formatMessage(messages['account.settings.field.social.platform.name.twitter'])}
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.social.platform.name.twitter.empty'])}
value={this.props.formValues.social_link_x}
label={this.props.intl.formatMessage(messages['account.settings.field.social.platform.name.xTwitter'])}
emptyLabel={this.props.intl.formatMessage(messages['account.settings.field.social.platform.name.xTwitter.empty'])}
{...editableFieldProps}
/>
</div>
<div className="account-section" id="site-preferences">
<h2 className="section-heading">
<div className="border border-light-700" />
<div className="mt-6" id="notifications" ref={this.navLinkRefs['#notifications']}>
<NotificationSettings />
</div>
<div className="account-section mb-5" id="site-preferences" ref={this.navLinkRefs['#site-preferences']}>
<h2 className="section-heading h4 mb-3">
{this.props.intl.formatMessage(messages['account.settings.section.site.preferences'])}
</h2>
<BetaLanguageBanner />
<EditableField
<EditableSelectField
name="siteLanguage"
type="select"
options={this.props.siteLanguageOptions}
@@ -411,7 +791,7 @@ class AccountSettingsPage extends React.Component {
helpText={this.props.intl.formatMessage(messages['account.settings.field.site.language.help.text'])}
{...editableFieldProps}
/>
<EditableField
<EditableSelectField
name="time_zone"
type="select"
value={this.props.formValues.time_zone}
@@ -427,20 +807,27 @@ class AccountSettingsPage extends React.Component {
/>
</div>
<div className="account-section" id="linked-accounts">
<h2 className="section-heading">{this.props.intl.formatMessage(messages['account.settings.section.linked.accounts'])}</h2>
<p>{this.props.intl.formatMessage(messages['account.settings.section.linked.accounts.description'])}</p>
<div className="account-section pt-3 mb-5" id="linked-accounts" ref={this.navLinkRefs['#linked-accounts']}>
<h2 className="section-heading h4 mb-3">{this.props.intl.formatMessage(messages['account.settings.section.linked.accounts'])}</h2>
<p>
{this.props.intl.formatMessage(
messages['account.settings.section.linked.accounts.description'],
{ siteName: getConfig().SITE_NAME },
)}
</p>
<ThirdPartyAuth />
</div>
<div className="account-section" id="delete-account">
<DeleteAccount
isVerifiedAccount={this.props.isActive}
hasLinkedTPA={hasLinkedTPA}
/>
</div>
</React.Fragment>
{getConfig().ENABLE_ACCOUNT_DELETION && (
<div className="account-section pt-3 mb-5" id="delete-account" ref={this.navLinkRefs['#delete-account']}>
<DeleteAccount
isVerifiedAccount={this.props.isActive}
hasLinkedTPA={hasLinkedTPA}
canDeleteAccount={this.canDeleteAccount()}
/>
</div>
)}
</>
);
}
@@ -468,7 +855,7 @@ class AccountSettingsPage extends React.Component {
} = this.props;
return (
<div className="page__account-settings container-fluid py-5">
<Container className="page__account-settings py-5" size="xl">
{this.renderDuplicateTpaProviderMessage()}
<h1 className="mb-4">
{this.props.intl.formatMessage(messages['account.settings.page.heading'])}
@@ -485,7 +872,7 @@ class AccountSettingsPage extends React.Component {
</div>
</div>
</div>
</div>
</Container>
);
}
}
@@ -504,22 +891,36 @@ AccountSettingsPage.propTypes = {
name: PropTypes.string,
email: PropTypes.string,
secondary_email: PropTypes.string,
secondary_email_enabled: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
year_of_birth: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
country: PropTypes.string,
level_of_education: PropTypes.string,
gender: PropTypes.string,
extended_profile: PropTypes.arrayOf(PropTypes.shape({
field_name: PropTypes.string,
field_value: PropTypes.string,
})),
language_proficiencies: PropTypes.string,
pending_name_change: PropTypes.string,
phone_number: PropTypes.string,
social_link_linkedin: PropTypes.string,
social_link_facebook: PropTypes.string,
social_link_twitter: PropTypes.string,
social_link_x: PropTypes.string,
time_zone: PropTypes.string,
coaching: PropTypes.shape({
coaching_consent: PropTypes.bool.isRequired,
user: PropTypes.number.isRequired,
eligible_for_coaching: PropTypes.bool.isRequired,
}),
state: PropTypes.string,
useVerifiedNameForCerts: PropTypes.bool.isRequired,
verified_name: PropTypes.string,
}).isRequired,
committedValues: PropTypes.shape({
name: PropTypes.string,
useVerifiedNameForCerts: PropTypes.bool,
verified_name: PropTypes.string,
country: PropTypes.string,
}),
drafts: PropTypes.shape({}),
formErrors: PropTypes.shape({
name: PropTypes.string,
}),
siteLanguage: PropTypes.shape({
previousValue: PropTypes.string,
draft: PropTypes.string,
@@ -543,15 +944,58 @@ AccountSettingsPage.propTypes = {
})),
fetchSiteLanguages: PropTypes.func.isRequired,
updateDraft: PropTypes.func.isRequired,
saveMultipleSettings: PropTypes.func.isRequired,
saveSettings: PropTypes.func.isRequired,
fetchSettings: PropTypes.func.isRequired,
tpaProviders: PropTypes.arrayOf(PropTypes.object),
beginNameChange: PropTypes.func.isRequired,
fetchNotificationPreferences: PropTypes.func.isRequired,
tpaProviders: PropTypes.arrayOf(PropTypes.shape({
connected: PropTypes.bool,
})),
nameChangeModal: PropTypes.oneOfType([
PropTypes.shape({
formId: PropTypes.string,
}),
PropTypes.bool,
]),
verifiedName: PropTypes.shape({
verified_name: PropTypes.string,
status: PropTypes.string,
proctored_exam_attempt_id: PropTypes.number,
}),
mostRecentVerifiedName: PropTypes.shape({
verified_name: PropTypes.string,
status: PropTypes.string,
proctored_exam_attempt_id: PropTypes.number,
}),
verifiedNameHistory: PropTypes.arrayOf(
PropTypes.shape({
verified_name: PropTypes.string,
status: PropTypes.string,
proctored_exam_attempt_id: PropTypes.number,
}),
),
navigate: PropTypes.func.isRequired,
location: PropTypes.string.isRequired,
countriesCodesList: PropTypes.arrayOf(
PropTypes.shape({
value: PropTypes.string.isRequired,
label: PropTypes.string.isRequired,
}),
),
};
AccountSettingsPage.defaultProps = {
loading: false,
loaded: false,
loadingError: null,
committedValues: {
useVerifiedNameForCerts: false,
verified_name: null,
country: '',
},
drafts: {},
formErrors: {},
siteLanguage: null,
siteLanguageOptions: [],
timeZoneOptions: [],
@@ -561,11 +1005,19 @@ AccountSettingsPage.defaultProps = {
tpaProviders: [],
isActive: true,
secondary_email_enabled: false,
nameChangeModal: {} || false,
verifiedName: null,
mostRecentVerifiedName: {},
verifiedNameHistory: [],
countriesCodesList: [],
};
export default connect(accountSettingsPageSelector, {
export default withLocation(withNavigate(connect(accountSettingsPageSelector, {
fetchNotificationPreferences,
fetchSettings,
saveSettings,
saveMultipleSettings,
updateDraft,
fetchSiteLanguages,
})(injectIntl(AccountSettingsPage));
beginNameChange,
})(injectIntl(AccountSettingsPage))));

View File

@@ -58,7 +58,7 @@ const messages = defineMessages({
},
'account.settings.section.linked.accounts.description': {
id: 'account.settings.section.linked.accounts.description',
defaultMessage: 'You can link your identity accounts to simplify signing in to edX.',
defaultMessage: 'You can link your identity accounts to simplify signing in to {siteName}.',
description: 'The linked accounts section heading description.',
},
'account.settings.field.username': {
@@ -68,7 +68,7 @@ const messages = defineMessages({
},
'account.settings.field.username.help.text': {
id: 'account.settings.field.username.help.text',
defaultMessage: 'The name that identifies you on edX. You cannot change your username.',
defaultMessage: 'The name that identifies you on {siteName}. You cannot change your username.',
description: 'Help text for the account settings username field.',
},
'account.settings.field.full.name': {
@@ -86,6 +86,121 @@ const messages = defineMessages({
defaultMessage: 'The name that is used for ID verification and that appears on your certificates.',
description: 'Help text for the account settings name field.',
},
'account.settings.field.full.name.help.text.default': {
id: 'account.settings.field.full.name.help.text.default',
defaultMessage: 'The name that appears on your public profile.',
description: 'Help text for the account settings name field.',
},
'account.settings.field.full.name.help.text.default.certificate': {
id: 'account.settings.field.full.name.help.text.default.certificate',
defaultMessage: 'This name is selected to appear on your certificates and public-facing records.',
description: 'Help text for the account settings name field.',
},
'account.settings.field.name.verified': {
id: 'account.settings.field.name.verified',
defaultMessage: 'Verified name',
description: 'Label for account settings verified name field.',
},
'account.settings.field.name.verified.help.text.verified': {
id: 'account.settings.field.name.verified.help.text.verified',
defaultMessage: 'This name has been verified by photo ID.',
description: 'Help text for the account settings verified name field when the name is verified.',
},
'account.settings.field.name.verified.help.text.verified.proctored': {
id: 'account.settings.field.name.verified.help.text.verified.proctored',
defaultMessage: 'This name has been verified by proctoring.',
description: 'Help text for the account settings verified name field when the name is verified through proctoring.',
},
'account.settings.field.name.verified.help.text.verified.certificate': {
id: 'account.settings.field.name.verified.help.text.verified.certificate',
defaultMessage: 'This name has been verified by photo ID, and is selected to appear on your certificates and public-facing records.',
description: 'Help text for the account settings verified name field when the name is selected for certificates.',
},
'account.settings.field.name.verified.help.text.verified.proctored.certificate': {
id: 'account.settings.field.name.verified.help.text.verified.proctored.certificate',
defaultMessage: 'This name has been verified by proctoring, and is selected to appear on your certificates and public-facing records.',
description: 'Help text for the account settings verified name field when the name is selected for certificates, and the name is verified through proctoring.',
},
'account.settings.field.name.verified.help.text.submitted': {
id: 'account.settings.field.name.verified.help.text.submitted',
defaultMessage: 'Verification has been submitted. This usually takes 48 hours or less. Verified name cannot be changed at this time.',
description: 'Help text for the account settings verified name field when a verified name has been submitted.',
},
'account.settings.field.name.verified.help.text.submitted.proctored': {
id: 'account.settings.field.name.verified.help.text.submitted.proctored',
defaultMessage: 'Your proctored exam has been submitted. Verified name cannot be changed at this time. Please check back in 2-5 days.',
description: 'Help text for the account settings verified name field when a verified name has been submitted through proctoring.',
},
'account.settings.field.name.verified.help.text.submitted.certificate': {
id: 'account.settings.field.name.verified.help.text.submitted.certificate',
defaultMessage: 'When identity verification is successful, this name will appear on your certificates and public-facing records. Verified name cannot be changed at this time.',
description: 'Help text for the account settings verified name field when a verified name has been submitted and will appear on certificates.',
},
'account.settings.field.name.verified.help.text.submitted.proctored.certificate': {
id: 'account.settings.field.name.verified.help.text.submitted.proctored.certificate',
defaultMessage: 'Once your proctored exam passes review, this name will appear on your certificate and public-facing records. Verified Name cannot be changed at this time.',
description: 'Help text for the account settings verified name field when a verified name has been submitted through proctoring and will appear on certificates.',
},
'account.settings.field.full.name.help.text.submitted': {
id: 'account.settings.field.full.name.help.text.submitted',
defaultMessage: 'Verification has been submitted. This usually takes 48 hours or less. Full name cannot be changed at this time.',
description: 'Help text for the account settings full name field when a verified name has been submitted.',
},
'account.settings.field.full.name.help.text.submitted.proctored': {
id: 'account.settings.field.full.name.help.text.submitted.proctored',
defaultMessage: 'Your proctored exam has been submitted. Full name cannot be changed at this time. Please check back in 2-5 days.',
description: 'Help text for the account settings full name field when a verified name has been submitted through proctoring.',
},
'account.settings.field.full.name.help.text.submitted.certificate': {
id: 'account.settings.field.full.name.help.text.submitted.certificate',
defaultMessage: 'When identity verification is successful, this name will appear on your certificates and public-facing records. Full name cannot be changed at this time.',
description: 'Help text for the account settings full name field when a full name has been submitted and will appear on certificates.',
},
'account.settings.field.full.name.help.text.submitted.proctored.certificate': {
id: 'account.settings.field.full.name.help.text.submitted.proctored.certificate',
defaultMessage: 'Once your proctored exam passes review, this name will appear on your certificates and public-facing records. Full name cannot be changed at this time.',
description: 'Help text for the account settings full name field when a full name has been submitted and will appear on certificates.',
},
'account.settings.field.name.verified.success.message': {
id: 'account.settings.field.name.verified.success.message',
defaultMessage: 'Your identity verification request has successfully completed. You now have the option of selecting which name you prefer to appear on your certificates and public-records.',
description: 'The body of the success alert indicating that a user\'s name has been verified',
},
'account.settings.field.name.verified.success.message.header': {
id: 'account.settings.field.name.verified.success.message.header',
defaultMessage: 'Your name change request is complete!',
description: 'The header of the success alert indicating that a user\'s name has been verified',
},
'account.settings.field.name.verified.failure.message': {
id: 'account.settings.field.name.verified.failure.message',
defaultMessage: 'Your most recent identity verification attempt did not pass. Related account settings have been restored.',
description: 'The body of the failure alert indicating that a user\'s name was not able to be verified',
},
'account.settings.field.name.verified.failure.message.header': {
id: 'account.settings.field.name.verified.failure.message.header',
defaultMessage: 'We were not able to verify your identity.',
description: 'The header of the failure alert indicating that a user\'s name was not able to be verified',
},
'account.settings.field.name.verified.failure.message.help.link': {
id: 'account.settings.field.name.verified.failure.message.help.link',
defaultMessage: 'Learn more about ID verification',
description: 'The text of the button displayed when a user\'s name was not able to be verified, intended to direct the user to a help article about ID verification.',
},
'account.settings.field.name.verified.submitted.message': {
id: 'account.settings.field.name.verified.submitted.message',
defaultMessage: 'Your identity verification request has been submitted and usually takes between 24 and 48 hours to complete.',
description: 'The body of the submitted alert indicating that a user\'s name has been submitted for verification',
},
'account.settings.field.name.verified.submitted.message.certificate': {
id: 'account.settings.field.name.verified.submitted.message.certificate',
defaultMessage: 'When your request is approved, your updated name will appear on all associated certificates and public-facing records.',
description: 'The body of the submitted alert indicating that a user\'s name will be updated on certificates.',
},
'account.settings.field.name.verified.submitted.message.header': {
id: 'account.settings.field.name.verified.submitted.message.header',
defaultMessage: 'Your name change request is almost complete!',
description: 'The header of the submitted alert indicating that a user\'s name has been submitted for verification',
},
'account.settings.field.email': {
id: 'account.settings.field.email',
defaultMessage: 'Email address (Sign in)',
@@ -103,7 +218,7 @@ const messages = defineMessages({
},
'account.settings.field.email.help.text': {
id: 'account.settings.field.email.help.text',
defaultMessage: 'You receive messages from edX and course teams at this address.',
defaultMessage: 'You receive messages from {siteName} and course teams at this address.',
description: 'Help text for the account settings email field.',
},
'account.settings.field.secondary.email': {
@@ -141,6 +256,56 @@ const messages = defineMessages({
defaultMessage: 'Select a year of birth',
description: 'Option for empty value on account settings year of birth field.',
},
'account.settings.field.dob.month': {
id: 'account.settings.field.dob.month',
defaultMessage: 'Month',
description: 'Label for account settings month of birth field.',
},
'account.settings.field.dob.year': {
id: 'account.settings.field.dob.year',
defaultMessage: 'Year',
description: 'Label for account settings year of birth field.',
},
'account.settings.field.dob.month.default': {
id: 'account.settings.field.month.year.default',
defaultMessage: 'Select month',
description: 'Default label for account settings month of birth field.',
},
'account.settings.field.dob.year.default': {
id: 'account.settings.field.dob.year.default',
defaultMessage: 'Select year',
description: 'Default label for account settings year of birth field.',
},
'account.settings.field.dob.form.button': {
id: 'account.settings.field.dob.form.button',
defaultMessage: 'Please confirm your date of birth',
description: 'Message to prompt user to enter dob',
},
'account.settings.field.dob.form.title': {
id: 'account.settings.field.dob.form.title',
defaultMessage: 'Enter your birth month and year',
description: 'Title of DOB form',
},
'account.settings.field.dob.form.help.text': {
id: 'account.settings.field.dob.form.help.text',
defaultMessage: 'We ask for birth month and year information to help us comply with our legal obligations.',
description: 'Help text for DOB form',
},
'account.settings.field.dob.form.success': {
id: 'account.settings.field.dob.form.success',
defaultMessage: 'Thank you for entering your information.',
description: 'Title of banner when date of birth is successfully entered',
},
'account.settings.field.month_of_birth.options.empty': {
id: 'account.settings.field.month_of_birth.options.empty',
defaultMessage: 'Select a month of birth',
description: 'Option for empty value on account settings month of birth field.',
},
'account.settingsfield.dob.error.general': {
id: 'account.settingsfield.dob.error.general',
defaultMessage: 'A technical error occurred. Please try again.',
description: 'Generic error message.',
},
'account.settings.field.country': {
id: 'account.settings.field.country',
defaultMessage: 'Country',
@@ -236,8 +401,8 @@ const messages = defineMessages({
defaultMessage: 'No formal education',
description: 'Selected by the user to describe their education.',
},
'account.settings.field.education.levels.o': {
id: 'account.settings.field.education.levels.o',
'account.settings.field.education.levels.other': {
id: 'account.settings.field.education.levels.other',
defaultMessage: 'Other education',
description: 'Selected by the user if they have a type of education not described by the other choices.',
},
@@ -274,19 +439,20 @@ const messages = defineMessages({
},
'account.settings.field.language.proficiencies': {
id: 'account.settings.field.language.proficiencies',
defaultMessage: 'Spoken languages',
description: 'Label for account settings spoken languages field.',
defaultMessage: 'Spoken language',
description: 'Label for account settings spoken language field.',
},
'account.settings.field.language.proficiencies.empty': {
id: 'account.settings.field.language.proficiencies.empty',
defaultMessage: 'Add a spoken language',
description: 'Placeholder for empty account settings spoken languages field.',
description: 'Placeholder for empty account settings spoken language field.',
},
'account.settings.field.language_proficiencies.options.empty': {
id: 'account.settings.field.language_proficiencies.options.empty',
defaultMessage: 'Select a Language',
description: 'Option for an empty value on account settings spoken languages field.',
description: 'Option for an empty value on account settings spoken language field.',
},
'account.settings.field.time.zone': {
id: 'account.settings.field.time.zone',
defaultMessage: 'Time zone',
@@ -325,7 +491,7 @@ const messages = defineMessages({
},
'account.settings.section.social.media.description': {
id: 'account.settings.section.social.media.description',
defaultMessage: 'Optionally, link your personal accounts to the social media icons on your edX profile.',
defaultMessage: 'Optionally, link your personal accounts to the social media icons on your {siteName} profile.',
description: 'Section subheader for social media links settings',
},
'account.settings.field.social.platform.name.linkedin': {
@@ -343,15 +509,15 @@ const messages = defineMessages({
defaultMessage: 'Delete My Account',
description: 'Header for the user account deletion area',
},
'account.settings.field.social.platform.name.twitter': {
id: 'account.settings.field.social.platform.name.twitter',
defaultMessage: 'Twitter',
description: 'Label for Twitter',
'account.settings.field.social.platform.name.xTwitter': {
id: 'account.settings.field.social.platform.name.xTwitter',
defaultMessage: 'X (Twitter)',
description: 'Label for X (Twitter)',
},
'account.settings.field.social.platform.name.twitter.empty': {
id: 'account.settings.field.social.platform.name.twitter.empty',
defaultMessage: 'Add Twitter profile',
description: 'Placeholder for an empty Twitter field',
'account.settings.field.social.platform.name.xTwitter.empty': {
id: 'account.settings.field.social.platform.name.xTwitter.empty',
defaultMessage: 'Add X profile',
description: 'Placeholder for an empty X field',
},
'account.settings.field.social.platform.name.facebook': {
@@ -389,6 +555,26 @@ const messages = defineMessages({
defaultMessage: 'No value set.',
description: 'The placeholder for an empty but uneditable field when there is no administrator',
},
'notification.preferences.notifications.label': {
id: 'notification.preferences.notifications.label',
defaultMessage: 'Notifications',
description: 'Label for Notifications',
},
'account.settings.field.work.experience': {
id: 'account.settings.work.experience',
defaultMessage: 'Work Experience',
description: 'Label for account settings Work experience field.',
},
'account.settings.field.work.experience.empty': {
id: 'account.settings.field.work.experience.empty',
defaultMessage: 'Add work experience',
description: 'Placeholder for empty account settings work experience field.',
},
'account.settings.field.work.experience.options.empty': {
id: 'account.settings.field.work.experience.options.empty',
defaultMessage: 'Select work experience',
description: 'Placeholder for the work experience levels dropdown.',
},
});
export default messages;

View File

@@ -2,20 +2,16 @@ import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
function Alert(props) {
return (
<div className={classNames('alert d-flex align-items-start', props.className)}>
<div>
{props.icon}
</div>
<div>
{props.children}
</div>
const Alert = (props) => (
<div className={classNames('alert d-flex align-items-start', props.className)}>
<div>
{props.icon}
</div>
);
}
<div>
{props.children}
</div>
</div>
);
Alert.propTypes = {
className: PropTypes.string,
@@ -29,5 +25,4 @@ Alert.defaultProps = {
children: undefined,
};
export default Alert;

View File

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { AppContext } from '@edx/frontend-platform/react';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { connect } from 'react-redux';
import { Button, Hyperlink } from '@edx/paragon';
import { Button, Hyperlink } from '@openedx/paragon';
import { betaLanguageBannerSelector } from './data/selectors';
import messages from './AccountSettingsPage.messages';
@@ -49,6 +49,9 @@ class BetaLanguageBanner extends React.Component {
render() {
const savedLanguage = this.getSiteLanguageEntry(this.context.locale);
if (!savedLanguage) {
return null;
}
const isSavedLanguageReleased = savedLanguage.released === true;
const noPreviousLanguageSet = this.props.siteLanguage.previousValue === null;
if (isSavedLanguageReleased || noPreviousLanguageSet) {
@@ -65,7 +68,7 @@ class BetaLanguageBanner extends React.Component {
})}
</p>
<div>
<Button onClick={this.handleRevertLanguage} className="btn btn-primary mr-2">
<Button onClick={this.handleRevertLanguage} className="mr-2">
{this.props.intl.formatMessage(
messages['account.settings.banner.beta.language.action.switch.back'],
{ previous_language: previousLanguage.name },

View File

@@ -0,0 +1,165 @@
import PropTypes from 'prop-types';
import { useIntl } from '@edx/frontend-platform/i18n';
import {
Form, StatefulButton, ModalDialog, ActionRow, useToggle, Button,
} from '@openedx/paragon';
import { useCallback, useEffect, useState } from 'react';
import { connect, useDispatch } from 'react-redux';
import messages from './AccountSettingsPage.messages';
import { YEAR_OF_BIRTH_OPTIONS } from './data/constants';
import { editableFieldSelector } from './data/selectors';
import { saveSettingsReset } from './data/actions';
const DOBModal = (props) => {
const intl = useIntl();
const {
saveState,
error,
onSubmit,
} = props;
const dispatch = useDispatch();
// eslint-disable-next-line no-unused-vars
const [isOpen, open, close, toggle] = useToggle(true, {});
const [monthValue, setMonthValue] = useState('');
const [yearValue, setYearValue] = useState('');
const handleChange = (e) => {
e.preventDefault();
if (e.target.name === 'month') {
setMonthValue(e.target.value);
} else if (e.target.name === 'year') {
setYearValue(e.target.value);
}
};
const handleSubmit = (e) => {
e.preventDefault();
const data = monthValue !== '' && yearValue !== '' ? [{ field_name: 'DOB', field_value: `${yearValue}-${monthValue}` }] : [];
onSubmit('extended_profile', data);
};
const handleComplete = useCallback(() => {
localStorage.setItem('submittedDOB', 'true');
close();
dispatch(saveSettingsReset());
}, [dispatch, close]);
const handleClose = useCallback(() => {
close();
dispatch(saveSettingsReset());
}, [dispatch, close]);
function renderErrors() {
if (saveState === 'error' || error) {
return (
<Form.Control.Feedback type="invalid" key="general-error" data-testid="error-message">
{intl.formatMessage(messages['account.settingsfield.dob.error.general'])}
</Form.Control.Feedback>
);
}
return null;
}
useEffect(() => {
if (saveState === 'complete' && isOpen) {
handleComplete();
}
}, [handleComplete, saveState, isOpen, monthValue, yearValue]);
return (
<>
<Button variant="primary" onClick={open} data-testid="open-modal-button">
{intl.formatMessage(messages['account.settings.field.dob.form.button'])}
</Button>
<ModalDialog
title={intl.formatMessage(messages['account.settings.field.dob.form.title'])}
isOpen={isOpen}
onClose={handleClose}
hasCloseButton={false}
variant="default"
data-testid="dob-modal"
>
<form onSubmit={handleSubmit} data-testid="dob-form">
<ModalDialog.Header>
<ModalDialog.Title data-testid="modal-title">
{intl.formatMessage(messages['account.settings.field.dob.form.title'])}
</ModalDialog.Title>
</ModalDialog.Header>
<ModalDialog.Body className="overflow-hidden" style={{ padding: '1.5rem' }}>
<p data-testid="help-text">{intl.formatMessage(messages['account.settings.field.dob.form.help.text'])}</p>
<Form.Group>
<Form.Label data-testid="month-label">
{intl.formatMessage(messages['account.settings.field.dob.month'])}
</Form.Label>
<Form.Control
as="select"
name="month"
onChange={handleChange}
data-testid="month-select"
>
<option value="">{intl.formatMessage(messages['account.settings.field.dob.month.default'])}</option>
{[...Array(12).keys()].map(month => (
<option key={month + 1} value={month + 1}>{month + 1}</option>
))}
</Form.Control>
</Form.Group>
<Form.Group>
<Form.Label data-testid="year-label">
{intl.formatMessage(messages['account.settings.field.dob.year'])}
</Form.Label>
<Form.Control
as="select"
name="year"
onChange={handleChange}
data-testid="year-select"
>
<option value="">{intl.formatMessage(messages['account.settings.field.dob.year.default'])}</option>
{YEAR_OF_BIRTH_OPTIONS.map(year => (
<option key={year.value} value={year.value}>{year.label}</option>
))}
</Form.Control>
</Form.Group>
{renderErrors()}
</ModalDialog.Body>
<ModalDialog.Footer>
<ActionRow>
<ModalDialog.CloseButton variant="tertiary" data-testid="cancel-button">
Cancel
</ModalDialog.CloseButton>
<StatefulButton
type="submit"
state={!(monthValue && yearValue) ? 'unedited' : saveState}
labels={{
default: intl.formatMessage(messages['account.settings.editable.field.action.save']),
}}
disabledStates={['unedited']}
data-testid="submit-button"
/>
</ActionRow>
</ModalDialog.Footer>
</form>
</ModalDialog>
</>
);
};
DOBModal.propTypes = {
saveState: PropTypes.oneOf(['default', 'pending', 'complete', 'error']),
error: PropTypes.string,
onSubmit: PropTypes.func.isRequired,
};
DOBModal.defaultProps = {
saveState: undefined,
error: undefined,
};
export default connect(editableFieldSelector)(DOBModal);

View File

@@ -1,8 +1,10 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Button, Input, StatefulButton, ValidationFormGroup } from '@edx/paragon';
import classNames from 'classnames';
import { useIntl } from '@edx/frontend-platform/i18n';
import {
Button, Form, StatefulButton,
} from '@openedx/paragon';
import { faPencilAlt } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
@@ -14,16 +16,16 @@ import {
closeForm,
} from './data/actions';
import { editableFieldSelector } from './data/selectors';
import CertificatePreference from './certificate-preference/CertificatePreference';
function EditableField(props) {
const EditableField = (props) => {
const {
name,
label,
emptyLabel,
type,
value,
options,
userSuppliedValue,
saveState,
error,
confirmationMessageDefinition,
@@ -35,10 +37,11 @@ function EditableField(props) {
onChange,
isEditing,
isEditable,
intl,
isGrayedOut,
...others
} = props;
const id = `field-${name}`;
const intl = useIntl();
const handleSubmit = (e) => {
e.preventDefault();
@@ -59,29 +62,35 @@ function EditableField(props) {
const renderEmptyLabel = () => {
if (isEditable) {
return <Button onClick={handleEdit} className="btn-link p-0">{emptyLabel}</Button>;
return <Button variant="link" onClick={handleEdit} className="p-0">{emptyLabel}</Button>;
}
return <span className="text-muted">{emptyLabel}</span>;
};
const renderValue = (rawValue) => {
if (!rawValue) return renderEmptyLabel();
if (!rawValue) {
return renderEmptyLabel();
}
let finalValue = rawValue;
if (options) {
// Use == instead of === to prevent issues when HTML casts numbers as strings
// eslint-disable-next-line eqeqeq
const selectedOption = options.find(option => option.value == rawValue);
if (selectedOption) return selectedOption.label;
if (userSuppliedValue) {
finalValue += `: ${userSuppliedValue}`;
}
return rawValue;
return finalValue;
};
const renderConfirmationMessage = () => {
if (!confirmationMessageDefinition || !confirmationValue) return null;
return intl.formatMessage(confirmationMessageDefinition, {
value: confirmationValue,
});
if (!confirmationMessageDefinition || !confirmationValue) {
return null;
}
return (
<span data-testid="editable-field-confirmation">
{intl.formatMessage(confirmationMessageDefinition, {
value: confirmationValue,
})}
</span>
);
};
return (
@@ -89,83 +98,89 @@ function EditableField(props) {
expression={isEditing ? 'editing' : 'default'}
cases={{
editing: (
<form onSubmit={handleSubmit}>
<ValidationFormGroup
for={id}
invalid={error != null}
invalidMessage={error}
helpText={helpText}
>
<label className="h6 d-block" htmlFor={id}>{label}</label>
<Input
name={name}
id={id}
type={type}
value={value}
onChange={handleChange}
options={options}
{...others}
/>
</ValidationFormGroup>
<p>
<StatefulButton
type="submit"
className="btn-primary mr-2"
state={saveState}
labels={{
default: intl.formatMessage(messages['account.settings.editable.field.action.save']),
}}
onClick={(e) => {
// Swallow clicks if the state is pending.
// We do this instead of disabling the button to prevent
// it from losing focus (disabled elements cannot have focus).
// Disabling it would causes upstream issues in focus management.
// Swallowing the onSubmit event on the form would be better, but
// we would have to add that logic for every field given our
// current structure of the application.
if (saveState === 'pending') e.preventDefault();
}}
disabledStates={[]}
/>
<Button
onClick={handleCancel}
className="btn-outline-primary"
<>
<form onSubmit={handleSubmit} data-testid="editable-field-form">
<Form.Group
controlId={id}
isInvalid={error != null}
>
{intl.formatMessage(messages['account.settings.editable.field.action.cancel'])}
</Button>
</p>
</form>
<Form.Label size="sm" className="h6 d-block" htmlFor={id}>{label}</Form.Label>
<Form.Control
data-hj-suppress
name={name}
id={id}
type={type}
value={value}
onChange={handleChange}
data-testid="editable-field-textbox"
{...others}
/>
{!!helpText && <Form.Text>{helpText}</Form.Text>}
{error != null && <Form.Control.Feedback hasIcon={false} data-testid="editable-field-error">{error}</Form.Control.Feedback>}
{others.children}
</Form.Group>
<p>
<StatefulButton
type="submit"
className="mr-2"
state={saveState}
labels={{
default: intl.formatMessage(messages['account.settings.editable.field.action.save']),
}}
onClick={(e) => {
// Swallow clicks if the state is pending.
// We do this instead of disabling the button to prevent
// it from losing focus (disabled elements cannot have focus).
// Disabling it would causes upstream issues in focus management.
// Swallowing the onSubmit event on the form would be better, but
// we would have to add that logic for every field given our
// current structure of the application.
if (saveState === 'pending') { e.preventDefault(); }
}}
disabledStates={[]}
data-testid="editable-field-save"
/>
<Button
variant="outline-primary"
onClick={handleCancel}
data-testid="editable-field-cancel"
data-clicked="cancel"
>
{intl.formatMessage(messages['account.settings.editable.field.action.cancel'])}
</Button>
</p>
</form>
{['name', 'verified_name'].includes(name) && (
<CertificatePreference fieldName={name} data-testid="editable-field-certificate-preference" />
)}
</>
),
default: (
<div className="form-group">
<div className="d-flex align-items-start">
<h6 aria-level="3">{label}</h6>
{isEditable ? (
<Button onClick={handleEdit} className="ml-3 btn-link">
<Button variant="link" onClick={handleEdit} className="ml-3" data-testid="editable-field-edit" data-clicked="edit">
<FontAwesomeIcon className="mr-1" icon={faPencilAlt} />{intl.formatMessage(messages['account.settings.editable.field.action.edit'])}
</Button>
) : null}
</div>
<p>{renderValue(value)}</p>
<p data-hj-suppress className={classNames('text-truncate', { 'grayed-out': isGrayedOut })}>{renderValue(value)}</p>
<p className="small text-muted mt-n2">{renderConfirmationMessage() || helpText}</p>
</div>
),
}}
/>
);
}
};
EditableField.propTypes = {
name: PropTypes.string.isRequired,
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.node]),
emptyLabel: PropTypes.node,
type: PropTypes.string.isRequired,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
options: PropTypes.arrayOf(PropTypes.shape({
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
})),
userSuppliedValue: PropTypes.string,
saveState: PropTypes.oneOf(['default', 'pending', 'complete', 'error']),
error: PropTypes.string,
confirmationMessageDefinition: PropTypes.shape({
@@ -181,12 +196,11 @@ EditableField.propTypes = {
onChange: PropTypes.func.isRequired,
isEditing: PropTypes.bool,
isEditable: PropTypes.bool,
intl: intlShape.isRequired,
isGrayedOut: PropTypes.bool,
};
EditableField.defaultProps = {
value: undefined,
options: undefined,
saveState: undefined,
label: undefined,
emptyLabel: undefined,
@@ -196,10 +210,11 @@ EditableField.defaultProps = {
helpText: undefined,
isEditing: false,
isEditable: true,
isGrayedOut: false,
userSuppliedValue: undefined,
};
export default connect(editableFieldSelector, {
onEdit: openForm,
onCancel: closeForm,
})(injectIntl(EditableField));
})(EditableField);

View File

@@ -0,0 +1,250 @@
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { useIntl } from '@edx/frontend-platform/i18n';
import {
Button, Form, StatefulButton,
} from '@openedx/paragon';
import { faPencilAlt } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import SwitchContent from './SwitchContent';
import messages from './AccountSettingsPage.messages';
import {
openForm,
closeForm,
} from './data/actions';
import { editableFieldSelector } from './data/selectors';
import CertificatePreference from './certificate-preference/CertificatePreference';
const EditableSelectField = (props) => {
const intl = useIntl();
const {
name,
label,
emptyLabel,
type,
value,
userSuppliedValue,
options,
saveState,
error,
confirmationMessageDefinition,
confirmationValue,
helpText,
onEdit,
onCancel,
onSubmit,
onChange,
isEditing,
isEditable,
isGrayedOut,
...others
} = props;
const id = `field-${name}`;
const handleSubmit = (e) => {
e.preventDefault();
onSubmit(name, new FormData(e.target).get(name));
};
const handleChange = (e) => {
onChange(name, e.target.value);
};
const handleEdit = () => {
onEdit(name);
};
const handleCancel = () => {
onCancel(name);
};
const renderEmptyLabel = () => {
if (isEditable) {
return <Button variant="link" onClick={handleEdit} className="p-0">{emptyLabel}</Button>;
}
return <span className="text-muted">{emptyLabel}</span>;
};
const renderValue = (rawValue) => {
if (!rawValue) {
return renderEmptyLabel();
}
let finalValue = rawValue;
if (options) {
// Use == instead of === to prevent issues when HTML casts numbers as strings
// eslint-disable-next-line eqeqeq
const selectedOption = options.find(option => option.value == rawValue);
if (selectedOption) {
finalValue = selectedOption.label;
}
}
if (userSuppliedValue) {
finalValue += `: ${userSuppliedValue}`;
}
return finalValue;
};
const renderConfirmationMessage = () => {
if (!confirmationMessageDefinition || !confirmationValue) {
return null;
}
return intl.formatMessage(confirmationMessageDefinition, {
value: confirmationValue,
});
};
const selectOptions = options.map((option) => {
if (option.group) {
// If the option has a 'group' property, it represents an element with sub-options.
return (
<optgroup label={option.label} key={option.label}>
{option.group.map((subOption) => (
<option
value={subOption.value}
key={`${subOption.value}-${subOption.label}`}
disabled={subOption?.disabled}
>
{subOption.label}
</option>
))}
</optgroup>
);
}
return (
<option value={option.value} key={`${option.value}-${option.label}`} disabled={option?.disabled}>
{option.label}
</option>
);
});
return (
<SwitchContent
expression={isEditing ? 'editing' : 'default'}
cases={{
editing: (
<>
<form onSubmit={handleSubmit}>
<Form.Group
controlId={id}
isInvalid={error != null}
>
<Form.Label size="sm" className="h6 d-block" htmlFor={id}>{label}</Form.Label>
<Form.Control
data-hj-suppress
name={name}
id={id}
type={type}
as={type}
value={value}
onChange={handleChange}
{...others}
>
{options.length > 0 && selectOptions}
</Form.Control>
{!!helpText && <Form.Text>{helpText}</Form.Text>}
{error != null && <Form.Control.Feedback>{error}</Form.Control.Feedback>}
{others.children}
</Form.Group>
<p>
<StatefulButton
type="submit"
className="mr-2"
state={saveState}
labels={{
default: intl.formatMessage(messages['account.settings.editable.field.action.save']),
}}
onClick={(e) => {
// Swallow clicks if the state is pending.
// We do this instead of disabling the button to prevent
// it from losing focus (disabled elements cannot have focus).
// Disabling it would causes upstream issues in focus management.
// Swallowing the onSubmit event on the form would be better, but
// we would have to add that logic for every field given our
// current structure of the application.
if (saveState === 'pending') { e.preventDefault(); }
}}
disabledStates={[]}
/>
<Button
variant="outline-primary"
onClick={handleCancel}
>
{intl.formatMessage(messages['account.settings.editable.field.action.cancel'])}
</Button>
</p>
</form>
{['name', 'verified_name'].includes(name) && <CertificatePreference fieldName={name} />}
</>
),
default: (
<div className="form-group">
<div className="d-flex align-items-start">
<h6 aria-level="3">{label}</h6>
{isEditable ? (
<Button variant="link" onClick={handleEdit} className="ml-3">
<FontAwesomeIcon className="mr-1" icon={faPencilAlt} />{intl.formatMessage(messages['account.settings.editable.field.action.edit'])}
</Button>
) : null}
</div>
<p data-hj-suppress className={isGrayedOut ? 'grayed-out' : null}>{renderValue(value)}</p>
<p className="small text-muted mt-n2">{renderConfirmationMessage() || helpText}</p>
</div>
),
}}
/>
);
};
EditableSelectField.propTypes = {
name: PropTypes.string.isRequired,
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.node]),
emptyLabel: PropTypes.node,
type: PropTypes.string.isRequired,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
userSuppliedValue: PropTypes.string,
options: PropTypes.arrayOf(PropTypes.shape({
label: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
})),
saveState: PropTypes.oneOf(['default', 'pending', 'complete', 'error']),
error: PropTypes.string,
confirmationMessageDefinition: PropTypes.shape({
id: PropTypes.string.isRequired,
defaultMessage: PropTypes.string.isRequired,
description: PropTypes.string,
}),
confirmationValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
helpText: PropTypes.node,
onEdit: PropTypes.func.isRequired,
onCancel: PropTypes.func.isRequired,
onSubmit: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
isEditing: PropTypes.bool,
isEditable: PropTypes.bool,
isGrayedOut: PropTypes.bool,
};
EditableSelectField.defaultProps = {
value: undefined,
options: [],
saveState: undefined,
label: undefined,
emptyLabel: undefined,
error: undefined,
confirmationMessageDefinition: undefined,
confirmationValue: undefined,
helpText: undefined,
isEditing: false,
isEditable: true,
isGrayedOut: false,
userSuppliedValue: undefined,
};
export default connect(editableFieldSelector, {
onEdit: openForm,
onCancel: closeForm,
})(EditableSelectField);

View File

@@ -1,8 +1,9 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';
import { Button, StatefulButton, Input, ValidationFormGroup } from '@edx/paragon';
import { useIntl, FormattedMessage } from '@edx/frontend-platform/i18n';
import {
Button, StatefulButton, Form, Tooltip, OverlayTrigger,
} from '@openedx/paragon';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faExclamationTriangle, faPencilAlt } from '@fortawesome/free-solid-svg-icons';
@@ -16,8 +17,7 @@ import {
} from './data/actions';
import { editableFieldSelector } from './data/selectors';
function EmailField(props) {
const EmailField = (props) => {
const {
name,
label,
@@ -34,9 +34,9 @@ function EmailField(props) {
onChange,
isEditing,
isEditable,
intl,
} = props;
const id = `field-${name}`;
const intl = useIntl();
const handleSubmit = (e) => {
e.preventDefault();
@@ -56,7 +56,9 @@ function EmailField(props) {
};
const renderConfirmationMessage = () => {
if (!confirmationMessageDefinition || !confirmationValue) return null;
if (!confirmationMessageDefinition || !confirmationValue) {
return null;
}
return (
<Alert
className="alert-warning mt-n2"
@@ -85,13 +87,15 @@ function EmailField(props) {
const renderEmptyLabel = () => {
if (isEditable) {
return <Button onClick={handleEdit} className="btn-link p-0">{emptyLabel}</Button>;
return <Button variant="link" onClick={handleEdit} className="p-0">{emptyLabel}</Button>;
}
return <span className="text-muted">{emptyLabel}</span>;
};
const renderValue = () => {
if (confirmationValue) return renderConfirmationValue();
if (confirmationValue) {
return renderConfirmationValue();
}
return value || renderEmptyLabel();
};
@@ -101,25 +105,26 @@ function EmailField(props) {
cases={{
editing: (
<form onSubmit={handleSubmit}>
<ValidationFormGroup
for={id}
invalid={error != null}
invalidMessage={error}
helpText={helpText}
<Form.Group
controlId={id}
isInvalid={error != null}
>
<label className="h6 d-block" htmlFor={id}>{label}</label>
<Input
<Form.Label className="h6 d-block" htmlFor={id}>{label}</Form.Label>
<Form.Control
data-hj-suppress
name={name}
id={id}
type="email"
value={value}
onChange={handleChange}
/>
</ValidationFormGroup>
{!!helpText && <Form.Text>{helpText}</Form.Text>}
{error != null && <Form.Control.Feedback hasIcon={false}>{error}</Form.Control.Feedback>}
</Form.Group>
<p>
<StatefulButton
type="submit"
className="btn-primary mr-2"
className="mr-2"
state={saveState}
labels={{
default: intl.formatMessage(messages['account.settings.editable.field.action.save']),
@@ -132,13 +137,13 @@ function EmailField(props) {
// Swallowing the onSubmit event on the form would be better, but
// we would have to add that logic for every field given our
// current structure of the application.
if (saveState === 'pending') e.preventDefault();
if (saveState === 'pending') { e.preventDefault(); }
}}
disabledStates={[]}
/>
<Button
variant="outline-primary"
onClick={handleCancel}
className="btn-outline-primary"
>
{intl.formatMessage(messages['account.settings.editable.field.action.cancel'])}
</Button>
@@ -150,21 +155,29 @@ function EmailField(props) {
<div className="d-flex align-items-start">
<h6 aria-level="3">{label}</h6>
{isEditable ? (
<Button onClick={handleEdit} className="ml-3 btn-link">
<Button variant="link" onClick={handleEdit} className="ml-3">
<FontAwesomeIcon className="mr-1" icon={faPencilAlt} />
{intl.formatMessage(messages['account.settings.editable.field.action.edit'])}
</Button>
) : null}
</div>
<p>{renderValue()}</p>
<OverlayTrigger
placement="top"
overlay={(
<Tooltip id={`tooltip-${name}`} variant="light" className="d-sm-none">
{renderValue()}
</Tooltip>
)}
>
<p data-hj-suppress className="text-truncate">{renderValue()}</p>
</OverlayTrigger>
{renderConfirmationMessage() || <p className="small text-muted mt-n2">{helpText}</p>}
</div>
),
}}
/>
);
}
};
EmailField.propTypes = {
name: PropTypes.string.isRequired,
@@ -186,7 +199,6 @@ EmailField.propTypes = {
onChange: PropTypes.func.isRequired,
isEditing: PropTypes.bool,
isEditable: PropTypes.bool,
intl: intlShape.isRequired,
};
EmailField.defaultProps = {
@@ -202,8 +214,7 @@ EmailField.defaultProps = {
isEditable: true,
};
export default connect(editableFieldSelector, {
onEdit: openForm,
onCancel: closeForm,
})(injectIntl(EmailField));
})(EmailField);

View File

@@ -1,25 +1,30 @@
import React from 'react';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { getConfig } from '@edx/frontend-platform';
import { useIntl } from '@edx/frontend-platform/i18n';
import { breakpoints, useWindowSize } from '@openedx/paragon';
import classNames from 'classnames';
import { NavHashLink } from 'react-router-hash-link';
import Scrollspy from 'react-scrollspy';
import messages from './AccountSettingsPage.messages';
const JumpNav = () => {
const intl = useIntl();
const stickToTop = useWindowSize().width > breakpoints.small.minWidth;
function JumpNav({ intl }) {
return (
<div className="jump-nav">
<div className={classNames('jump-nav', { 'jump-nav-sm position-sticky pt-3': stickToTop })}>
<Scrollspy
items={[
'basic-information',
'profile-information',
'social-media',
'notifications',
'site-preferences',
'linked-accounts',
'delete-account',
]}
className="list-unstyled"
currentClassName="font-weight-bold"
offset={-64}
>
<li>
<NavHashLink to="#basic-information">
@@ -36,6 +41,11 @@ function JumpNav({ intl }) {
{intl.formatMessage(messages['account.settings.section.social.media'])}
</NavHashLink>
</li>
<li>
<NavHashLink to="#notifications">
{intl.formatMessage(messages['notification.preferences.notifications.label'])}
</NavHashLink>
</li>
<li>
<NavHashLink to="#site-preferences">
{intl.formatMessage(messages['account.settings.section.site.preferences'])}
@@ -46,20 +56,17 @@ function JumpNav({ intl }) {
{intl.formatMessage(messages['account.settings.section.linked.accounts'])}
</NavHashLink>
</li>
<li>
<NavHashLink to="#delete-account">
{intl.formatMessage(messages['account.settings.jump.nav.delete.account'])}
</NavHashLink>
</li>
{getConfig().ENABLE_ACCOUNT_DELETION
&& (
<li>
<NavHashLink to="#delete-account">
{intl.formatMessage(messages['account.settings.jump.nav.delete.account'])}
</NavHashLink>
</li>
)}
</Scrollspy>
</div>
);
}
JumpNav.propTypes = {
intl: intlShape.isRequired,
};
export default injectIntl(JumpNav);
export default JumpNav;

View File

@@ -1,16 +1,19 @@
import React from 'react';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
export default function NotFoundPage() {
return (
<div className="container-fluid d-flex py-5 justify-content-center align-items-start text-center">
<p className="my-0 py-5 text-muted" style={{ maxWidth: '32em' }}>
<FormattedMessage
id="error.notfound.message"
defaultMessage="The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again."
description="error message when a page does not exist"
/>
</p>
</div>
);
}
const NotFoundPage = () => (
<div
className="container-fluid d-flex py-5 justify-content-center align-items-start text-center"
data-testid="not-found-page"
>
<p className="my-0 py-5 text-muted" style={{ maxWidth: '32em' }}>
<FormattedMessage
id="error.notfound.message"
defaultMessage="The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again."
description="Error message when a page does not exist"
/>
</p>
</div>
);
export default NotFoundPage;

View File

@@ -0,0 +1,45 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { Alert } from '@openedx/paragon';
const OneTimeDismissibleAlert = (props) => {
const [dismissed, setDismissed] = useState(localStorage.getItem(props.id) !== 'true');
const onClose = () => {
localStorage.setItem(props.id, 'true');
setDismissed(false);
};
return (
<Alert
variant={props.variant}
dismissible
icon={props.icon}
onClose={onClose}
show={dismissed}
>
<Alert.Heading>{props.header}</Alert.Heading>
<p>
{props.body}
</p>
</Alert>
);
};
OneTimeDismissibleAlert.propTypes = {
id: PropTypes.string.isRequired,
variant: PropTypes.string,
icon: PropTypes.func,
header: PropTypes.string,
body: PropTypes.string,
};
OneTimeDismissibleAlert.defaultProps = {
variant: 'success',
icon: undefined,
header: undefined,
body: undefined,
};
export default OneTimeDismissibleAlert;

View File

@@ -1,7 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { TransitionReplace } from '@edx/paragon';
import { TransitionReplace } from '@openedx/paragon';
const onChildExit = (htmlNode) => {
// If the leaving child has focus, take control and redirect it
@@ -11,7 +10,9 @@ const onChildExit = (htmlNode) => {
const enteringChild = htmlNode.previousSibling || htmlNode.nextSibling;
// There's no replacement, do nothing.
if (!enteringChild) return;
if (!enteringChild) {
return;
}
// Get all the focusable elements in the entering child and focus the first one
const focusableElements = enteringChild.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
@@ -21,15 +22,15 @@ const onChildExit = (htmlNode) => {
}
};
function SwitchContent({ expression, cases, className }) {
const SwitchContent = ({ expression, cases, className }) => {
const getContent = (caseKey) => {
if (cases[caseKey]) {
if (typeof cases[caseKey] === 'string') {
return getContent(cases[caseKey]);
}
return React.cloneElement(cases[caseKey], { key: caseKey });
} else if (cases.default) {
}
if (cases.default) {
if (typeof cases.default === 'string') {
return getContent(cases.default);
}
@@ -47,8 +48,7 @@ function SwitchContent({ expression, cases, className }) {
{getContent(expression)}
</TransitionReplace>
);
}
};
SwitchContent.propTypes = {
expression: PropTypes.string,
@@ -61,5 +61,4 @@ SwitchContent.defaultProps = {
className: null,
};
export default SwitchContent;

View File

@@ -2,9 +2,11 @@
.form-group {
margin-bottom: 1.5rem;
}
h6, .h6 {
margin-bottom: .25rem;
}
.btn-link {
line-height: 1.2;
border: none;
@@ -12,36 +14,37 @@
display: inline-block;
}
.jump-nav-sm {
top: 1rem;
}
.jump-nav {
@media (min-width: map-get($grid-breakpoints, "sm")) {
padding-top: 1rem;
position: sticky;
top: 1rem;
}
li {
margin-bottom: .5rem;
a {
text-decoration: underline;
}
}
}
.section-heading {
@extend .h4;
margin-bottom: map-get($spacers, 3);
}
.account-section {
// These properties together will shift the hashlink position
margin-bottom: map-get($spacers, 5);
padding-top: 1rem;
}
.custom-switch {
padding: 0;
max-width: 500px;
.custom-control-label {
left: 2.25rem;
line-height: 1.6rem;
}
}
.grayed-out{
opacity: 0.6; /* Real browsers */
filter: alpha(opacity = 60); /* MSIE */
}
}
#tooltip-email .small {
display: block;
margin: 0 !important;
}

View File

@@ -0,0 +1,172 @@
import { useState, useEffect } from 'react';
import { connect, useDispatch } from 'react-redux';
import PropTypes from 'prop-types';
import {
ActionRow,
Form,
ModalDialog,
StatefulButton,
} from '@openedx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';
import {
closeForm,
resetDrafts,
saveSettings,
updateDraft,
} from '../data/actions';
import { certPreferenceSelector } from '../data/selectors';
import commonMessages from '../AccountSettingsPage.messages';
import messages from './messages';
const CertificatePreference = ({
fieldName,
originalFullName,
originalVerifiedName,
saveState,
useVerifiedNameForCerts,
}) => {
const dispatch = useDispatch();
const [checked, setChecked] = useState(false);
const [modalIsOpen, setModalIsOpen] = useState(false);
const formId = 'useVerifiedNameForCerts';
const intl = useIntl();
const handleCheckboxChange = () => {
if (!checked) {
if (fieldName === 'verified_name') {
dispatch(updateDraft(formId, true));
} else {
dispatch(updateDraft(formId, false));
}
} else {
setModalIsOpen(true);
}
};
const handleCancel = () => {
setModalIsOpen(false);
dispatch(resetDrafts());
};
const handleModalChange = (e) => {
if (e.target.value === 'fullName') {
dispatch(updateDraft(formId, false));
} else {
dispatch(updateDraft(formId, true));
}
};
const handleSubmit = (e) => {
e.preventDefault();
if (saveState === 'pending') {
return;
}
dispatch(saveSettings(formId, useVerifiedNameForCerts));
};
useEffect(() => {
if (originalVerifiedName) {
if (fieldName === 'verified_name') {
setChecked(useVerifiedNameForCerts);
} else {
setChecked(!useVerifiedNameForCerts);
}
}
}, [originalVerifiedName, fieldName, useVerifiedNameForCerts]);
useEffect(() => {
if (originalVerifiedName) {
if (modalIsOpen && saveState === 'complete') {
setModalIsOpen(false);
dispatch(closeForm(fieldName));
}
}
}, [dispatch, originalVerifiedName, fieldName, modalIsOpen, saveState]);
// If the user doesn't have an approved verified name, do not display this component
return originalVerifiedName ? (
<>
<Form.Checkbox className="mt-1 mb-4" checked={checked} onChange={handleCheckboxChange}>
{intl.formatMessage(messages['account.settings.field.name.checkbox.certificate.select'])}
</Form.Checkbox>
<ModalDialog
title={intl.formatMessage(messages['account.settings.field.name.modal.certificate.title'])}
isOpen={modalIsOpen}
onClose={handleCancel}
size="lg"
hasCloseButton
isFullscreenOnMobile
>
<Form onSubmit={handleSubmit}>
<ModalDialog.Header>
<ModalDialog.Title>
{intl.formatMessage(messages['account.settings.field.name.modal.certificate.title'])}
</ModalDialog.Title>
</ModalDialog.Header>
<ModalDialog.Body className="overflow-hidden">
<Form.Group className="mb-4">
<Form.Label>
{intl.formatMessage(messages['account.settings.field.name.modal.certificate.select'])}
</Form.Label>
<Form.RadioSet
name={formId}
value={useVerifiedNameForCerts ? 'verifiedName' : 'fullName'}
onChange={handleModalChange}
>
<Form.Radio value="fullName">
{originalFullName}{' '}
({intl.formatMessage(messages['account.settings.field.name.modal.certificate.option.full'])})
</Form.Radio>
<Form.Radio value="verifiedName">
{originalVerifiedName}{' '}
({intl.formatMessage(messages['account.settings.field.name.modal.certificate.option.verified'])})
</Form.Radio>
</Form.RadioSet>
</Form.Group>
</ModalDialog.Body>
<ModalDialog.Footer>
<ActionRow>
<ModalDialog.CloseButton variant="outline-primary" disabled={saveState === 'pending'}>
{intl.formatMessage(commonMessages['account.settings.editable.field.action.cancel'])}
</ModalDialog.CloseButton>
<StatefulButton
type="submit"
state={saveState}
labels={{
default: intl.formatMessage(messages['account.settings.field.name.modal.certificate.button.choose']),
}}
disabledStates={[]}
/>
</ActionRow>
</ModalDialog.Footer>
</Form>
</ModalDialog>
</>
) : null;
};
CertificatePreference.propTypes = {
fieldName: PropTypes.string.isRequired,
originalFullName: PropTypes.string,
originalVerifiedName: PropTypes.string,
saveState: PropTypes.string,
useVerifiedNameForCerts: PropTypes.bool,
};
CertificatePreference.defaultProps = {
originalFullName: '',
originalVerifiedName: '',
saveState: null,
useVerifiedNameForCerts: false,
};
export default connect(certPreferenceSelector)(CertificatePreference);

View File

@@ -0,0 +1,22 @@
import { getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { handleRequestError } from '../../data/utils';
// eslint-disable-next-line import/prefer-default-export
export async function postVerifiedNameConfig(username, commitValues) {
const requestConfig = { headers: { Accept: 'application/json' } };
const requestUrl = `${getConfig().LMS_BASE_URL}/api/edx_name_affirmation/v1/verified_name/config`;
const { useVerifiedNameForCerts } = commitValues;
const postValues = {
username,
use_verified_name_for_certs: useVerifiedNameForCerts,
};
const { data } = await getAuthenticatedHttpClient()
.post(requestUrl, postValues, requestConfig)
.catch(error => handleRequestError(error));
return data;
}

View File

@@ -0,0 +1,76 @@
import { getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { postVerifiedNameConfig } from './service';
import { handleRequestError } from '../../data/utils';
jest.mock('@edx/frontend-platform');
jest.mock('@edx/frontend-platform/auth');
jest.mock('../../data/utils');
describe('postVerifiedNameConfig', () => {
const mockPost = jest.fn();
beforeEach(() => {
jest.resetAllMocks();
getConfig.mockReturnValue({
LMS_BASE_URL: 'http://testserver',
});
getAuthenticatedHttpClient.mockReturnValue({
post: mockPost,
});
});
it('posts verified name config with useVerifiedNameForCerts = true', async () => {
const mockResponse = { data: { success: true } };
mockPost.mockResolvedValueOnce(mockResponse);
const result = await postVerifiedNameConfig('testuser', { useVerifiedNameForCerts: true });
expect(getConfig).toHaveBeenCalled();
expect(getAuthenticatedHttpClient).toHaveBeenCalled();
expect(mockPost).toHaveBeenCalledWith(
'http://testserver/api/edx_name_affirmation/v1/verified_name/config',
{
username: 'testuser',
use_verified_name_for_certs: true,
},
{ headers: { Accept: 'application/json' } },
);
expect(result).toEqual(mockResponse.data);
});
it('posts verified name config with useVerifiedNameForCerts = false', async () => {
const mockResponse = { data: { success: false } };
mockPost.mockResolvedValueOnce(mockResponse);
const result = await postVerifiedNameConfig('anotheruser', { useVerifiedNameForCerts: false });
expect(mockPost).toHaveBeenCalledWith(
'http://testserver/api/edx_name_affirmation/v1/verified_name/config',
{
username: 'anotheruser',
use_verified_name_for_certs: false,
},
{ headers: { Accept: 'application/json' } },
);
expect(result).toEqual(mockResponse.data);
});
it('calls handleRequestError and throws when request fails', async () => {
const mockError = new Error('Request failed');
mockPost.mockRejectedValueOnce(mockError);
handleRequestError.mockImplementation(() => {
throw mockError;
});
await expect(
postVerifiedNameConfig('erroruser', { useVerifiedNameForCerts: true }),
).rejects.toThrow('Request failed');
expect(handleRequestError).toHaveBeenCalledWith(mockError);
});
});

View File

@@ -0,0 +1,36 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
'account.settings.field.name.checkbox.certificate.select': {
id: 'account.settings.field.name.certificate.select',
defaultMessage: 'If checked, this name will appear on your certificates and public-facing records.',
description: 'Label for checkbox describing that the selected name will appear on the users certificates.',
},
'account.settings.field.name.modal.certificate.title': {
id: 'account.settings.field.name.modal.certificate.title',
defaultMessage: 'Choose a preferred name for certificates and public-facing records',
description: 'Title instructing the user to choose a preferred name.',
},
'account.settings.field.name.modal.certificate.select': {
id: 'account.settings.field.name.modal.certificate.select',
defaultMessage: 'Select a name',
description: 'Label instructing the user to select a name.',
},
'account.settings.field.name.modal.certificate.option.full': {
id: 'account.settings.field.name.modal.certificate.option.full',
defaultMessage: 'Full Name',
description: 'Option representing the users full name.',
},
'account.settings.field.name.modal.certificate.option.verified': {
id: 'account.settings.field.name.modal.certificate.option.verified',
defaultMessage: 'Verified Name',
description: 'Option representing the users verified name.',
},
'account.settings.field.name.modal.certificate.button.choose': {
id: 'account.settings.field.name.modal.certificate.button.choose',
defaultMessage: 'Choose name',
description: 'Button to confirm the users name choice.',
},
});
export default messages;

View File

@@ -0,0 +1,171 @@
/* eslint-disable no-import-assign */
import React from 'react';
import { Provider } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom';
import configureStore from 'redux-mock-store';
import {
fireEvent,
render,
screen,
} from '@testing-library/react';
import * as auth from '@edx/frontend-platform/auth';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import messages from '../messages';
// Modal creates a portal. Overriding createPortal allows portals to be tested in jest.
jest.mock('react-dom', () => ({
...jest.requireActual('react-dom'),
createPortal: jest.fn(node => node), // Mock portal behavior
}));
import CertificatePreference from '../CertificatePreference'; // eslint-disable-line import/first
const mockDispatch = jest.fn();
jest.mock('react-redux', () => ({
...jest.requireActual('react-redux'),
useDispatch: () => mockDispatch,
}));
jest.mock('@edx/frontend-platform/auth');
jest.mock('../../data/selectors', () => jest.fn().mockImplementation(() => ({ certPreferenceSelector: () => ({}) })));
const mockStore = configureStore();
describe('NameChange', () => {
let props = {};
let store = {};
const formId = 'useVerifiedNameForCerts';
const updateDraft = 'UPDATE_DRAFT';
const labelText = messages['account.settings.field.name.checkbox.certificate.select'].defaultMessage;
const reduxWrapper = children => (
<Router>
<IntlProvider locale="en">
<Provider store={store}>{children}</Provider>
</IntlProvider>
</Router>
);
beforeEach(() => {
store = mockStore();
props = {
fieldName: 'name',
originalFullName: 'Ed X',
originalVerifiedName: 'edX Verified',
saveState: null,
useVerifiedNameForCerts: false,
};
auth.getAuthenticatedHttpClient = jest.fn(() => ({
patch: async () => ({
data: { status: 200 },
catch: () => {},
}),
}));
auth.getAuthenticatedUser = jest.fn(() => ({ userId: 3 }));
});
afterEach(() => jest.clearAllMocks());
it('does not render if there is no verified name', () => {
props = {
...props,
originalVerifiedName: '',
};
const wrapper = render(reduxWrapper(<CertificatePreference {...props} />));
expect(wrapper).toMatchSnapshot();
});
it('does not trigger modal when checking empty checkbox, and updates draft immediately', () => {
props = {
...props,
useVerifiedNameForCerts: true,
};
render(reduxWrapper(<CertificatePreference {...props} />));
const checkbox = screen.getByLabelText(labelText);
expect(checkbox.checked).toEqual(false);
fireEvent.click(checkbox);
expect(screen.queryByRole('radiogroup')).toBeNull();
expect(mockDispatch).toHaveBeenCalledWith({
payload: { name: formId, value: false },
type: updateDraft,
});
});
it('triggers modal when attempting to uncheck checkbox', () => {
render(reduxWrapper(<CertificatePreference {...props} />));
const checkbox = screen.getByLabelText(labelText);
expect(checkbox.checked).toEqual(true);
fireEvent.click(checkbox);
expect(mockDispatch).not.toHaveBeenCalled();
screen.getByRole('radiogroup');
});
it('updates draft when changing radio value', () => {
render(reduxWrapper(<CertificatePreference {...props} />));
const checkbox = screen.getByLabelText(labelText);
fireEvent.click(checkbox);
const fullNameOption = screen.getByLabelText('Ed X (Full Name)');
const verifiedNameOption = screen.getByLabelText('edX Verified (Verified Name)');
expect(fullNameOption.checked).toEqual(true);
expect(verifiedNameOption.checked).toEqual(false);
fireEvent.click(verifiedNameOption);
expect(mockDispatch).toHaveBeenCalledWith({
payload: { name: formId, value: true },
type: updateDraft,
});
});
it('clears draft on cancel', () => {
render(reduxWrapper(<CertificatePreference {...props} />));
const checkbox = screen.getByLabelText(labelText);
fireEvent.click(checkbox);
const cancelButton = screen.getByText('Cancel');
fireEvent.click(cancelButton);
expect(mockDispatch).toHaveBeenCalledWith({ type: 'RESET_DRAFTS' });
expect(screen.queryByRole('radiogroup')).toBeNull();
});
it('submits', () => {
render(reduxWrapper(<CertificatePreference {...props} />));
const checkbox = screen.getByLabelText(labelText);
fireEvent.click(checkbox);
const submitButton = screen.getByText('Choose name');
fireEvent.click(submitButton);
expect(mockDispatch).toHaveBeenCalledWith({
payload: { formId, commitValues: false, extendedProfile: {} },
type: 'ACCOUNT_SETTINGS__SAVE_SETTINGS',
});
});
it('checks box for verified name', () => {
props = {
...props,
fieldName: 'verified_name',
useVerifiedNameForCerts: true,
};
render(reduxWrapper(<CertificatePreference {...props} />));
const checkbox = screen.getByLabelText(labelText);
expect(checkbox.checked).toEqual(true);
});
});

View File

@@ -0,0 +1,62 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`NameChange does not render if there is no verified name 1`] = `
{
"asFragment": [Function],
"baseElement": <body>
<div />
</body>,
"container": <div />,
"debug": [Function],
"findAllByAltText": [Function],
"findAllByDisplayValue": [Function],
"findAllByLabelText": [Function],
"findAllByPlaceholderText": [Function],
"findAllByRole": [Function],
"findAllByTestId": [Function],
"findAllByText": [Function],
"findAllByTitle": [Function],
"findByAltText": [Function],
"findByDisplayValue": [Function],
"findByLabelText": [Function],
"findByPlaceholderText": [Function],
"findByRole": [Function],
"findByTestId": [Function],
"findByText": [Function],
"findByTitle": [Function],
"getAllByAltText": [Function],
"getAllByDisplayValue": [Function],
"getAllByLabelText": [Function],
"getAllByPlaceholderText": [Function],
"getAllByRole": [Function],
"getAllByTestId": [Function],
"getAllByText": [Function],
"getAllByTitle": [Function],
"getByAltText": [Function],
"getByDisplayValue": [Function],
"getByLabelText": [Function],
"getByPlaceholderText": [Function],
"getByRole": [Function],
"getByTestId": [Function],
"getByText": [Function],
"getByTitle": [Function],
"queryAllByAltText": [Function],
"queryAllByDisplayValue": [Function],
"queryAllByLabelText": [Function],
"queryAllByPlaceholderText": [Function],
"queryAllByRole": [Function],
"queryAllByTestId": [Function],
"queryAllByText": [Function],
"queryAllByTitle": [Function],
"queryByAltText": [Function],
"queryByDisplayValue": [Function],
"queryByLabelText": [Function],
"queryByPlaceholderText": [Function],
"queryByRole": [Function],
"queryByTestId": [Function],
"queryByText": [Function],
"queryByTitle": [Function],
"rerender": [Function],
"unmount": [Function],
}
`;

View File

@@ -1,316 +0,0 @@
import React from 'react';
import { getConfig, getQueryParameters } from '@edx/frontend-platform';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Hyperlink } from '@edx/paragon';
import { faCheck } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import PageLoading from '../PageLoading';
import CoachingConsentForm from './CoachingConsentForm';
import messages from './CoachingConsent.messages';
import LogoSVG from '../../logo.svg';
import { fetchSettings, saveSettings, saveMultipleSettings } from '../data/actions';
import { coachingConsentPageSelector } from '../data/selectors';
const Logo = ({ src, alt, ...attributes }) => (
<>
<img src={src} alt={alt} {...attributes} />
</>
);
const SuccessMessage = props => (
<div className="col-12 col-lg-6 shadow-lg mx-auto mt-4 p-5">
<FontAwesomeIcon className="text-success" icon={faCheck} size="5x" />
<div className="h3">{props.header}</div>
<div>{props.message}</div>
<Hyperlink destination={props.continueUrl} className="d-block p-2 my-3 text-center text-white bg-primary rounded">
{props.continue}
</Hyperlink>
</div>
);
const AutoRedirect = (props) => {
window.location.href = props.redirectUrl;
return <></>;
};
const VIEWS = {
NOT_LOADED: 'NOT_LOADED',
LOADED: 'LOADED',
SUCCESS: 'SUCCESS',
SUCCESS_PENDING: 'SUCCESS_PENDING',
DECLINED: 'DECLINED',
DECLINE_PENDING: 'DECLINE_PENDING',
};
class CoachingConsent extends React.Component {
constructor(props, context) {
super(props, context);
// Used to redirect back to the courseware.
const nextUrl = this.sanitizeForwardingUrl(getQueryParameters().next);
this.state = {
redirectUrl: nextUrl || `${getConfig().LMS_BASE_URL}/dashboard/`,
formErrors: {},
formSubmitted: false,
declineSubmitted: false,
allSubmissionsComplete: false,
};
this.handleSubmit = this.handleSubmit.bind(this);
this.declineCoaching = this.declineCoaching.bind(this);
}
componentDidMount() {
this.props.fetchSettings();
}
componentDidUpdate(prevProps, prevState) {
/*
When we are submitting the form, we're calling saveSettings 3 times, which causes
multiple parallel redux flows. Because of this we can't rely on just the redux states
being sent in through props. For instance if the coaching submission and name
submission happen in near parallel, the coaching flow could return errors in
formErrors and the name flow could overwrite the formErrors with an empty object.
To minimize disruption to the rest of the app, we're going to manage flow state from
within this component.
*/
// If a new error comes in, store it before the next redux call overwrites it.
let allFormErrors = {};
let allSubmissionsComplete = false;
// Collect new errors and add to state (will be cleared on new submission)
const newErrorsFound = (
this.props.formErrors !== prevProps.formErrors
&& Object.keys(this.props.formErrors).length > 0
);
if (newErrorsFound) {
allFormErrors = Object.assign({}, this.state.formErrors, this.props.formErrors);
}
// Check if all values from the form have confirmation values
if (
this.state.formSubmitted &&
this.props.saveState === 'complete'
) {
allSubmissionsComplete = true;
}
// Check if all values from the decline link have confirmation values
if (this.props.confirmationValues.coaching && this.state.declineSubmitted) {
allSubmissionsComplete = true;
}
if (newErrorsFound || (allSubmissionsComplete !== prevState.allSubmissionsComplete)) {
this.setState({
formErrors: allFormErrors,
allSubmissionsComplete,
});
}
}
sanitizeForwardingUrl(url) {
// Redirect to root of MFE if invalid next param is sent
return url && url.startsWith(getConfig().LMS_BASE_URL) ? url : `${getConfig().LMS_BASE_URL}/dashboard/`;
}
async handleSubmit(e) {
e.preventDefault();
this.setState({
formErrors: {},
formSubmitted: true,
declineSubmitted: false,
});
// Must store target values or they disappear before the async function can use them.
const fullName = e.target.fullName.value;
const phoneNumber = e.target.phoneNumber.value;
const coachingValues = this.props.formValues.coaching;
// !important: The order of this data matters!
// The order that this data is in, is the order that the saveSettings() function
// is called.
const settingsSubmissions = [];
if (!this.props.profileDataManager) {
settingsSubmissions.push({
formId: 'name',
commitValues: fullName,
});
}
Array.prototype.push.apply(settingsSubmissions, [
{
formId: 'coaching',
commitValues: {
...coachingValues,
phone_number: phoneNumber,
coaching_consent: true,
consent_form_seen: true,
},
},
{
formId: 'phone_number',
commitValues: phoneNumber,
},
]);
this.props.saveMultipleSettings(settingsSubmissions);
}
async declineCoaching(e) {
e.preventDefault();
this.setState({
formErrors: {},
declineSubmitted: true,
formSubmitted: false,
});
// Must store target values or they disappear before the async function can use them.
const coachingValues = this.props.formValues.coaching;
this.props.saveSettings('coaching', {
...coachingValues,
coaching_consent: false,
consent_form_seen: true,
});
}
renderView(currentView) {
switch (currentView) {
case VIEWS.NOT_LOADED:
return <PageLoading srMessage="" />;
case VIEWS.LOADED:
return (<CoachingConsentForm
onSubmit={this.handleSubmit}
declineCoaching={this.declineCoaching}
formErrors={this.state.formErrors}
formValues={this.props.formValues}
redirectUrl={this.state.redirectUrl}
profileDataManager={this.props.profileDataManager}
/>);
case VIEWS.SUCCESS_PENDING:
return <PageLoading srMessage="Submitting..." />;
case VIEWS.SUCCESS:
return (<SuccessMessage
continueUrl={this.state.redirectUrl}
header={this.props.intl.formatMessage(messages['account.settings.coaching.consent.success.header'])}
message={this.props.intl.formatMessage(messages['account.settings.coaching.consent.success.message'])}
continue={this.props.intl.formatMessage(messages['account.settings.coaching.consent.success.continue'])}
/>);
case VIEWS.DECLINE_PENDING:
return <PageLoading srMessage="Redirecting..." />;
case VIEWS.DECLINED:
return <AutoRedirect redirectUrl={this.state.redirectUrl} />;
default:
return <></>;
}
}
render() {
const { loaded } = this.props;
const formHasErrors = Object.keys(this.state.formErrors).length > 0;
let currentView = null;
// This amount of logic was making the template very hard to read, so I broke it out into views.
if (!loaded) {
currentView = VIEWS.NOT_LOADED;
} else if (this.state.formSubmitted && !formHasErrors) {
if (this.state.allSubmissionsComplete) {
currentView = VIEWS.SUCCESS;
} else {
currentView = VIEWS.SUCCESS_PENDING;
}
} else if (this.state.declineSubmitted && !formHasErrors) {
if (this.state.allSubmissionsComplete) {
currentView = VIEWS.DECLINED;
} else {
currentView = VIEWS.DECLINE_PENDING;
}
} else {
currentView = VIEWS.LOADED;
}
return (
<main>
<div className="w-100 d-flex justify-content-center align-items-center shadow coaching-header">
<Logo
className="logo"
src={LogoSVG}
alt="Logo"
/>
</div>
{this.renderView(currentView)}
</main>
);
}
}
Logo.defaultProps = {
src: '',
alt: '',
};
Logo.propTypes = {
src: PropTypes.string,
alt: PropTypes.string,
};
SuccessMessage.defaultProps = {
header: '',
message: '',
continueUrl: '',
continue: '',
};
SuccessMessage.propTypes = {
header: PropTypes.string,
message: PropTypes.string,
continueUrl: PropTypes.string,
continue: PropTypes.string,
};
AutoRedirect.defaultProps = {
redirectUrl: '',
};
AutoRedirect.propTypes = {
redirectUrl: PropTypes.string,
};
CoachingConsent.defaultProps = {
loaded: false,
saveState: undefined,
profileDataManager: null,
};
CoachingConsent.propTypes = {
intl: intlShape.isRequired,
loaded: PropTypes.bool,
formValues: PropTypes.shape({
name: PropTypes.string,
phone_number: PropTypes.string,
coaching: PropTypes.shape({
coaching_consent: PropTypes.bool.isRequired,
user: PropTypes.number.isRequired,
eligible_for_coaching: PropTypes.bool.isRequired,
consent_form_seen: PropTypes.bool.isRequired,
}),
}).isRequired,
formErrors: PropTypes.shape({
coaching: PropTypes.object,
}).isRequired,
confirmationValues: PropTypes.shape({
coaching: PropTypes.object,
name: PropTypes.object,
phone_number: PropTypes.object,
}).isRequired,
fetchSettings: PropTypes.func.isRequired,
saveSettings: PropTypes.func.isRequired,
saveMultipleSettings: PropTypes.func.isRequired,
saveState: PropTypes.string,
profileDataManager: PropTypes.string,
};
export default connect(coachingConsentPageSelector, {
fetchSettings,
saveSettings,
saveMultipleSettings,
})(injectIntl(CoachingConsent));

View File

@@ -1,66 +0,0 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
'account.settings.coaching.consent.welcome.header': {
id: 'account.settings.coaching.consent.welcome.header',
defaultMessage: 'Lets get started.',
description: 'The welcome header for consent form.',
},
'account.settings.coaching.consent.welcome.subheader': {
id: 'account.settings.coaching.consent.welcome.subheader',
defaultMessage: "We're here for you from start to finish",
description: 'The welcome subheader for consent form.',
},
'account.settings.coaching.consent.description': {
id: 'account.settings.coaching.consent.description',
defaultMessage: "MicroBachelors programs include coaching that focuses on your career, education, and how you'll achieve results through one-on-one communication with an experienced professional. If youre interested, provide the information below and click “Submit,” and our coaching partner will connect with you via email and/or text message to help you move forward. Terms and conditions apply.*",
description: 'Text describing what Coaching is.',
},
'account.settings.coaching.consent.text-messaging.disclaimer': {
id: 'account.settings.coaching.consent.text-messaging.disclaimer',
defaultMessage: '* Coaching services are included at no additional cost to learners with US phone numbers. Coaching includes recurring text messages. Message and data rates may apply. Text STOP to opt-out.',
description: 'Text describing what Coaching is.',
},
'account.settings.coaching.consent.accept-coaching': {
id: 'account.settings.coaching.consent.accept-coaching',
defaultMessage: 'Sign up for coaching',
description: 'Text to confirm coaching enablement',
},
'account.settings.coaching.consent.decline-coaching': {
id: 'account.settings.coaching.consent.decline-coaching',
defaultMessage: 'I prefer not to be contacted with free coaching services',
description: 'Text to decline coaching enablement',
},
'account.settings.coaching.consent.label.name': {
id: 'account.settings.coaching.consent.label.name',
defaultMessage: 'Please confirm your name',
description: 'Label for name input',
},
'account.settings.coaching.consent.label.phone-number': {
id: 'account.settings.coaching.consent.label.phone-number',
defaultMessage: 'Enter your mobile number',
description: 'Label for mobile phone number input',
},
'account.settings.coaching.consent.success.header': {
id: 'account.settings.coaching.consent.success.header',
defaultMessage: 'Success!',
description: 'Heading announcing that submission succeeded',
},
'account.settings.coaching.consent.success.message': {
id: 'account.settings.coaching.consent.success.message',
defaultMessage: "You're signed up for coaching. You will receive a text message confirmation.",
description: 'Text announcing that you have signed up and will receive texts',
},
'account.settings.coaching.consent.success.continue': {
id: 'account.settings.coaching.consent.success.continue',
defaultMessage: 'Start my course',
description: 'Text that the user will be sent back to the courseware',
},
'account.settings.coaching.managed.support': {
id: 'account.settings.coaching.managed.support',
defaultMessage: 'support',
description: 'website support',
},
});
export default messages;

View File

@@ -1,130 +0,0 @@
import React from 'react';
import { getConfig } from '@edx/frontend-platform';
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';
import { Input, Button, Hyperlink } from '@edx/paragon';
import PropTypes from 'prop-types';
import Alert from '../Alert';
import messages from './CoachingConsent.messages';
const ErrorMessage = props => (
<div className="alert-warning mb-2">{props.message}</div>
);
const ManagedProfileAlert = ({ profileDataManager }) => (
<Alert className="alert alert-primary" role="alert">
<FormattedMessage
id="account.settings.coaching.managed.alert"
defaultMessage="Your name is managed by {managerTitle}. Contact your administrator for help."
description="alert message informing the user their account data is managed by a third party"
values={{
managerTitle: <b>{profileDataManager}</b>,
}}
/>
</Alert>
);
const CoachingForm = props => (
<div className="col-12 col-md-6 col-xl-5 mx-auto mt-4 p-5 shadow-lg">
<h2 className="h2">
{props.intl.formatMessage(messages['account.settings.coaching.consent.welcome.header'])}
</h2>
<p>{props.intl.formatMessage(messages['account.settings.coaching.consent.description'])}</p>
<div>
<form onSubmit={props.onSubmit}>
<div className="py-3">
{
!!props.profileDataManager &&
<ManagedProfileAlert profileDataManager={props.profileDataManager} />
}
<ErrorMessage message={props.formErrors.name} />
<label className="h6" htmlFor="fullName">{props.intl.formatMessage(messages['account.settings.coaching.consent.label.name'])}</label>
<Input
type="text"
name="full-name"
id="fullName"
disabled={!!props.profileDataManager}
defaultValue={props.formValues.name}
/>
</div>
<div className="py-3">
<ErrorMessage message={props.formErrors.phone_number} />
<label className="h6" htmlFor="phoneNumber">{props.intl.formatMessage(messages['account.settings.coaching.consent.label.phone-number'])}</label>
<Input
type="text"
name="full-name"
id="phoneNumber"
defaultValue={props.formValues.phone_number}
/>
</div>
<div className=" py-3">
<p className="small font-italic">
{props.intl.formatMessage(messages['account.settings.coaching.consent.text-messaging.disclaimer'])}
</p>
</div>
<ErrorMessage message={props.formErrors.coaching} />
<div className="d-flex flex-column align-items-center">
<Button className="w-100 btn-outline-primary" type="submit">
{props.intl.formatMessage(messages['account.settings.coaching.consent.accept-coaching'])}
</Button>
</div>
<div className="mt-3">
<Hyperlink
className="mt-3 text-dark btn-link small"
destination={props.redirectUrl}
onClick={props.declineCoaching}
>
{props.intl.formatMessage(messages['account.settings.coaching.consent.decline-coaching'])}
</Hyperlink>
</div>
</form>
</div>
</div>
);
CoachingForm.defaultProps = {
formErrors: {
coaching: '',
name: '',
phone_number: '',
},
};
CoachingForm.propTypes = {
intl: intlShape.isRequired,
onSubmit: PropTypes.func.isRequired,
declineCoaching: PropTypes.func.isRequired,
formValues: PropTypes.shape({
name: PropTypes.string,
phone_number: PropTypes.string,
coaching: PropTypes.shape({
coaching_consent: PropTypes.bool.isRequired,
user: PropTypes.number.isRequired,
eligible_for_coaching: PropTypes.bool.isRequired,
consent_form_seen: PropTypes.bool.isRequired,
}),
}).isRequired,
formErrors: PropTypes.shape({
coaching: PropTypes.string,
name: PropTypes.string,
phone_number: PropTypes.string,
}),
redirectUrl: PropTypes.string.isRequired,
profileDataManager: PropTypes.string.isRequired,
};
ErrorMessage.defaultProps = {
message: '',
};
ErrorMessage.propTypes = {
message: PropTypes.string,
};
ManagedProfileAlert.propTypes = {
profileDataManager: PropTypes.string.isRequired,
intl: intlShape.isRequired,
};
export default injectIntl(CoachingForm);

View File

@@ -1,99 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { ValidationFormGroup, Input } from '@edx/paragon';
import messages from './CoachingToggle.messages';
import { editableFieldSelector } from '../data/selectors';
import { saveSettings, updateDraft, saveMultipleSettings } from '../data/actions';
import EditableField from '../EditableField';
const CoachingToggle = props => (
<>
<EditableField
name="phone_number"
type="text"
value={props.phone_number}
label={props.intl.formatMessage(messages['account.settings.field.phone_number'])}
emptyLabel={props.intl.formatMessage(messages['account.settings.field.phone_number.empty'])}
onChange={props.updateDraft}
onSubmit={() => {
const { coaching } = props;
if (coaching.coaching_consent === true) {
return props.saveMultipleSettings([
{
formId: 'coaching',
commitValues: {
...coaching,
phone_number: props.phone_number,
},
},
{
formId: 'phone_number',
commitValues: props.phone_number,
},
]);
}
return props.saveSettings('phone_number', props.phone_number);
}}
/>
<ValidationFormGroup
for="coachingConsent"
helpText={props.intl.formatMessage(messages['account.settings.field.coaching_consent.tooltip'])}
invalid={!!props.error}
invalidMessage={props.intl.formatMessage(messages['account.settings.field.coaching_consent.error'])}
className="custom-control custom-switch"
>
<Input
name={props.name}
className="custom-control-input"
disabled={props.saveState === 'pending'}
type="checkbox"
id="coachingConsent"
checked={props.coaching.coaching_consent}
value={props.coaching.coaching_consent}
onChange={async (e) => {
const { name } = e.target;
// eslint-disable-next-line camelcase
const { user, eligible_for_coaching } = props.coaching;
const value = {
user,
eligible_for_coaching,
coaching_consent: e.target.checked,
};
props.saveSettings(name, value);
}}
/>
<label className="custom-control-label" htmlFor="coachingConsent">{props.intl.formatMessage(messages['account.settings.field.coaching_consent'])}</label>
</ValidationFormGroup>
</>
);
CoachingToggle.defaultProps = {
phone_number: '',
error: '',
saveState: undefined,
};
CoachingToggle.propTypes = {
name: PropTypes.string.isRequired,
error: PropTypes.string,
coaching: PropTypes.shape({
coaching_consent: PropTypes.bool.isRequired,
user: PropTypes.number.isRequired,
eligible_for_coaching: PropTypes.bool.isRequired,
}).isRequired,
saveState: PropTypes.oneOf(['default', 'pending', 'complete', 'error']),
saveSettings: PropTypes.func.isRequired,
saveMultipleSettings: PropTypes.func.isRequired,
updateDraft: PropTypes.func.isRequired,
intl: intlShape.isRequired,
phone_number: PropTypes.string,
};
export default connect(editableFieldSelector, {
saveSettings,
updateDraft,
saveMultipleSettings,
})(injectIntl(CoachingToggle));

View File

@@ -1,31 +0,0 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
'account.settings.field.phone_number': {
id: 'account.settings.field.phone_number',
defaultMessage: 'Phone Number',
description: 'The label for a phone numbers setting in the user profile',
},
'account.settings.field.phone_number.empty': {
id: 'account.settings.field.phone_number.empty',
defaultMessage: 'Add a phone number',
description: 'placeholder for a profiles empty phone number field',
},
'account.settings.field.coaching_consent': {
id: 'account.settings.field.coaching_consent',
defaultMessage: 'Coaching consent',
description: 'The label for the coaching consent setting in the user profile',
},
'account.settings.field.coaching_consent.tooltip': {
id: 'account.settings.field.coaching_consent.tooltip',
defaultMessage: 'MicroBachelors programs include text message based coaching that helps you pair educational experiences with your career goals through one-on-one advice. Coaching services are included at no additional cost, and are available to learners with U.S. mobile phone numbers. Standard messaging rates apply. Text STOP at anytime to opt-out of messages.',
description: 'A tooltip explaining what coaching is and who it is for',
},
'account.settings.field.coaching_consent.error': {
id: 'account.settings.field.coaching_consent.error',
defaultMessage: 'A valid US phone number is required to opt into coaching',
description: 'An error message that displays when a user attempts to consent to coaching without first providing a phone number in their profile',
},
});
export default messages;

View File

@@ -1,51 +0,0 @@
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { getConfig } from '@edx/frontend-platform';
import get from 'lodash.get';
/**
* get all settings related to the coaching plugin. Settings used
* by Microbachelors students.
* @param {Number} userId users are identified in the api by LMS id
*/
export async function getCoachingPreferences(userId) {
let data = {};
try {
({ data } = await getAuthenticatedHttpClient()
.get(`${getConfig().LMS_BASE_URL}/api/coaching/v1/users/${userId}/`));
} catch (error) {
// If a user isn't active the API call will fail with a lack of credentials.
data = {
coaching_consent: false,
user: userId,
eligible_for_coaching: false,
consent_form_seen: false,
};
}
return data;
}
/**
* patch all of the settings related to coaching.
* @param {Number} userId users are identified in the api by LMS id
* @param {Object} commitValues { coaching }
*/
export async function patchCoachingPreferences(userId, commitValues) {
const requestUrl = `${getConfig().LMS_BASE_URL}/api/coaching/v1/users/${userId}/`;
const { coaching } = commitValues;
coaching.user = userId;
await getAuthenticatedHttpClient()
.patch(requestUrl, coaching)
.catch((error) => {
const apiError = Object.create(error);
apiError.fieldErrors = JSON.parse(error.customAttributes.httpErrorResponseData);
if (get(apiError, 'fieldErrors.phone_number')) {
// eslint-disable-next-line prefer-destructuring
apiError.fieldErrors.coaching = apiError.fieldErrors.phone_number[0];
delete apiError.fieldErrors.phone_number;
}
throw apiError;
});
return commitValues;
}

View File

@@ -9,6 +9,7 @@ export const OPEN_FORM = 'OPEN_FORM';
export const CLOSE_FORM = 'CLOSE_FORM';
export const UPDATE_DRAFT = 'UPDATE_DRAFT';
export const RESET_DRAFTS = 'RESET_DRAFTS';
export const BEGIN_NAME_CHANGE = 'BEGIN_NAME_CHANGE';
// FETCH SETTINGS ACTIONS
@@ -25,6 +26,8 @@ export const fetchSettingsSuccess = ({
thirdPartyAuthProviders,
profileDataManager,
timeZones,
verifiedNameHistory,
countriesCodesList,
}) => ({
type: FETCH_SETTINGS.SUCCESS,
payload: {
@@ -32,6 +35,8 @@ export const fetchSettingsSuccess = ({
thirdPartyAuthProviders,
profileDataManager,
timeZones,
verifiedNameHistory,
countriesCodesList,
},
});
@@ -44,7 +49,6 @@ export const fetchSettingsReset = () => ({
type: FETCH_SETTINGS.RESET,
});
// FORM STATE ACTIONS
export const openForm = formId => ({
@@ -69,12 +73,15 @@ export const resetDrafts = () => ({
type: RESET_DRAFTS,
});
export const beginNameChange = (formId) => ({
type: BEGIN_NAME_CHANGE,
payload: { formId },
});
// SAVE SETTINGS ACTIONS
export const saveSettings = (formId, commitValues) => ({
export const saveSettings = (formId, commitValues, extendedProfile = {}) => ({
type: SAVE_SETTINGS.BASE,
payload: { formId, commitValues },
payload: { formId, commitValues, extendedProfile },
});
export const saveSettingsBegin = () => ({
@@ -100,9 +107,9 @@ export const savePreviousSiteLanguage = previousSiteLanguage => ({
payload: { previousSiteLanguage },
});
export const saveMultipleSettings = settingsArray => ({
export const saveMultipleSettings = (settingsArray, form = null) => ({
type: SAVE_MULTIPLE_SETTINGS.BASE,
payload: { settingsArray },
payload: { settingsArray, form },
});
export const saveMultipleSettingsBegin = () => ({

View File

@@ -1,4 +1,3 @@
export const YEAR_OF_BIRTH_OPTIONS = (() => {
const currentYear = new Date().getFullYear();
const years = [];
@@ -11,6 +10,11 @@ export const YEAR_OF_BIRTH_OPTIONS = (() => {
return years.reverse();
})();
export const COPPA_COMPLIANCE_YEAR = (() => {
const currentYear = new Date().getFullYear();
return currentYear - 13;
})();
export const EDUCATION_LEVELS = [
'',
'p',
@@ -21,7 +25,7 @@ export const EDUCATION_LEVELS = [
'jhs',
'el',
'none',
'o',
'other',
];
export const GENDER_OPTIONS = [
@@ -30,6 +34,21 @@ export const GENDER_OPTIONS = [
'm',
'o',
];
export const WORK_EXPERIENCE_OPTIONS = [
'',
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'10+',
];
export const COUNTRY_WITH_STATES = 'US';
@@ -112,3 +131,7 @@ const COUNTRY_STATES_MAP = {
export function getStatesList(country) {
return country && COUNTRY_STATES_MAP[country.toUpperCase()];
}
export const FIELD_LABELS = {
COUNTRY: 'country',
};

View File

@@ -8,11 +8,13 @@ import {
UPDATE_DRAFT,
RESET_DRAFTS,
SAVE_MULTIPLE_SETTINGS,
BEGIN_NAME_CHANGE,
} from './actions';
import { reducer as deleteAccountReducer, DELETE_ACCOUNT } from '../delete-account';
import { reducer as siteLanguageReducer, FETCH_SITE_LANGUAGES } from '../site-language';
import { reducer as resetPasswordReducer, RESET_PASSWORD } from '../reset-password';
import { reducer as nameChangeReducer, REQUEST_NAME_CHANGE } from '../name-change';
import { reducer as thirdPartyAuthReducer, DISCONNECT_AUTH } from '../third-party-auth';
export const defaultState = {
@@ -31,10 +33,16 @@ export const defaultState = {
deleteAccount: deleteAccountReducer(),
siteLanguage: siteLanguageReducer(),
resetPassword: resetPasswordReducer(),
nameChange: nameChangeReducer(),
thirdPartyAuth: thirdPartyAuthReducer(),
nameChangeModal: false,
verifiedName: null,
mostRecentVerifiedName: {},
verifiedNameHistory: {},
countriesCodesList: [],
};
const reducer = (state = defaultState, action) => {
const reducer = (state = defaultState, action = {}) => {
let dispatcherIsOpenForm;
switch (action.type) {
@@ -48,16 +56,16 @@ const reducer = (state = defaultState, action) => {
case FETCH_SETTINGS.SUCCESS:
return {
...state,
values: Object.assign({}, state.values, action.payload.values),
values: { ...state.values, ...action.payload.values },
// Dump the providers into thirdPartyAuth.
thirdPartyAuth: Object.assign({}, state.thirdPartyAuth, {
providers: action.payload.thirdPartyAuthProviders,
}),
thirdPartyAuth: { ...state.thirdPartyAuth, providers: action.payload.thirdPartyAuthProviders },
profileDataManager: action.payload.profileDataManager,
timeZones: action.payload.timeZones,
loading: false,
loaded: true,
loadingError: null,
verifiedNameHistory: action.payload.verifiedNameHistory,
countriesCodesList: action.payload.countriesCodesList,
};
case FETCH_SETTINGS.FAILURE:
return {
@@ -91,15 +99,14 @@ const reducer = (state = defaultState, action) => {
saveState: null,
errors: {},
drafts: {},
nameChangeModal: false,
};
}
return state;
case UPDATE_DRAFT:
return {
...state,
drafts: Object.assign({}, state.drafts, {
[action.payload.name]: action.payload.value,
}),
drafts: { ...state.drafts, [action.payload.name]: action.payload.value },
saveState: null,
errors: {},
};
@@ -110,6 +117,15 @@ const reducer = (state = defaultState, action) => {
drafts: {},
};
case BEGIN_NAME_CHANGE:
return {
...state,
saveState: 'error',
nameChangeModal: {
formId: action.payload.formId,
},
};
case SAVE_SETTINGS.BEGIN:
return {
...state,
@@ -120,19 +136,18 @@ const reducer = (state = defaultState, action) => {
return {
...state,
saveState: 'complete',
values: Object.assign({}, state.values, action.payload.values),
values: { ...state.values, ...action.payload.values },
errors: {},
confirmationValues: Object.assign(
{},
state.confirmationValues,
action.payload.confirmationValues,
),
confirmationValues: {
...state.confirmationValues,
...action.payload.confirmationValues,
},
};
case SAVE_SETTINGS.FAILURE:
return {
...state,
saveState: 'error',
errors: Object.assign({}, state.errors, action.payload.errors),
errors: { ...state.errors, ...action.payload.errors },
};
case SAVE_SETTINGS.RESET:
return {
@@ -161,7 +176,7 @@ const reducer = (state = defaultState, action) => {
return {
...state,
saveState: 'error',
errors: Object.assign({}, state.errors, action.payload.errors),
errors: { ...state.errors, ...action.payload.errors },
};
case FETCH_TIME_ZONES.SUCCESS:
@@ -202,6 +217,15 @@ const reducer = (state = defaultState, action) => {
resetPassword: resetPasswordReducer(state.resetPassword, action),
};
case REQUEST_NAME_CHANGE.BEGIN:
case REQUEST_NAME_CHANGE.SUCCESS:
case REQUEST_NAME_CHANGE.FAILURE:
case REQUEST_NAME_CHANGE.RESET:
return {
...state,
nameChange: nameChangeReducer(state.nameChange, action),
};
case DISCONNECT_AUTH.BEGIN:
case DISCONNECT_AUTH.SUCCESS:
case DISCONNECT_AUTH.FAILURE:

View File

@@ -1,4 +1,6 @@
import { call, put, delay, takeEvery, all } from 'redux-saga/effects';
import {
call, put, delay, takeEvery, all,
} from 'redux-saga/effects';
import { publish } from '@edx/frontend-platform';
import { getLocale, handleRtl, LOCALE_CHANGED } from '@edx/frontend-platform/i18n';
@@ -23,11 +25,13 @@ import {
saveMultipleSettingsBegin,
saveMultipleSettingsSuccess,
saveMultipleSettingsFailure,
beginNameChange,
} from './actions';
// Sub-modules
import { saga as deleteAccountSaga } from '../delete-account';
import { saga as resetPasswordSaga } from '../reset-password';
import { saga as nameChangeSaga } from '../name-change';
import {
saga as siteLanguageSaga,
patchPreferences,
@@ -36,7 +40,12 @@ import {
import { saga as thirdPartyAuthSaga } from '../third-party-auth';
// Services
import { getSettings, patchSettings, getTimeZones } from './service';
import {
getSettings,
patchSettings,
getTimeZones,
getVerifiedNameHistory,
} from './service';
export function* handleFetchSettings() {
try {
@@ -44,7 +53,7 @@ export function* handleFetchSettings() {
const { username, userId, roles: userRoles } = getAuthenticatedUser();
const {
thirdPartyAuthProviders, profileDataManager, timeZones, ...values
thirdPartyAuthProviders, profileDataManager, timeZones, countries, ...values
} = yield call(
getSettings,
username,
@@ -52,13 +61,17 @@ export function* handleFetchSettings() {
userId,
);
if (values.country) yield put(fetchTimeZones(values.country));
const verifiedNameHistory = yield call(getVerifiedNameHistory);
if (values.country) { yield put(fetchTimeZones(values.country)); }
yield put(fetchSettingsSuccess({
values,
thirdPartyAuthProviders,
profileDataManager,
timeZones,
verifiedNameHistory,
countriesCodesList: countries,
}));
} catch (e) {
yield put(fetchSettingsFailure(e.message));
@@ -71,8 +84,8 @@ export function* handleSaveSettings(action) {
yield put(saveSettingsBegin());
const { username, userId } = getAuthenticatedUser();
const { commitValues, formId } = action.payload;
const commitData = { [formId]: commitValues };
const { commitValues, formId, extendedProfile } = action.payload;
const commitData = Object.keys(extendedProfile).length > 0 ? extendedProfile : { [formId]: commitValues };
let savedValues = null;
if (formId === 'siteLanguage') {
const previousSiteLanguage = getLocale();
@@ -91,11 +104,14 @@ export function* handleSaveSettings(action) {
savedValues = yield call(patchSettings, username, commitData, userId);
}
yield put(saveSettingsSuccess(savedValues, commitData));
if (savedValues.country) yield put(fetchTimeZones(savedValues.country));
if (savedValues.country) { yield put(fetchTimeZones(savedValues.country)); }
yield delay(1000);
yield put(closeForm(action.payload.formId));
} catch (e) {
if (e.fieldErrors) {
if (e.fieldErrors.name?.includes('verification')) {
yield put(beginNameChange('name'));
}
yield put(saveSettingsFailure({ fieldErrors: e.fieldErrors }));
} else {
yield put(saveSettingsFailure(e.message));
@@ -104,13 +120,12 @@ export function* handleSaveSettings(action) {
}
}
// handles mutiple settings saved at once, in order, and stops executing on first failure.
export function* handleSaveMultipleSettings(settings) {
export function* handleSaveMultipleSettings(action) {
try {
yield put(saveMultipleSettingsBegin());
const { username, userId } = getAuthenticatedUser();
const { settingsArray } = settings.payload;
const { settingsArray, form } = action.payload;
for (let i = 0; i < settingsArray.length; i += 1) {
const { formId, commitValues } = settingsArray[i];
yield put(saveSettingsBegin());
@@ -118,9 +133,16 @@ export function* handleSaveMultipleSettings(settings) {
const savedSettings = yield call(patchSettings, username, commitData, userId);
yield put(saveSettingsSuccess(savedSettings, commitData));
}
yield put(saveMultipleSettingsSuccess(settings));
yield put(saveMultipleSettingsSuccess(action));
if (form) {
yield delay(1000);
yield put(closeForm(form));
}
} catch (e) {
if (e.fieldErrors) {
if (e.fieldErrors.name?.includes('verification')) {
yield put(beginNameChange('name'));
}
yield put(saveMultipleSettingsFailure({ fieldErrors: e.fieldErrors }));
} else {
yield put(saveMultipleSettingsFailure(e.message));
@@ -134,7 +156,6 @@ export function* handleFetchTimeZones(action) {
yield put(fetchTimeZonesSuccess(response, action.payload.country));
}
export default function* saga() {
yield takeEvery(FETCH_SETTINGS.BASE, handleFetchSettings);
yield takeEvery(SAVE_SETTINGS.BASE, handleSaveSettings);
@@ -144,6 +165,7 @@ export default function* saga() {
deleteAccountSaga(),
siteLanguageSaga(),
resetPasswordSaga(),
nameChangeSaga(),
thirdPartyAuthSaga(),
]);
}

View File

@@ -1,6 +1,6 @@
import { createSelector, createStructuredSelector } from 'reselect';
import { siteLanguageOptionsSelector, siteLanguageListSelector } from '../site-language';
import { siteLanguageListSelector, siteLanguageOptionsSelector } from '../site-language';
import { compareVerifiedNamesByCreatedDate } from '../../utils';
export const storeName = 'accountSettings';
@@ -8,9 +8,74 @@ export const accountSettingsSelector = state => ({ ...state[storeName] });
const editableFieldNameSelector = (state, props) => props.name;
const verifiedNameSettingsSelector = createSelector(
accountSettingsSelector,
accountSettings => ({
history: accountSettings.verifiedNameHistory.results,
useVerifiedNameForCerts: accountSettings?.verifiedNameHistory.use_verified_name_for_certs,
}),
);
const sortedVerifiedNameHistorySelector = createSelector(
verifiedNameSettingsSelector,
verifiedNameSettings => {
const { history } = verifiedNameSettings;
if (Array.isArray(history)) {
return history.sort(compareVerifiedNamesByCreatedDate);
}
return [];
},
);
const mostRecentVerifiedNameSelector = createSelector(
sortedVerifiedNameHistorySelector,
sortedHistory => (sortedHistory.length > 0 ? sortedHistory[0] : null),
);
const mostRecentApprovedVerifiedNameValueSelector = createSelector(
sortedVerifiedNameHistorySelector,
mostRecentVerifiedNameSelector,
(sortedHistory, mostRecentVerifiedName) => {
const approvedVerifiedNames = sortedHistory.filter(name => name.status === 'approved');
const approvedVerifiedName = approvedVerifiedNames.length > 0 ? approvedVerifiedNames[0] : null;
let verifiedName = null;
switch (mostRecentVerifiedName && mostRecentVerifiedName.status) {
case 'approved':
case 'denied':
case 'pending':
verifiedName = approvedVerifiedName;
break;
case 'submitted':
verifiedName = mostRecentVerifiedName;
break;
default:
verifiedName = null;
}
return verifiedName;
},
);
const valuesSelector = createSelector(
accountSettingsSelector,
accountSettings => accountSettings.values,
mostRecentApprovedVerifiedNameValueSelector,
(accountSettings, mostRecentApprovedVerifiedNameValue) => {
let useVerifiedNameForCerts = (
accountSettings.verifiedNameHistory?.use_verified_name_for_certs || false
);
if (Object.keys(accountSettings.confirmationValues).includes('useVerifiedNameForCerts')) {
useVerifiedNameForCerts = accountSettings.confirmationValues.useVerifiedNameForCerts;
}
return {
...accountSettings.values,
verified_name: mostRecentApprovedVerifiedNameValue?.verified_name,
useVerifiedNameForCerts,
};
},
);
const draftsSelector = createSelector(
@@ -23,6 +88,11 @@ const previousSiteLanguageSelector = createSelector(
accountSettings => accountSettings.previousSiteLanguage,
);
const countriesSelector = createSelector(
accountSettingsSelector,
accountSettings => accountSettings.countriesCodesList,
);
const editableFieldErrorSelector = createSelector(
editableFieldNameSelector,
accountSettingsSelector,
@@ -41,16 +111,16 @@ const isEditingSelector = createSelector(
(name, accountSettings) => accountSettings.openFormId === name,
);
const confirmationValuesSelector = createSelector(
accountSettingsSelector,
accountSettings => accountSettings.confirmationValues,
);
const errorSelector = createSelector(
accountSettingsSelector,
accountSettings => accountSettings.errors,
);
const nameChangeModalSelector = createSelector(
accountSettingsSelector,
accountSettings => accountSettings.nameChangeModal,
);
const saveStateSelector = createSelector(
accountSettingsSelector,
accountSettings => accountSettings.saveState,
@@ -70,9 +140,19 @@ export const profileDataManagerSelector = createSelector(
export const staticFieldsSelector = createSelector(
accountSettingsSelector,
accountSettings => (accountSettings.profileDataManager ? ['name', 'email', 'country'] : []),
);
mostRecentVerifiedNameSelector,
(accountSettings, verifiedName) => {
const staticFields = [];
if (accountSettings.profileDataManager) {
staticFields.push('name', 'email', 'country');
}
if (verifiedName && ['submitted'].includes(verifiedName.status)) {
staticFields.push('verifiedName');
}
return staticFields;
},
);
/**
* If there's no draft present at all (undefined), use the original committed value.
@@ -81,13 +161,31 @@ function chooseFormValue(draft, committed) {
return draft !== undefined ? draft : committed;
}
const formValuesSelector = createSelector(
export const formValuesSelector = createSelector(
valuesSelector,
draftsSelector,
(values, drafts) => {
const formValues = {};
Object.entries(values).forEach(([name, value]) => {
formValues[name] = chooseFormValue(drafts[name], value) || '';
if (typeof value === 'boolean') {
formValues[name] = chooseFormValue(drafts[name], value);
} else if (typeof value === 'object' && name === 'extended_profile' && value !== null) {
const extendedProfile = value.slice();
const draftsKeys = Object.keys(drafts);
if (draftsKeys.length !== 0) {
const draftFieldName = draftsKeys[0];
const index = extendedProfile.findIndex((profile) => profile.field_name === draftFieldName);
if (index !== -1) {
extendedProfile[index] = { field_name: draftFieldName, field_value: drafts[draftFieldName] };
}
}
formValues.extended_profile = [...extendedProfile];
} else {
formValues[name] = chooseFormValue(drafts[name], value) || '';
}
});
return formValues;
},
@@ -95,7 +193,7 @@ const formValuesSelector = createSelector(
const transformTimeZonesToOptions = timeZoneArr => timeZoneArr
.map(({ time_zone, description }) => ({ // eslint-disable-line camelcase
value: time_zone, label: description,
value: time_zone, label: description, // eslint-disable-line camelcase
}));
const timeZonesSelector = createSelector(
@@ -132,21 +230,37 @@ export const accountSettingsPageSelector = createSelector(
siteLanguageOptionsSelector,
siteLanguageSelector,
formValuesSelector,
valuesSelector,
draftsSelector,
errorSelector,
profileDataManagerSelector,
staticFieldsSelector,
timeZonesSelector,
countryTimeZonesSelector,
activeAccountSelector,
nameChangeModalSelector,
mostRecentApprovedVerifiedNameValueSelector,
mostRecentVerifiedNameSelector,
sortedVerifiedNameHistorySelector,
countriesSelector,
(
accountSettings,
siteLanguageOptions,
siteLanguage,
formValues,
committedValues,
drafts,
formErrors,
profileDataManager,
staticFields,
timeZoneOptions,
countryTimeZoneOptions,
activeAccount,
nameChangeModal,
verifiedName,
mostRecentVerifiedName,
verifiedNameHistory,
countriesCodesList,
) => ({
siteLanguageOptions,
siteLanguage,
@@ -157,37 +271,46 @@ export const accountSettingsPageSelector = createSelector(
countryTimeZoneOptions,
isActive: activeAccount,
formValues,
committedValues,
drafts,
formErrors,
profileDataManager,
staticFields,
tpaProviders: accountSettings.thirdPartyAuth.providers,
nameChangeModal,
verifiedName,
mostRecentVerifiedName,
verifiedNameHistory,
countriesCodesList,
}),
);
export const coachingConsentPageSelector = createSelector(
accountSettingsSelector,
export const certPreferenceSelector = createSelector(
valuesSelector,
formValuesSelector,
activeAccountSelector,
profileDataManagerSelector,
mostRecentApprovedVerifiedNameValueSelector,
saveStateSelector,
confirmationValuesSelector,
errorSelector,
(
accountSettings,
committedValues,
formValues,
activeAccount,
profileDataManager,
mostRecentApprovedVerifiedNameValue,
saveState,
confirmationValues,
errors,
) => ({
loading: accountSettings.loading,
loaded: accountSettings.loaded,
loadingError: accountSettings.loadingError,
isActive: activeAccount,
profileDataManager,
formValues,
originalFullName: committedValues?.name || '',
originalVerifiedName: mostRecentApprovedVerifiedNameValue?.verified_name || '',
useVerifiedNameForCerts: formValues.useVerifiedNameForCerts || false,
saveState,
confirmationValues,
formErrors: errors,
}),
);
export const nameChangeSelector = createSelector(
accountSettingsSelector,
formValuesSelector,
(accountSettings, formValues) => ({
...accountSettings.nameChange,
formValues,
}),
);

View File

@@ -0,0 +1,72 @@
import { profileDataManagerSelector, formValuesSelector } from './selectors';
const testValue = 'test VALUE';
describe('profileDataManagerSelector', () => {
it('returns the profileDataManager from the state', () => {
const state = {
accountSettings: {
profileDataManager: { testValue },
},
};
const result = profileDataManagerSelector(state);
expect(result).toEqual(state.accountSettings.profileDataManager);
});
it('should correctly select form values', () => {
const state = {
accountSettings: {
values: {
name: 'John Doe',
age: 25,
},
drafts: {
age: 26,
},
verifiedNameHistory: 'test',
confirmationValues: {},
},
};
const result = formValuesSelector(state);
const expected = {
name: 'John Doe',
age: 26,
verified_name: '',
useVerifiedNameForCerts: false,
};
expect(result).toEqual(expected);
});
it('should correctly select form values with extended_profile', () => {
// Mock data with extended_profile field in both values and drafts
const state = {
accountSettings: {
values: {
extended_profile: [
{ field_name: 'test_field', field_value: '5' },
],
},
drafts: { test_field: '6' },
verifiedNameHistory: 'test',
confirmationValues: {},
},
};
const result = formValuesSelector(state);
const expected = {
verified_name: '',
useVerifiedNameForCerts: false,
extended_profile: [ // Draft value should override the committed value
{ field_name: 'test_field', field_value: '6' }, // Value from the committed values
],
};
expect(result).toEqual(expected);
});
});

View File

@@ -1,15 +1,17 @@
import { getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { logError } from '@edx/frontend-platform/logging';
import pick from 'lodash.pick';
import omit from 'lodash.omit';
import isEmpty from 'lodash.isempty';
import { handleRequestError, unpackFieldErrors } from './utils';
import { getThirdPartyAuthProviders } from '../third-party-auth';
import { getCoachingPreferences, patchCoachingPreferences } from '../coaching/data/service';
import { postVerifiedNameConfig } from '../certificate-preference/data/service';
import { FIELD_LABELS } from './constants';
const SOCIAL_PLATFORMS = [
{ id: 'twitter', key: 'social_link_twitter' },
{ id: 'xTwitter', key: 'social_link_x' },
{ id: 'facebook', key: 'social_link_facebook' },
{ id: 'linkedin', key: 'social_link_linkedin' },
];
@@ -41,7 +43,9 @@ function packAccountCommitData(commitData) {
SOCIAL_PLATFORMS.forEach(({ id, key }) => {
// Skip missing values. Empty strings are valid values and should be preserved.
if (commitData[key] === undefined) return;
if (commitData[key] === undefined) {
return;
}
packedData.social_links = [{ platform: id, social_link: commitData[key] }];
delete packedData[key];
@@ -149,43 +153,107 @@ export async function getProfileDataManager(username, userRoles) {
return null;
}
export async function getVerifiedName() {
let data;
const client = getAuthenticatedHttpClient();
try {
const requestUrl = `${getConfig().LMS_BASE_URL}/api/edx_name_affirmation/v1/verified_name`;
({ data } = await client.get(requestUrl));
} catch (error) {
return {};
}
return data;
}
export async function getVerifiedNameHistory() {
let data;
const client = getAuthenticatedHttpClient();
try {
const requestUrl = `${getConfig().LMS_BASE_URL}/api/edx_name_affirmation/v1/verified_name/history`;
({ data } = await client.get(requestUrl));
} catch (error) {
return {};
}
return data;
}
export async function postVerifiedName(data) {
const requestConfig = { headers: { Accept: 'application/json' } };
const requestUrl = `${getConfig().LMS_BASE_URL}/api/edx_name_affirmation/v1/verified_name`;
await getAuthenticatedHttpClient()
.post(requestUrl, data, requestConfig)
.catch(error => handleRequestError(error));
}
function extractCountryList(data) {
return data?.fields
.find(({ name }) => name === FIELD_LABELS.COUNTRY)
?.options?.map(({ value }) => (value)) || [];
}
export async function getCountryList() {
const url = `${getConfig().LMS_BASE_URL}/user_api/v1/account/registration/`;
try {
const { data } = await getAuthenticatedHttpClient().get(url);
return extractCountryList(data);
} catch (e) {
logError(e);
return [];
}
}
/**
* A single function to GET everything considered a setting.
* Currently encapsulates Account, Preferences, Coaching, and ThirdPartyAuth
* A single function to GET everything considered a setting. Currently encapsulates Account, Preferences, and
* ThirdPartyAuth.
*/
export async function getSettings(username, userRoles, userId) {
const results = await Promise.all([
export async function getSettings(username, userRoles) {
const [
account,
preferences,
thirdPartyAuthProviders,
profileDataManager,
timeZones,
countries,
] = await Promise.all([
getAccount(username),
getPreferences(username),
getThirdPartyAuthProviders(),
getProfileDataManager(username, userRoles),
getTimeZones(),
getConfig().COACHING_ENABLED && getCoachingPreferences(userId),
getCountryList(),
]);
return {
...results[0],
...results[1],
thirdPartyAuthProviders: results[2],
profileDataManager: results[3],
timeZones: results[4],
coaching: results[5],
...account,
...preferences,
thirdPartyAuthProviders,
profileDataManager,
timeZones,
countries,
};
}
/**
* A single function to PATCH everything considered a setting.
* Currently encapsulates Account, Preferences, coaching and ThirdPartyAuth
* Currently encapsulates Account, Preferences, ThirdPartyAuth
*/
export async function patchSettings(username, commitValues, userId) {
export async function patchSettings(username, commitValues) {
// Note: time_zone exists in the return value from user/v1/accounts
// but it is always null and won't update. It also exists in
// user/v1/preferences where it does update. This is the one we use.
const preferenceKeys = ['time_zone'];
const coachingKeys = ['coaching'];
const accountCommitValues = omit(commitValues, preferenceKeys, coachingKeys);
const certificateKeys = ['useVerifiedNameForCerts'];
const accountCommitValues = omit(
commitValues,
preferenceKeys,
certificateKeys,
);
const preferenceCommitValues = pick(commitValues, preferenceKeys);
const coachingCommitValues = pick(commitValues, coachingKeys);
const certCommitValues = pick(commitValues, certificateKeys);
const patchRequests = [];
if (!isEmpty(accountCommitValues)) {
@@ -194,8 +262,8 @@ export async function patchSettings(username, commitValues, userId) {
if (!isEmpty(preferenceCommitValues)) {
patchRequests.push(patchPreferences(username, preferenceCommitValues));
}
if (!isEmpty(coachingCommitValues)) {
patchRequests.push(patchCoachingPreferences(userId, coachingCommitValues));
if (!isEmpty(certCommitValues)) {
patchRequests.push(postVerifiedNameConfig(username, certCommitValues));
}
const results = await Promise.all(patchRequests);

View File

@@ -0,0 +1,181 @@
import { getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { logError } from '@edx/frontend-platform/logging';
import { FIELD_LABELS } from './constants';
import {
getAccount,
patchAccount,
getPreferences,
patchPreferences,
getTimeZones,
getProfileDataManager,
getVerifiedName,
getVerifiedNameHistory,
postVerifiedName,
getCountryList,
patchSettings,
} from './service';
jest.mock('@edx/frontend-platform');
jest.mock('@edx/frontend-platform/auth');
jest.mock('@edx/frontend-platform/logging');
const mockHttpClient = {
get: jest.fn(),
patch: jest.fn(),
post: jest.fn(),
};
getAuthenticatedHttpClient.mockReturnValue(mockHttpClient);
getConfig.mockReturnValue({ LMS_BASE_URL: 'http://lms.test' });
beforeEach(() => {
jest.clearAllMocks();
});
describe('account service', () => {
describe('getAccount', () => {
it('returns unpacked account data', async () => {
const apiResponse = {
username: 'testuser',
social_links: [{ platform: 'xTwitter', social_link: 'http://t' }],
language_proficiencies: [{ code: 'en' }],
};
mockHttpClient.get.mockResolvedValue({ data: apiResponse });
const result = await getAccount('testuser');
expect(mockHttpClient.get).toHaveBeenCalledWith('http://lms.test/api/user/v1/accounts/testuser');
expect(result.social_link_x).toEqual('http://t');
expect(result.language_proficiencies).toEqual('en');
});
});
describe('patchAccount', () => {
it('sends packed commit data and returns unpacked response', async () => {
const commit = { social_link_x: 'http://t' };
const apiResponse = {
username: 'testuser',
social_links: [{ platform: 'xTwitter', social_link: 'http://t' }],
language_proficiencies: [],
};
mockHttpClient.patch.mockResolvedValue({ data: apiResponse });
const result = await patchAccount('testuser', commit);
expect(mockHttpClient.patch).toHaveBeenCalledWith(
'http://lms.test/api/user/v1/accounts/testuser',
expect.objectContaining({ social_links: [{ platform: 'xTwitter', social_link: 'http://t' }] }),
expect.any(Object),
);
expect(result.social_link_x).toEqual('http://t');
});
});
describe('getPreferences', () => {
it('returns preferences data', async () => {
mockHttpClient.get.mockResolvedValue({ data: { theme: 'dark' } });
const result = await getPreferences('user');
expect(result.theme).toBe('dark');
});
});
describe('patchPreferences', () => {
it('patches preferences and returns commitValues', async () => {
mockHttpClient.patch.mockResolvedValue({});
const commit = { time_zone: 'UTC' };
const result = await patchPreferences('user', commit);
expect(mockHttpClient.patch).toHaveBeenCalled();
expect(result).toEqual(commit);
});
});
describe('getTimeZones', () => {
it('returns data from API', async () => {
mockHttpClient.get.mockResolvedValue({ data: ['UTC', 'PST'] });
const result = await getTimeZones('PK');
expect(mockHttpClient.get).toHaveBeenCalledWith(
'http://lms.test/user_api/v1/preferences/time_zones/',
{ params: { country_code: 'PK' } },
);
expect(result).toEqual(['UTC', 'PST']);
});
});
describe('getProfileDataManager', () => {
it('returns null if no enterprise manages profile', async () => {
mockHttpClient.get.mockResolvedValue({ data: { results: [] } });
const result = await getProfileDataManager('user', ['learner']);
expect(result).toBeNull();
});
it('returns enterprise name if sync is enabled', async () => {
mockHttpClient.get.mockResolvedValue({ data: { results: [{ enterprise_customer: { name: 'Acme', sync_learner_profile_data: true } }] } });
const result = await getProfileDataManager('user', ['enterprise_learner']);
expect(result).toBe('Acme');
});
});
describe('getVerifiedName', () => {
it('returns verified name data', async () => {
mockHttpClient.get.mockResolvedValue({ data: { verified: true } });
const result = await getVerifiedName();
expect(result.verified).toBe(true);
});
it('returns {} on error', async () => {
mockHttpClient.get.mockRejectedValue(new Error('fail'));
const result = await getVerifiedName();
expect(result).toEqual({});
});
});
describe('getVerifiedNameHistory', () => {
it('returns verified name history data', async () => {
mockHttpClient.get.mockResolvedValue({ data: [{ id: 1 }] });
const result = await getVerifiedNameHistory();
expect(result[0].id).toBe(1);
});
});
describe('postVerifiedName', () => {
it('posts verified name data', async () => {
mockHttpClient.post.mockResolvedValue({});
await postVerifiedName({ first_name: 'A' });
expect(mockHttpClient.post).toHaveBeenCalledWith(
'http://lms.test/api/edx_name_affirmation/v1/verified_name',
{ first_name: 'A' },
{ headers: { Accept: 'application/json' } },
);
});
});
describe('getCountryList', () => {
it('extracts country values from registration API', async () => {
const apiResponse = { fields: [{ name: FIELD_LABELS.COUNTRY, options: [{ value: 'PK' }] }] };
mockHttpClient.get.mockResolvedValue({ data: apiResponse });
const result = await getCountryList();
expect(result).toEqual(['PK']);
});
it('returns [] and logs error on failure', async () => {
mockHttpClient.get.mockRejectedValue(new Error('fail'));
const result = await getCountryList();
expect(result).toEqual([]);
expect(logError).toHaveBeenCalled();
});
});
describe('patchSettings', () => {
it('calls patchAccount and patchPreferences as needed', async () => {
mockHttpClient.patch.mockResolvedValue({
data: {
username: 'user',
social_links: [],
language_proficiencies: [],
},
});
const result = await patchSettings('user', { time_zone: 'UTC', social_link_twitter: 't' });
expect(result.username).toBe('user');
});
});
});

View File

@@ -1,38 +0,0 @@
import camelCase from 'lodash.camelcase';
import snakeCase from 'lodash.snakecase';
export function modifyObjectKeys(object, modify) {
// If the passed in object is not an object, return it.
if (
object === undefined ||
object === null ||
(typeof object !== 'object' && !Array.isArray(object))
) {
return object;
}
if (Array.isArray(object)) {
return object.map(value => modifyObjectKeys(value, modify));
}
// Otherwise, process all its keys.
const result = {};
Object.entries(object).forEach(([key, value]) => {
result[modify(key)] = modifyObjectKeys(value, modify);
});
return result;
}
export function camelCaseObject(object) {
return modifyObjectKeys(object, camelCase);
}
export function snakeCaseObject(object) {
return modifyObjectKeys(object, snakeCase);
}
export function convertKeyNames(object, nameMap) {
const transformer = key => (nameMap[key] === undefined ? key : nameMap[key]);
return modifyObjectKeys(object, transformer);
}

View File

@@ -1,90 +0,0 @@
import {
modifyObjectKeys,
camelCaseObject,
snakeCaseObject,
convertKeyNames,
} from './dataUtils';
describe('modifyObjectKeys', () => {
it('should use the provided modify function to change all keys in and object and its children', () => {
function meowKeys(key) {
return `${key}Meow`;
}
const result = modifyObjectKeys(
{
one: undefined,
two: null,
three: '',
four: 0,
five: NaN,
six: [1, 2, { seven: 'woof' }],
eight: { nine: { ten: 'bark' }, eleven: true },
},
meowKeys,
);
expect(result).toEqual({
oneMeow: undefined,
twoMeow: null,
threeMeow: '',
fourMeow: 0,
fiveMeow: NaN,
sixMeow: [1, 2, { sevenMeow: 'woof' }],
eightMeow: { nineMeow: { tenMeow: 'bark' }, elevenMeow: true },
});
});
});
describe('camelCaseObject', () => {
it('should make everything camelCase', () => {
const result = camelCaseObject({
what_now: 'brown cow',
but_who: { says_you_people: 'okay then', but_how: { will_we_even_know: 'the song is over' } },
'dot.dot.dot': 123,
});
expect(result).toEqual({
whatNow: 'brown cow',
butWho: { saysYouPeople: 'okay then', butHow: { willWeEvenKnow: 'the song is over' } },
dotDotDot: 123,
});
});
});
describe('snakeCaseObject', () => {
it('should make everything snake_case', () => {
const result = snakeCaseObject({
whatNow: 'brown cow',
butWho: { saysYouPeople: 'okay then', butHow: { willWeEvenKnow: 'the song is over' } },
'dot.dot.dot': 123,
});
expect(result).toEqual({
what_now: 'brown cow',
but_who: { says_you_people: 'okay then', but_how: { will_we_even_know: 'the song is over' } },
dot_dot_dot: 123,
});
});
});
describe('convertKeyNames', () => {
it('should replace the specified keynames', () => {
const result = convertKeyNames(
{
one: { two: { three: 'four' } },
five: 'six',
},
{
two: 'blue',
five: 'alive',
seven: 'heaven',
},
);
expect(result).toEqual({
one: { blue: { three: 'four' } },
alive: 'six',
});
});
});

View File

@@ -1,9 +1,3 @@
export {
camelCaseObject,
convertKeyNames,
modifyObjectKeys,
snakeCaseObject,
} from './dataUtils';
export {
AsyncActionType,
getModuleState,

View File

@@ -1,8 +1,7 @@
import { put } from 'redux-saga/effects';
import { logError } from '@edx/frontend-platform/logging';
import { history } from '@edx/frontend-platform';
export default function* handleFailure(error, failureAction = null, failureRedirectPath = null) {
export default function* handleFailure(error, navigate, failureAction = null, failureRedirectPath = null) {
if (error.fieldErrors && failureAction !== null) {
yield put(failureAction({ fieldErrors: error.fieldErrors }));
}
@@ -11,6 +10,6 @@ export default function* handleFailure(error, failureAction = null, failureRedir
yield put(failureAction(error.message));
}
if (failureRedirectPath !== null) {
history.push(failureRedirectPath);
navigate(failureRedirectPath);
}
}

View File

@@ -1,18 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Hyperlink } from '@edx/paragon';
import { Hyperlink } from '@openedx/paragon';
// Messages
import { getConfig } from '@edx/frontend-platform';
import messages from './messages';
// Components
import Alert from '../Alert';
const BeforeProceedingBanner = (props) => {
const { instructionMessageId, intl, supportArticleUrl } = props;
const { instructionMessageId, supportArticleUrl } = props;
const intl = useIntl();
return (
<Alert
@@ -22,13 +23,16 @@ const BeforeProceedingBanner = (props) => {
<FormattedMessage
id="account.settings.delete.account.before.proceeding"
defaultMessage="Before proceeding, please {actionLink}."
description="Error that appears if you are trying to delete your edX account, but something about your account needs attention first. The actionLink will be instructions, such as 'unlink your Facebook account'."
description="Error that appears if you are trying to delete your account, but something about your account needs attention first. The actionLink will be instructions, such as 'unlink your Facebook account'."
values={{
actionLink: (
actionLink: supportArticleUrl ? (
<Hyperlink destination={supportArticleUrl}>
{intl.formatMessage(messages[instructionMessageId])}
</Hyperlink>
) : (
intl.formatMessage(messages[instructionMessageId])
),
siteName: getConfig().SITE_NAME,
}}
/>
</Alert>
@@ -37,8 +41,7 @@ const BeforeProceedingBanner = (props) => {
BeforeProceedingBanner.propTypes = {
instructionMessageId: PropTypes.string.isRequired,
intl: intlShape.isRequired,
supportArticleUrl: PropTypes.string.isRequired,
};
export default injectIntl(BeforeProceedingBanner);
export default BeforeProceedingBanner;

View File

@@ -0,0 +1,45 @@
import renderer from 'react-test-renderer';
import { IntlProvider } from '@edx/frontend-platform/i18n';
jest.mock('react-dom', () => ({
...jest.requireActual('react-dom'),
createPortal: jest.fn(node => node), // Mock portal behavior
}));
import BeforeProceedingBanner from './BeforeProceedingBanner'; // eslint-disable-line import/first
describe('BeforeProceedingBanner', () => {
it('should match the snapshot if SUPPORT_URL_TO_UNLINK_SOCIAL_MEDIA_ACCOUNT does not have a support link', () => {
const props = {
instructionMessageId: 'account.settings.delete.account.please.unlink',
supportArticleUrl: '',
};
const tree = renderer
.create((
<IntlProvider locale="en">
<BeforeProceedingBanner
{...props}
/>
</IntlProvider>
))
.toJSON();
expect(tree).toMatchSnapshot();
});
it('should match the snapshot when SUPPORT_URL_TO_UNLINK_SOCIAL_MEDIA_ACCOUNT has a support link', () => {
const props = {
instructionMessageId: 'account.settings.delete.account.please.unlink',
supportArticleUrl: 'http://test-support.edx',
};
const tree = renderer
.create((
<IntlProvider locale="en">
<BeforeProceedingBanner
{...props}
/>
</IntlProvider>
))
.toJSON();
expect(tree).toMatchSnapshot();
});
});

View File

@@ -1,11 +1,14 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Button, Input, Modal, ValidationFormGroup } from '@edx/paragon';
import {
AlertModal,
Button, Form, ActionRow,
} from '@openedx/paragon';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { faExclamationCircle, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { getConfig } from '@edx/frontend-platform';
import messages from './messages';
import Alert from '../Alert';
import PrintingInstructions from './PrintingInstructions';
@@ -19,6 +22,8 @@ export class ConfirmationModal extends Component {
switch (reason) {
case 'empty-password':
return 'account.settings.delete.account.error.no.password';
case 'invalid-password':
return 'account.settings.delete.account.error.invalid.password';
default:
return 'account.settings.delete.account.error.unable.to.delete';
}
@@ -31,10 +36,9 @@ export class ConfirmationModal extends Component {
return null;
}
const headerMessageId = this.getShortErrorMessageId(errorType);
const detailsMessageId =
reason === 'empty-password'
? null
: 'account.settings.delete.account.error.unable.to.delete.details';
const detailsMessageId = reason === 'empty-password'
? null
: 'account.settings.delete.account.error.unable.to.delete.details';
return (
<Alert
@@ -62,52 +66,71 @@ export class ConfirmationModal extends Component {
const open = ['confirming', 'pending', 'failed'].includes(status);
const passwordFieldId = 'passwordFieldId';
const invalidMessage = messages[this.getShortErrorMessageId(errorType)];
// TODO: We lack a good way of providing custom language for a particular site. This is a hack
// to allow edx.org to fulfill its business requirements.
const deleteAccountModalText2MessageKey = getConfig().SITE_NAME === 'edX'
? 'account.settings.delete.account.modal.text.2.edX'
: 'account.settings.delete.account.modal.text.2';
return (
<Modal
open={open}
<AlertModal
isOpen={open}
title={intl.formatMessage(messages['account.settings.delete.account.modal.header'])}
body={
<div>
{this.renderError()}
<Alert
className="alert-warning mt-n2"
icon={<FontAwesomeIcon className="mr-2" icon={faExclamationTriangle} />}
>
<h6>
{intl.formatMessage(messages['account.settings.delete.account.modal.text.1'])}
</h6>
<p>{intl.formatMessage(messages['account.settings.delete.account.modal.text.2'])}</p>
<p>
<PrintingInstructions />
</p>
</Alert>
<ValidationFormGroup
for={passwordFieldId}
invalid={errorType !== null}
invalidMessage={intl.formatMessage(invalidMessage)}
>
<label className="d-block" htmlFor={passwordFieldId}>
{intl.formatMessage(messages['account.settings.delete.account.modal.enter.password'])}
</label>
<Input
name="password"
id={passwordFieldId}
type="password"
value={password}
onChange={onChange}
/>
</ValidationFormGroup>
</div>
}
buttons={[
<Button className="btn-danger" onClick={onSubmit}>
{intl.formatMessage(messages['account.settings.delete.account.modal.confirm.delete'])}
</Button>,
]}
closeText={intl.formatMessage(messages['account.settings.delete.account.modal.confirm.cancel'])}
renderHeaderCloseButton={false}
onClose={onCancel}
/>
isOverflowVisible
footerNode={(
<ActionRow>
<Button variant="link" onClick={onCancel}>{intl.formatMessage(messages['account.settings.delete.account.modal.confirm.cancel'])}</Button>
<Button variant="danger" onClick={onSubmit}>{intl.formatMessage(messages['account.settings.delete.account.modal.confirm.delete'])}</Button>
</ActionRow>
)}
>
<div className="p-3">
{this.renderError()}
<Alert
className="alert-warning mt-n2"
icon={<FontAwesomeIcon className="mr-2" icon={faExclamationTriangle} />}
>
<h6>
{intl.formatMessage(
messages['account.settings.delete.account.modal.text.1'],
{ siteName: getConfig().SITE_NAME },
)}
</h6>
<p>
{intl.formatMessage(
messages[deleteAccountModalText2MessageKey],
{ siteName: getConfig().SITE_NAME },
)}
</p>
<p>
<PrintingInstructions />
</p>
</Alert>
<Form.Group
for={passwordFieldId}
isInvalid={errorType !== null}
>
<Form.Label className="d-block" htmlFor={passwordFieldId}>
{intl.formatMessage(messages['account.settings.delete.account.modal.enter.password'])}
</Form.Label>
<Form.Control
name="password"
id={passwordFieldId}
type="password"
value={password}
onChange={onChange}
/>
{errorType !== null && (
<Form.Control.Feedback type="invalid" feedback-for={passwordFieldId}>
{intl.formatMessage(invalidMessage)}
</Form.Control.Feedback>
)}
</Form.Group>
</div>
</AlertModal>
);
}
}

View File

@@ -1,10 +1,12 @@
import React from 'react';
import ReactDOM from 'react-dom';
import renderer from 'react-test-renderer';
import { IntlProvider, injectIntl } from '@edx/frontend-platform/i18n';
// Modal creates a portal. Overriding ReactDOM.createPortal allows portals to be tested in jest.
ReactDOM.createPortal = node => node;
// Modal creates a portal. Overriding createPortal allows portals to be tested in jest.
jest.mock('react-dom', () => ({
...jest.requireActual('react-dom'),
createPortal: jest.fn(node => node), // Mock portal behavior
}));
import { ConfirmationModal } from './ConfirmationModal'; // eslint-disable-line import/first

View File

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { getConfig } from '@edx/frontend-platform';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Button, Hyperlink } from '@edx/paragon';
import { Button, Hyperlink } from '@openedx/paragon';
// Actions
import {
@@ -24,9 +24,13 @@ import ConnectedSuccessModal from './SuccessModal';
import BeforeProceedingBanner from './BeforeProceedingBanner';
export class DeleteAccount extends React.Component {
state = {
password: '',
};
constructor(props) {
super(props);
this.state = {
password: '',
};
}
handleSubmit = () => {
if (this.state.password === '') {
@@ -55,60 +59,91 @@ export class DeleteAccount extends React.Component {
hasLinkedTPA, isVerifiedAccount, status, errorType, intl,
} = this.props;
const canDelete = isVerifiedAccount && !hasLinkedTPA;
const supportArticleUrl = process.env.SUPPORT_URL_TO_UNLINK_SOCIAL_MEDIA_ACCOUNT;
// TODO: We lack a good way of providing custom language for a particular site. This is a hack
// to allow edx.org to fulfill its business requirements.
const deleteAccountText2MessageKey = getConfig().SITE_NAME === 'edX'
? 'account.settings.delete.account.text.2.edX'
: 'account.settings.delete.account.text.2';
const optInInstructionMessageId = getConfig().MARKETING_EMAILS_OPT_IN
? 'account.settings.delete.account.please.confirm'
: 'account.settings.delete.account.please.activate';
return (
<div>
<h2 className="section-heading">
<h2 className="section-heading h4 mb-3">
{intl.formatMessage(messages['account.settings.delete.account.header'])}
</h2>
<p>{intl.formatMessage(messages['account.settings.delete.account.subheader'])}</p>
<p>{intl.formatMessage(messages['account.settings.delete.account.text.1'])}</p>
<p>{intl.formatMessage(messages['account.settings.delete.account.text.2'])}</p>
<p>
<PrintingInstructions />
</p>
<p className="text-danger h6">
{intl.formatMessage(messages['account.settings.delete.account.text.warning'])}
</p>
<p>
<Hyperlink destination="https://support.edx.org/hc/en-us/sections/115004139268-Manage-Your-Account-Settings">
{intl.formatMessage(messages['account.settings.delete.account.text.change.instead'])}
</Hyperlink>
</p>
<p>
<Button
className="btn-outline-danger"
onClick={canDelete ? this.props.deleteAccountConfirmation : null}
disabled={!canDelete}
>
{intl.formatMessage(messages['account.settings.delete.account.button'])}
</Button>
</p>
{
this.props.canDeleteAccount ? (
<>
<p>{intl.formatMessage(messages['account.settings.delete.account.subheader'])}</p>
<p>
{intl.formatMessage(
messages['account.settings.delete.account.text.1'],
{ siteName: getConfig().SITE_NAME },
)}
</p>
<p>
{intl.formatMessage(
messages[deleteAccountText2MessageKey],
{ siteName: getConfig().SITE_NAME },
)}
</p>
<p>
<PrintingInstructions />
</p>
<p className="text-danger h6">
{intl.formatMessage(
messages['account.settings.delete.account.text.warning'],
{ siteName: getConfig().SITE_NAME },
)}
</p>
<p>
<Hyperlink destination="https://help.edx.org/edxlearner/s/topic/0TOQq0000001UdZOAU/account-basics">
{intl.formatMessage(messages['account.settings.delete.account.text.change.instead'])}
</Hyperlink>
</p>
<p>
<Button
variant="outline-danger"
onClick={canDelete ? this.props.deleteAccountConfirmation : null}
disabled={!canDelete}
>
{intl.formatMessage(messages['account.settings.delete.account.button'])}
</Button>
</p>
{isVerifiedAccount ? null : (
<BeforeProceedingBanner
instructionMessageId={optInInstructionMessageId}
supportArticleUrl="https://support.edx.org/hc/en-us/articles/115000940568-How-do-I-confirm-my-email"
/>
)}
{hasLinkedTPA ? (
<BeforeProceedingBanner
instructionMessageId="account.settings.delete.account.please.unlink"
supportArticleUrl={supportArticleUrl}
/>
) : null}
{isVerifiedAccount ? null : (
<BeforeProceedingBanner
instructionMessageId="account.settings.delete.account.please.activate"
supportArticleUrl="https://support.edx.org/hc/en-us/articles/115000940568-How-do-I-activate-my-account-"
/>
)}
<ConnectedConfirmationModal
status={status}
errorType={errorType}
onSubmit={this.handleSubmit}
onCancel={this.handleCancel}
onChange={this.handlePasswordChange}
password={this.state.password}
/>
{hasLinkedTPA ? (
<BeforeProceedingBanner
instructionMessageId="account.settings.delete.account.please.unlink"
supportArticleUrl="https://support.edx.org/hc/en-us/articles/207206067"
/>
) : null}
<ConnectedSuccessModal status={status} onClose={this.handleFinalClose} />
</>
) : (
<p>{intl.formatMessage(messages['account.settings.cannot.delete.account.text'])}</p>
)
}
<ConnectedConfirmationModal
status={status}
errorType={errorType}
onSubmit={this.handleSubmit}
onCancel={this.handleCancel}
onChange={this.handlePasswordChange}
password={this.state.password}
/>
<ConnectedSuccessModal status={status} onClose={this.handleFinalClose} />
</div>
);
}
@@ -124,6 +159,7 @@ DeleteAccount.propTypes = {
errorType: PropTypes.oneOf(['empty-password', 'server']),
hasLinkedTPA: PropTypes.bool,
isVerifiedAccount: PropTypes.bool,
canDeleteAccount: PropTypes.bool,
intl: intlShape.isRequired,
};
@@ -132,6 +168,7 @@ DeleteAccount.defaultProps = {
isVerifiedAccount: true,
status: null,
errorType: null,
canDeleteAccount: true,
};
// Assume we're part of the accountSettings state.

View File

@@ -1,10 +1,15 @@
/* eslint-disable react/jsx-no-useless-fragment */
import React from 'react';
import renderer from 'react-test-renderer';
import { IntlProvider, injectIntl } from '@edx/frontend-platform/i18n';
// Testing the modals separately, they just clutter up the snapshots if included here.
jest.mock('./ConfirmationModal');
jest.mock('./SuccessModal');
jest.mock('./ConfirmationModal', () => function ConfirmationModalMock() {
return <></>;
});
jest.mock('./SuccessModal', () => function SuccessModalMock() {
return <></>;
});
import { DeleteAccount } from './DeleteAccount'; // eslint-disable-line import/first
@@ -37,6 +42,7 @@ describe('DeleteAccount', () => {
</IntlProvider>
))
.toJSON();
expect(tree).toMatchSnapshot();
});

View File

@@ -1,30 +1,43 @@
import React from 'react';
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Hyperlink } from '@edx/paragon';
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
import { Hyperlink } from '@openedx/paragon';
import { getConfig } from '@edx/frontend-platform';
import messages from './messages';
const PrintingInstructions = (props) => {
const PrintingInstructions = () => {
const intl = useIntl();
const actionLink = (
<Hyperlink
destination="https://edx.readthedocs.io/projects/edx-guide-for-students/en/latest/SFD_certificates.html#printing-a-certificate"
// TODO: What would a generic version of this link look like? Should
// CERTIFICATE_SHARING_HELP_URL really be a configuration variable? In the meantime,
// We've removed the link from the default message.
destination="https://help.edx.org/edxlearner/s/topic/0TOQq0000001UVVOA2/certificates"
>
{props.intl.formatMessage(messages['account.settings.delete.account.text.3.link'])}
{intl.formatMessage(messages['account.settings.delete.account.text.3.link'])}
</Hyperlink>
);
// TODO: We lack a good way of providing custom language for a particular site. This is a hack
// to allow edx.org to mention MicroMasters certificates to fulfill its business requirements.
if (getConfig().SITE_NAME === 'edX') {
return (
<FormattedMessage
id="account.settings.delete.account.text.3.edX"
defaultMessage="You may also lose access to verified certificates and other program credentials like MicroMasters certificates. You can make a copy of these for your records before proceeding with deletion. {actionLink}."
description="A message in the user account deletion area warning users that deleting their account will prevent them from accessing their certificates. 'actionLink' is a HTML link with a full sentence that describes how to print a certificate."
values={{ actionLink }}
/>
);
}
return (
<FormattedMessage
id="account.settings.delete.account.text.3"
defaultMessage="You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion, {actionLink}."
description="A message in the user account deletion area"
defaultMessage="You may also lose access to verified certificates and other program credentials. You can make a copy of these for your records before proceeding with deletion."
description="A message in the user account deletion area warning users that deleting their account will prevent them from accessing their certificates."
values={{ actionLink }}
/>
);
};
PrintingInstructions.propTypes = {
intl: intlShape.isRequired,
};
export default injectIntl(PrintingInstructions);
export default PrintingInstructions;

View File

@@ -1,33 +1,36 @@
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Modal } from '@edx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';
import { ModalLayer, ModalCloseButton } from '@openedx/paragon';
import messages from './messages';
export const SuccessModal = (props) => {
const { status, intl, onClose } = props;
const intl = useIntl();
const { status, onClose } = props;
return (
<Modal
open={status === 'deleted'}
title={intl.formatMessage(messages['account.settings.delete.account.modal.after.header'])}
body={
<div>
<ModalLayer isOpen={status === 'deleted'} onClose={onClose}>
<div className="mw-sm p-5 bg-white mx-auto my-3">
<h3>
{intl.formatMessage(messages['account.settings.delete.account.modal.after.header'])}
</h3>
<div className="p-3">
<p className="h6">
{intl.formatMessage(messages['account.settings.delete.account.modal.after.text'])}
</p>
</div>
}
closeText={intl.formatMessage(messages['account.settings.delete.account.modal.after.button'])}
renderHeaderCloseButton={false}
onClose={onClose}
/>
<p>
<ModalCloseButton className="float-right" variant="link">{intl.formatMessage(messages['account.settings.delete.account.modal.after.button'])}</ModalCloseButton>
</p>
</div>
</ModalLayer>
);
};
SuccessModal.propTypes = {
status: PropTypes.oneOf(['confirming', 'pending', 'deleted', 'failed']),
intl: intlShape.isRequired,
onClose: PropTypes.func.isRequired,
};
@@ -35,4 +38,4 @@ SuccessModal.defaultProps = {
status: null,
};
export default injectIntl(SuccessModal);
export default SuccessModal;

View File

@@ -1,14 +1,13 @@
import React from 'react';
import ReactDOM from 'react-dom';
import renderer from 'react-test-renderer';
import { IntlProvider, injectIntl } from '@edx/frontend-platform/i18n';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { waitFor } from '@testing-library/react';
import { SuccessModal } from './SuccessModal';
// Modal creates a portal. Overriding ReactDOM.createPortal allows portals to be tested in jest.
ReactDOM.createPortal = node => node;
import { SuccessModal } from './SuccessModal'; // eslint-disable-line import/first
const IntlSuccessModal = injectIntl(SuccessModal);
// Modal creates a portal. Overriding createPortal allows portals to be tested in jest.
jest.mock('react-dom', () => ({
...jest.requireActual('react-dom'),
createPortal: jest.fn(node => node), // Mock portal behavior
}));
describe('SuccessModal', () => {
let props = {};
@@ -20,39 +19,40 @@ describe('SuccessModal', () => {
};
});
it('should match default closed success modal snapshot', () => {
let tree = renderer.create((
<IntlProvider locale="en"><IntlSuccessModal {...props} /></IntlProvider>))
.toJSON();
expect(tree).toMatchSnapshot();
tree = renderer.create((
<IntlProvider locale="en"><IntlSuccessModal {...props} status="confirming" /></IntlProvider>))
.toJSON();
expect(tree).toMatchSnapshot();
tree = renderer.create((
<IntlProvider locale="en"><IntlSuccessModal {...props} status="pending" /></IntlProvider>))
.toJSON();
expect(tree).toMatchSnapshot();
tree = renderer.create((
<IntlProvider locale="en"><IntlSuccessModal {...props} status="failed" /></IntlProvider>))
.toJSON();
expect(tree).toMatchSnapshot();
it('should match default closed success modal snapshot', async () => {
await waitFor(() => {
const tree = renderer.create((
<IntlProvider locale="en"><SuccessModal {...props} /></IntlProvider>)).toJSON();
expect(tree).toMatchSnapshot();
});
await waitFor(() => {
const tree = renderer.create((
<IntlProvider locale="en"><SuccessModal {...props} status="confirming" /></IntlProvider>)).toJSON();
expect(tree).toMatchSnapshot();
});
await waitFor(() => {
const tree = renderer.create((
<IntlProvider locale="en"><SuccessModal {...props} status="pending" /></IntlProvider>)).toJSON();
expect(tree).toMatchSnapshot();
});
await waitFor(() => {
const tree = renderer.create((
<IntlProvider locale="en"><SuccessModal {...props} status="failed" /></IntlProvider>)).toJSON();
expect(tree).toMatchSnapshot();
});
});
it('should match open success modal snapshot', () => {
const tree = renderer
.create((
it('should match open success modal snapshot', async () => {
await waitFor(() => {
const tree = renderer.create(
<IntlProvider locale="en">
<IntlSuccessModal
<SuccessModal
{...props}
status="deleted" // This will cause 'modal-backdrop' and 'show' to appear on the modal as CSS classes.
status="deleted"
/>
</IntlProvider>
))
.toJSON();
expect(tree).toMatchSnapshot();
</IntlProvider>,
).toJSON();
expect(tree).toMatchSnapshot();
});
});
});

View File

@@ -0,0 +1,67 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`BeforeProceedingBanner should match the snapshot if SUPPORT_URL_TO_UNLINK_SOCIAL_MEDIA_ACCOUNT does not have a support link 1`] = `
<div
className="alert d-flex align-items-start alert-warning mt-n2"
>
<div>
<svg
aria-hidden="true"
className="svg-inline--fa fa-triangle-exclamation mr-2"
data-icon="triangle-exclamation"
data-prefix="fas"
focusable="false"
role="img"
style={{}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"
fill="currentColor"
style={{}}
/>
</svg>
</div>
<div>
Before proceeding, please unlink all social media accounts.
</div>
</div>
`;
exports[`BeforeProceedingBanner should match the snapshot when SUPPORT_URL_TO_UNLINK_SOCIAL_MEDIA_ACCOUNT has a support link 1`] = `
<div
className="alert d-flex align-items-start alert-warning mt-n2"
>
<div>
<svg
aria-hidden="true"
className="svg-inline--fa fa-triangle-exclamation mr-2"
data-icon="triangle-exclamation"
data-prefix="fas"
focusable="false"
role="img"
style={{}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"
fill="currentColor"
style={{}}
/>
</svg>
</div>
<div>
Before proceeding, please
<a
className="pgn__hyperlink default-link standalone-link"
href="http://test-support.edx"
target="_self"
>
unlink all social media accounts
</a>
.
</div>
</div>
`;

View File

@@ -1,439 +1,403 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`ConfirmationModal should match default closed confirmation modal snapshot 1`] = `
<div>
<div
className="fade"
role="presentation"
/>
<div
className="modal js-close-modal-on-click fade"
onMouseDown={[Function]}
role="presentation"
>
<div
aria-labelledby="id2"
aria-modal={true}
className=""
role="dialog"
tabIndex="-1"
>
<div
className="modal-content"
>
<div
className="modal-header"
>
<h2
className="modal-title"
id="id2"
>
Are you sure?
</h2>
</div>
<div
className="modal-body"
>
<div>
<div
className="alert d-flex align-items-start alert-warning mt-n2"
>
<div>
<svg
aria-hidden="true"
className="svg-inline--fa fa-exclamation-triangle fa-w-18 mr-2"
data-icon="exclamation-triangle"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 576 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
fill="currentColor"
style={Object {}}
/>
</svg>
</div>
<div>
<h6>
You have selected "Delete My Account". Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.
</h6>
<p>
If you proceed, you will be unable to use this account to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employer's or university's system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.
</p>
<p>
<span>
You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion,
<a
href="https://edx.readthedocs.io/projects/edx-guide-for-students/en/latest/SFD_certificates.html#printing-a-certificate"
onClick={[Function]}
target="_self"
>
follow the instructions for printing or downloading a certificate
</a>
.
</span>
</p>
</div>
</div>
<div
className="form-group"
>
<label
className="d-block"
htmlFor="passwordFieldId"
>
If you still wish to continue and delete your account, please enter your account password:
</label>
<input
aria-describedby=""
className="form-control"
id="passwordFieldId"
name="password"
onChange={[MockFunction]}
type="password"
value="fluffy bunnies"
/>
<strong
className="invalid-feedback"
id="passwordFieldId-invalid-feedback"
>
Unable to delete account
</strong>
</div>
</div>
</div>
<div
className="modal-footer"
>
<button
className="btn btn-danger"
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Yes, Delete
</button>
<button
className="btn js-close-modal-on-click btn-secondary"
id="paragonCloseModalButton1"
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Cancel
</button>
</div>
</div>
</div>
</div>
</div>
`;
exports[`ConfirmationModal should match default closed confirmation modal snapshot 1`] = `null`;
exports[`ConfirmationModal should match empty password confirmation modal snapshot 1`] = `
<div>
[
<div
className="modal-backdrop show"
role="presentation"
/>
data-focus-guard={true}
style={
{
"height": "0px",
"left": "1px",
"overflow": "hidden",
"padding": 0,
"position": "fixed",
"top": "1px",
"width": "1px",
}
}
tabIndex={0}
/>,
<div
className="modal js-close-modal-on-click show d-block"
className="pgn__modal-layer"
data-focus-lock-disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
role="presentation"
onScrollCapture={[Function]}
onTouchMoveCapture={[Function]}
onTouchStart={[Function]}
onWheelCapture={[Function]}
>
<div
aria-labelledby="id6"
aria-modal={true}
className="modal-dialog"
role="dialog"
tabIndex="-1"
className="pgn__modal-content-container"
>
<div
className="modal-content"
className="pgn__modal-backdrop"
data-testid="modal-backdrop"
onClick={[MockFunction]}
onKeyDown={[MockFunction]}
role="presentation"
/>
<div
aria-label="Are you sure?"
className="pgn__modal pgn__modal-md pgn__modal-default pgn__modal-visible-overflow pgn__alert-modal"
role="dialog"
>
<div
className="modal-header"
className="pgn__modal-header"
>
<h2
className="modal-title"
id="id6"
className="pgn__modal-title"
>
Are you sure?
</h2>
</div>
<div
className="modal-body"
className="pgn__modal-body pgn__modal-body-scroll-top pgn__modal-body-scroll-bottom"
>
<div>
<div />
<div
className="pgn__modal-body-content"
>
<div
className="alert d-flex align-items-start alert-danger mt-n2"
className="p-3"
>
<div>
<svg
aria-hidden="true"
className="svg-inline--fa fa-exclamation-circle fa-w-16 mr-2"
data-icon="exclamation-circle"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
<div
className="alert d-flex align-items-start alert-danger mt-n2"
>
<div>
<svg
aria-hidden="true"
className="svg-inline--fa fa-circle-exclamation mr-2"
data-icon="circle-exclamation"
data-prefix="fas"
focusable="false"
role="img"
style={{}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-384c13.3 0 24 10.7 24 24l0 112c0 13.3-10.7 24-24 24s-24-10.7-24-24l0-112c0-13.3 10.7-24 24-24zM224 352a32 32 0 1 1 64 0 32 32 0 1 1 -64 0z"
fill="currentColor"
style={{}}
/>
</svg>
</div>
<div>
<h6>
A password is required
</h6>
<p
className="text-danger"
>
Sorry, there was an error trying to process your request. Please try again later.
</p>
</div>
</div>
<div
className="alert d-flex align-items-start alert-warning mt-n2"
>
<div>
<svg
aria-hidden="true"
className="svg-inline--fa fa-triangle-exclamation mr-2"
data-icon="triangle-exclamation"
data-prefix="fas"
focusable="false"
role="img"
style={{}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"
fill="currentColor"
style={{}}
/>
</svg>
</div>
<div>
<h6>
You have selected "Delete My Account". Deletion of your account and personal data is permanent and cannot be undone. localhost will not be able to recover your account or the data that is deleted.
</h6>
<p>
If you proceed, you will be unable to use this account to take courses on localhost.
</p>
<p>
You may also lose access to verified certificates and other program credentials. You can make a copy of these for your records before proceeding with deletion.
</p>
</div>
</div>
<div
className="pgn__form-group"
for="passwordFieldId"
>
<label
className="pgn__form-label d-block"
htmlFor="form-field3"
>
<path
d="M504 256c0 136.997-111.043 248-248 248S8 392.997 8 256C8 119.083 119.043 8 256 8s248 111.083 248 248zm-248 50c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
fill="currentColor"
style={Object {}}
If you still wish to continue and delete your account, please enter your account password:
</label>
<div
className="pgn__form-control-decorator-group"
>
<input
aria-describedby="form-field3-5"
className="has-value form-control is-invalid"
id="form-field3"
name="password"
onBlur={[Function]}
onChange={[Function]}
type="password"
value="fluffy bunnies"
/>
</svg>
</div>
<div>
<h6>
A password is required
</h6>
<p
className="text-danger"
</div>
<div
className="pgn__form-control-description pgn__form-text pgn__form-text-invalid"
feedback-for="passwordFieldId"
id="form-field3-5"
>
Sorry, there was an error trying to process your request. Please try again later.
</p>
</div>
</div>
<div
className="alert d-flex align-items-start alert-warning mt-n2"
>
<div>
<svg
aria-hidden="true"
className="svg-inline--fa fa-exclamation-triangle fa-w-18 mr-2"
data-icon="exclamation-triangle"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 576 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
fill="currentColor"
style={Object {}}
/>
</svg>
</div>
<div>
<h6>
You have selected "Delete My Account". Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.
</h6>
<p>
If you proceed, you will be unable to use this account to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employer's or university's system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.
</p>
<p>
<span>
You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion,
<a
href="https://edx.readthedocs.io/projects/edx-guide-for-students/en/latest/SFD_certificates.html#printing-a-certificate"
onClick={[Function]}
target="_self"
<span
className="pgn__icon"
>
<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"
>
follow the instructions for printing or downloading a certificate
</a>
.
<path
d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"
fill="currentColor"
/>
</svg>
</span>
</p>
<div>
A password is required
</div>
</div>
</div>
</div>
<div
className="form-group"
>
<label
className="d-block"
htmlFor="passwordFieldId"
>
If you still wish to continue and delete your account, please enter your account password:
</label>
<input
aria-describedby="passwordFieldId-invalid-feedback"
className="form-control is-invalid"
id="passwordFieldId"
name="password"
onChange={[MockFunction]}
type="password"
value="fluffy bunnies"
/>
<strong
className="invalid-feedback"
id="passwordFieldId-invalid-feedback"
>
A password is required
</strong>
</div>
</div>
<div />
</div>
<div
className="modal-footer"
className="pgn__modal-footer"
>
<button
className="btn btn-danger"
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
<div
className="pgn__action-row"
>
Yes, Delete
</button>
<button
className="btn js-close-modal-on-click btn-secondary"
id="paragonCloseModalButton5"
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Cancel
</button>
<button
className="btn btn-link"
disabled={false}
onClick={[MockFunction]}
type="button"
>
Cancel
</button>
<button
className="btn btn-danger"
disabled={false}
onClick={[MockFunction]}
type="button"
>
Yes, Delete
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>,
<div
data-focus-guard={true}
style={
{
"height": "0px",
"left": "1px",
"overflow": "hidden",
"padding": 0,
"position": "fixed",
"top": "1px",
"width": "1px",
}
}
tabIndex={0}
/>,
]
`;
exports[`ConfirmationModal should match open confirmation modal snapshot 1`] = `
<div>
[
<div
className="modal-backdrop show"
role="presentation"
/>
data-focus-guard={true}
style={
{
"height": "0px",
"left": "1px",
"overflow": "hidden",
"padding": 0,
"position": "fixed",
"top": "1px",
"width": "1px",
}
}
tabIndex={0}
/>,
<div
className="modal js-close-modal-on-click show d-block"
className="pgn__modal-layer"
data-focus-lock-disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
role="presentation"
onScrollCapture={[Function]}
onTouchMoveCapture={[Function]}
onTouchStart={[Function]}
onWheelCapture={[Function]}
>
<div
aria-labelledby="id4"
aria-modal={true}
className="modal-dialog"
role="dialog"
tabIndex="-1"
className="pgn__modal-content-container"
>
<div
className="modal-content"
className="pgn__modal-backdrop"
data-testid="modal-backdrop"
onClick={[MockFunction]}
onKeyDown={[MockFunction]}
role="presentation"
/>
<div
aria-label="Are you sure?"
className="pgn__modal pgn__modal-md pgn__modal-default pgn__modal-visible-overflow pgn__alert-modal"
role="dialog"
>
<div
className="modal-header"
className="pgn__modal-header"
>
<h2
className="modal-title"
id="id4"
className="pgn__modal-title"
>
Are you sure?
</h2>
</div>
<div
className="modal-body"
className="pgn__modal-body pgn__modal-body-scroll-top pgn__modal-body-scroll-bottom"
>
<div>
<div />
<div
className="pgn__modal-body-content"
>
<div
className="alert d-flex align-items-start alert-warning mt-n2"
className="p-3"
>
<div>
<svg
aria-hidden="true"
className="svg-inline--fa fa-exclamation-triangle fa-w-18 mr-2"
data-icon="exclamation-triangle"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 576 512"
xmlns="http://www.w3.org/2000/svg"
<div
className="alert d-flex align-items-start alert-warning mt-n2"
>
<div>
<svg
aria-hidden="true"
className="svg-inline--fa fa-triangle-exclamation mr-2"
data-icon="triangle-exclamation"
data-prefix="fas"
focusable="false"
role="img"
style={{}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"
fill="currentColor"
style={{}}
/>
</svg>
</div>
<div>
<h6>
You have selected "Delete My Account". Deletion of your account and personal data is permanent and cannot be undone. localhost will not be able to recover your account or the data that is deleted.
</h6>
<p>
If you proceed, you will be unable to use this account to take courses on localhost.
</p>
<p>
You may also lose access to verified certificates and other program credentials. You can make a copy of these for your records before proceeding with deletion.
</p>
</div>
</div>
<div
className="pgn__form-group"
for="passwordFieldId"
>
<label
className="pgn__form-label d-block"
htmlFor="form-field1"
>
<path
d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
fill="currentColor"
style={Object {}}
If you still wish to continue and delete your account, please enter your account password:
</label>
<div
className="pgn__form-control-decorator-group"
>
<input
className="has-value form-control"
id="form-field1"
name="password"
onBlur={[Function]}
onChange={[Function]}
type="password"
value="fluffy bunnies"
/>
</svg>
</div>
</div>
<div>
<h6>
You have selected "Delete My Account". Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.
</h6>
<p>
If you proceed, you will be unable to use this account to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employer's or university's system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.
</p>
<p>
<span>
You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion,
<a
href="https://edx.readthedocs.io/projects/edx-guide-for-students/en/latest/SFD_certificates.html#printing-a-certificate"
onClick={[Function]}
target="_self"
>
follow the instructions for printing or downloading a certificate
</a>
.
</span>
</p>
</div>
</div>
<div
className="form-group"
>
<label
className="d-block"
htmlFor="passwordFieldId"
>
If you still wish to continue and delete your account, please enter your account password:
</label>
<input
aria-describedby=""
className="form-control"
id="passwordFieldId"
name="password"
onChange={[MockFunction]}
type="password"
value="fluffy bunnies"
/>
<strong
className="invalid-feedback"
id="passwordFieldId-invalid-feedback"
>
Unable to delete account
</strong>
</div>
</div>
<div />
</div>
<div
className="modal-footer"
className="pgn__modal-footer"
>
<button
className="btn btn-danger"
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
<div
className="pgn__action-row"
>
Yes, Delete
</button>
<button
className="btn js-close-modal-on-click btn-secondary"
id="paragonCloseModalButton3"
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Cancel
</button>
<button
className="btn btn-link"
disabled={false}
onClick={[MockFunction]}
type="button"
>
Cancel
</button>
<button
className="btn btn-danger"
disabled={false}
onClick={[MockFunction]}
type="button"
>
Yes, Delete
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>,
<div
data-focus-guard={true}
style={
{
"height": "0px",
"left": "1px",
"overflow": "hidden",
"padding": 0,
"position": "fixed",
"top": "1px",
"width": "1px",
}
}
tabIndex={0}
/>,
]
`;

View File

@@ -3,7 +3,7 @@
exports[`DeleteAccount should match default section snapshot 1`] = `
<div>
<h2
className="section-heading"
className="section-heading h4 mb-3"
>
Delete My Account
</h2>
@@ -11,33 +11,23 @@ exports[`DeleteAccount should match default section snapshot 1`] = `
We're sorry to see you go!
</p>
<p>
Please note: Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.
Please note: Deletion of your account and personal data is permanent and cannot be undone. localhost will not be able to recover your account or the data that is deleted.
</p>
<p>
Once your account is deleted, you cannot use it to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employers or universitys system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.
Once your account is deleted, you cannot use it to take courses on localhost.
</p>
<p>
<span>
You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion,
<a
href="https://edx.readthedocs.io/projects/edx-guide-for-students/en/latest/SFD_certificates.html#printing-a-certificate"
onClick={[Function]}
target="_self"
>
follow the instructions for printing or downloading a certificate
</a>
.
</span>
You may also lose access to verified certificates and other program credentials. You can make a copy of these for your records before proceeding with deletion.
</p>
<p
className="text-danger h6"
>
Warning: Account deletion is permanent. Please read the above carefully before proceeding. This is an irreversible action, and you will no longer be able to use the same email on edX.
Warning: Account deletion is permanent. Please read the above carefully before proceeding. This is an irreversible action, and you will no longer be able to use the same email on localhost.
</p>
<p>
<a
href="https://support.edx.org/hc/en-us/sections/115004139268-Manage-Your-Account-Settings"
onClick={[Function]}
className="pgn__hyperlink default-link standalone-link"
href="https://help.edx.org/edxlearner/s/topic/0TOQq0000001UdZOAU/account-basics"
target="_self"
>
Want to change your email, name, or password instead?
@@ -47,9 +37,7 @@ exports[`DeleteAccount should match default section snapshot 1`] = `
<button
className="btn btn-outline-danger"
disabled={false}
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
onClick={[MockFunction]}
type="button"
>
Delete My Account
@@ -61,7 +49,7 @@ exports[`DeleteAccount should match default section snapshot 1`] = `
exports[`DeleteAccount should match unverified account section snapshot 1`] = `
<div>
<h2
className="section-heading"
className="section-heading h4 mb-3"
>
Delete My Account
</h2>
@@ -69,33 +57,23 @@ exports[`DeleteAccount should match unverified account section snapshot 1`] = `
We're sorry to see you go!
</p>
<p>
Please note: Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.
Please note: Deletion of your account and personal data is permanent and cannot be undone. localhost will not be able to recover your account or the data that is deleted.
</p>
<p>
Once your account is deleted, you cannot use it to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employers or universitys system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.
Once your account is deleted, you cannot use it to take courses on localhost.
</p>
<p>
<span>
You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion,
<a
href="https://edx.readthedocs.io/projects/edx-guide-for-students/en/latest/SFD_certificates.html#printing-a-certificate"
onClick={[Function]}
target="_self"
>
follow the instructions for printing or downloading a certificate
</a>
.
</span>
You may also lose access to verified certificates and other program credentials. You can make a copy of these for your records before proceeding with deletion.
</p>
<p
className="text-danger h6"
>
Warning: Account deletion is permanent. Please read the above carefully before proceeding. This is an irreversible action, and you will no longer be able to use the same email on edX.
Warning: Account deletion is permanent. Please read the above carefully before proceeding. This is an irreversible action, and you will no longer be able to use the same email on localhost.
</p>
<p>
<a
href="https://support.edx.org/hc/en-us/sections/115004139268-Manage-Your-Account-Settings"
onClick={[Function]}
className="pgn__hyperlink default-link standalone-link"
href="https://help.edx.org/edxlearner/s/topic/0TOQq0000001UdZOAU/account-basics"
target="_self"
>
Want to change your email, name, or password instead?
@@ -105,9 +83,7 @@ exports[`DeleteAccount should match unverified account section snapshot 1`] = `
<button
className="btn btn-outline-danger"
disabled={true}
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
onClick={null}
type="button"
>
Delete My Account
@@ -119,34 +95,32 @@ exports[`DeleteAccount should match unverified account section snapshot 1`] = `
<div>
<svg
aria-hidden="true"
className="svg-inline--fa fa-exclamation-triangle fa-w-18 mr-2"
data-icon="exclamation-triangle"
className="svg-inline--fa fa-triangle-exclamation mr-2"
data-icon="triangle-exclamation"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 576 512"
style={{}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"
fill="currentColor"
style={Object {}}
style={{}}
/>
</svg>
</div>
<div>
<span>
Before proceeding, please
<a
href="https://support.edx.org/hc/en-us/articles/115000940568-How-do-I-activate-my-account-"
onClick={[Function]}
target="_self"
>
activate your account
</a>
.
</span>
Before proceeding, please
<a
className="pgn__hyperlink default-link standalone-link"
href="https://support.edx.org/hc/en-us/articles/115000940568-How-do-I-confirm-my-email"
target="_self"
>
activate your account
</a>
.
</div>
</div>
</div>
@@ -155,7 +129,7 @@ exports[`DeleteAccount should match unverified account section snapshot 1`] = `
exports[`DeleteAccount should match unverified account section snapshot 2`] = `
<div>
<h2
className="section-heading"
className="section-heading h4 mb-3"
>
Delete My Account
</h2>
@@ -163,33 +137,23 @@ exports[`DeleteAccount should match unverified account section snapshot 2`] = `
We're sorry to see you go!
</p>
<p>
Please note: Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.
Please note: Deletion of your account and personal data is permanent and cannot be undone. localhost will not be able to recover your account or the data that is deleted.
</p>
<p>
Once your account is deleted, you cannot use it to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employers or universitys system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.
Once your account is deleted, you cannot use it to take courses on localhost.
</p>
<p>
<span>
You may also lose access to verified certificates and other program credentials like MicroMasters certificates. If you want to make a copy of these for your records before proceeding with deletion,
<a
href="https://edx.readthedocs.io/projects/edx-guide-for-students/en/latest/SFD_certificates.html#printing-a-certificate"
onClick={[Function]}
target="_self"
>
follow the instructions for printing or downloading a certificate
</a>
.
</span>
You may also lose access to verified certificates and other program credentials. You can make a copy of these for your records before proceeding with deletion.
</p>
<p
className="text-danger h6"
>
Warning: Account deletion is permanent. Please read the above carefully before proceeding. This is an irreversible action, and you will no longer be able to use the same email on edX.
Warning: Account deletion is permanent. Please read the above carefully before proceeding. This is an irreversible action, and you will no longer be able to use the same email on localhost.
</p>
<p>
<a
href="https://support.edx.org/hc/en-us/sections/115004139268-Manage-Your-Account-Settings"
onClick={[Function]}
className="pgn__hyperlink default-link standalone-link"
href="https://help.edx.org/edxlearner/s/topic/0TOQq0000001UdZOAU/account-basics"
target="_self"
>
Want to change your email, name, or password instead?
@@ -199,9 +163,7 @@ exports[`DeleteAccount should match unverified account section snapshot 2`] = `
<button
className="btn btn-outline-danger"
disabled={true}
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
onClick={null}
type="button"
>
Delete My Account
@@ -213,34 +175,32 @@ exports[`DeleteAccount should match unverified account section snapshot 2`] = `
<div>
<svg
aria-hidden="true"
className="svg-inline--fa fa-exclamation-triangle fa-w-18 mr-2"
data-icon="exclamation-triangle"
className="svg-inline--fa fa-triangle-exclamation mr-2"
data-icon="triangle-exclamation"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 576 512"
style={{}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M569.517 440.013C587.975 472.007 564.806 512 527.94 512H48.054c-36.937 0-59.999-40.055-41.577-71.987L246.423 23.985c18.467-32.009 64.72-31.951 83.154 0l239.94 416.028zM288 354c-25.405 0-46 20.595-46 46s20.595 46 46 46 46-20.595 46-46-20.595-46-46-46zm-43.673-165.346l7.418 136c.347 6.364 5.609 11.346 11.982 11.346h48.546c6.373 0 11.635-4.982 11.982-11.346l7.418-136c.375-6.874-5.098-12.654-11.982-12.654h-63.383c-6.884 0-12.356 5.78-11.981 12.654z"
d="M256 32c14.2 0 27.3 7.5 34.5 19.8l216 368c7.3 12.4 7.3 27.7 .2 40.1S486.3 480 472 480L40 480c-14.3 0-27.6-7.7-34.7-20.1s-7-27.8 .2-40.1l216-368C228.7 39.5 241.8 32 256 32zm0 128c-13.3 0-24 10.7-24 24l0 112c0 13.3 10.7 24 24 24s24-10.7 24-24l0-112c0-13.3-10.7-24-24-24zm32 224a32 32 0 1 0 -64 0 32 32 0 1 0 64 0z"
fill="currentColor"
style={Object {}}
style={{}}
/>
</svg>
</div>
<div>
<span>
Before proceeding, please
<a
href="https://support.edx.org/hc/en-us/articles/207206067"
onClick={[Function]}
target="_self"
>
unlink all social media accounts
</a>
.
</span>
Before proceeding, please
<a
className="pgn__hyperlink default-link standalone-link"
href="https://help.edx.org/edxlearner/s/article/How-do-I-link-or-unlink-my-edX-account-to-a-social-media-account"
target="_self"
>
unlink all social media accounts
</a>
.
</div>
</div>
</div>

View File

@@ -1,311 +1,93 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`SuccessModal should match default closed success modal snapshot 1`] = `
<div>
<div
className="fade"
role="presentation"
/>
<div
className="modal js-close-modal-on-click fade"
onMouseDown={[Function]}
role="presentation"
>
<div
aria-labelledby="id2"
aria-modal={true}
className=""
role="dialog"
tabIndex="-1"
>
<div
className="modal-content"
>
<div
className="modal-header"
>
<h2
className="modal-title"
id="id2"
>
We're sorry to see you go! Your account will be deleted shortly.
</h2>
</div>
<div
className="modal-body"
>
<div>
<p
className="h6"
>
Account deletion, including removal from email lists, may take a few weeks to fully process through our system. If you want to opt-out of emails before then, please unsubscribe from the footer of any email.
</p>
</div>
</div>
<div
className="modal-footer"
>
<button
className="btn js-close-modal-on-click btn-secondary"
id="paragonCloseModalButton1"
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Close
</button>
</div>
</div>
</div>
</div>
</div>
`;
exports[`SuccessModal should match default closed success modal snapshot 1`] = `null`;
exports[`SuccessModal should match default closed success modal snapshot 2`] = `
<div>
<div
className="fade"
role="presentation"
/>
<div
className="modal js-close-modal-on-click fade"
onMouseDown={[Function]}
role="presentation"
>
<div
aria-labelledby="id4"
aria-modal={true}
className=""
role="dialog"
tabIndex="-1"
>
<div
className="modal-content"
>
<div
className="modal-header"
>
<h2
className="modal-title"
id="id4"
>
We're sorry to see you go! Your account will be deleted shortly.
</h2>
</div>
<div
className="modal-body"
>
<div>
<p
className="h6"
>
Account deletion, including removal from email lists, may take a few weeks to fully process through our system. If you want to opt-out of emails before then, please unsubscribe from the footer of any email.
</p>
</div>
</div>
<div
className="modal-footer"
>
<button
className="btn js-close-modal-on-click btn-secondary"
id="paragonCloseModalButton3"
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Close
</button>
</div>
</div>
</div>
</div>
</div>
`;
exports[`SuccessModal should match default closed success modal snapshot 2`] = `null`;
exports[`SuccessModal should match default closed success modal snapshot 3`] = `
<div>
<div
className="fade"
role="presentation"
/>
<div
className="modal js-close-modal-on-click fade"
onMouseDown={[Function]}
role="presentation"
>
<div
aria-labelledby="id6"
aria-modal={true}
className=""
role="dialog"
tabIndex="-1"
>
<div
className="modal-content"
>
<div
className="modal-header"
>
<h2
className="modal-title"
id="id6"
>
We're sorry to see you go! Your account will be deleted shortly.
</h2>
</div>
<div
className="modal-body"
>
<div>
<p
className="h6"
>
Account deletion, including removal from email lists, may take a few weeks to fully process through our system. If you want to opt-out of emails before then, please unsubscribe from the footer of any email.
</p>
</div>
</div>
<div
className="modal-footer"
>
<button
className="btn js-close-modal-on-click btn-secondary"
id="paragonCloseModalButton5"
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Close
</button>
</div>
</div>
</div>
</div>
</div>
`;
exports[`SuccessModal should match default closed success modal snapshot 3`] = `null`;
exports[`SuccessModal should match default closed success modal snapshot 4`] = `
<div>
<div
className="fade"
role="presentation"
/>
<div
className="modal js-close-modal-on-click fade"
onMouseDown={[Function]}
role="presentation"
>
<div
aria-labelledby="id8"
aria-modal={true}
className=""
role="dialog"
tabIndex="-1"
>
<div
className="modal-content"
>
<div
className="modal-header"
>
<h2
className="modal-title"
id="id8"
>
We're sorry to see you go! Your account will be deleted shortly.
</h2>
</div>
<div
className="modal-body"
>
<div>
<p
className="h6"
>
Account deletion, including removal from email lists, may take a few weeks to fully process through our system. If you want to opt-out of emails before then, please unsubscribe from the footer of any email.
</p>
</div>
</div>
<div
className="modal-footer"
>
<button
className="btn js-close-modal-on-click btn-secondary"
id="paragonCloseModalButton7"
onBlur={[Function]}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Close
</button>
</div>
</div>
</div>
</div>
</div>
`;
exports[`SuccessModal should match default closed success modal snapshot 4`] = `null`;
exports[`SuccessModal should match open success modal snapshot 1`] = `
<div>
[
<div
className="modal-backdrop show"
role="presentation"
/>
data-focus-guard={true}
style={
{
"height": "0px",
"left": "1px",
"overflow": "hidden",
"padding": 0,
"position": "fixed",
"top": "1px",
"width": "1px",
}
}
tabIndex={0}
/>,
<div
className="modal js-close-modal-on-click show d-block"
className="pgn__modal-layer"
data-focus-lock-disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
role="presentation"
onScrollCapture={[Function]}
onTouchMoveCapture={[Function]}
onTouchStart={[Function]}
onWheelCapture={[Function]}
>
<div
aria-labelledby="id10"
aria-modal={true}
className="modal-dialog"
role="dialog"
tabIndex="-1"
className="pgn__modal-content-container"
>
<div
className="modal-content"
className="pgn__modal-backdrop"
data-testid="modal-backdrop"
onClick={[MockFunction]}
onKeyDown={[MockFunction]}
role="presentation"
/>
<div
className="mw-sm p-5 bg-white mx-auto my-3"
>
<h3>
We're sorry to see you go! Your account will be deleted shortly.
</h3>
<div
className="modal-header"
className="p-3"
>
<h2
className="modal-title"
id="id10"
<p
className="h6"
>
We're sorry to see you go! Your account will be deleted shortly.
</h2>
Account deletion, including removal from email lists, may take a few weeks to fully process through our system. If you want to opt-out of emails before then, please unsubscribe from the footer of any email.
</p>
</div>
<div
className="modal-body"
>
<div>
<p
className="h6"
>
Account deletion, including removal from email lists, may take a few weeks to fully process through our system. If you want to opt-out of emails before then, please unsubscribe from the footer of any email.
</p>
</div>
</div>
<div
className="modal-footer"
>
<p>
<button
className="btn js-close-modal-on-click btn-secondary"
id="paragonCloseModalButton9"
onBlur={[Function]}
className="pgn__modal-close-button float-right btn btn-link"
disabled={false}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Close
</button>
</div>
</p>
</div>
</div>
</div>
</div>
</div>,
<div
data-focus-guard={true}
style={
{
"height": "0px",
"left": "1px",
"overflow": "hidden",
"padding": 0,
"position": "fixed",
"top": "1px",
"width": "1px",
}
}
tabIndex={0}
/>,
]
`;

View File

@@ -15,7 +15,9 @@ export function* handleDeleteAccount(action) {
const response = yield call(postDeleteAccount, action.payload.password);
yield put(deleteAccountSuccess(response));
} catch (e) {
if (typeof e.response.data === 'string') {
if (e.response.status === 403) {
yield put(deleteAccountFailure('invalid-password'));
} else if (typeof e.response.data === 'string') {
yield put(deleteAccountFailure());
} else {
throw e;

View File

@@ -0,0 +1,65 @@
import { getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import formurlencoded from 'form-urlencoded';
import { handleRequestError } from '../../data/utils';
import { postDeleteAccount } from './service';
jest.mock('@edx/frontend-platform');
jest.mock('@edx/frontend-platform/auth');
jest.mock('form-urlencoded');
jest.mock('../../data/utils');
describe('postDeleteAccount', () => {
const mockPost = jest.fn();
beforeEach(() => {
jest.resetAllMocks();
getConfig.mockReturnValue({
LMS_BASE_URL: 'http://testserver',
});
getAuthenticatedHttpClient.mockReturnValue({
post: mockPost,
});
formurlencoded.mockImplementation(obj => `encoded:${JSON.stringify(obj)}`);
});
it('posts delete account request with password', async () => {
const mockResponse = { data: { success: true } };
mockPost.mockResolvedValueOnce(mockResponse);
const result = await postDeleteAccount('mypassword');
expect(getConfig).toHaveBeenCalled();
expect(getAuthenticatedHttpClient).toHaveBeenCalled();
expect(formurlencoded).toHaveBeenCalledWith({ password: 'mypassword' });
expect(mockPost).toHaveBeenCalledWith(
'http://testserver/api/user/v1/accounts/deactivate_logout/',
'encoded:{"password":"mypassword"}',
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
},
);
expect(result).toEqual(mockResponse.data);
});
it('calls handleRequestError and throws when request fails', async () => {
const mockError = new Error('Request failed');
mockPost.mockRejectedValueOnce(mockError);
handleRequestError.mockImplementation(() => {
throw mockError;
});
await expect(postDeleteAccount('wrongpassword')).rejects.toThrow('Request failed');
expect(handleRequestError).toHaveBeenCalledWith(mockError);
});
});

View File

@@ -1,3 +1,4 @@
// eslint-disable-next-line no-restricted-exports
export { default } from './DeleteAccount';
export { default as reducer } from './data/reducers';
export { default as saga } from './data/sagas';

View File

@@ -1,6 +1,11 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
'account.settings.cannot.delete.account.text': {
id: 'account.settings.cannot.delete.account.text',
defaultMessage: 'Please note that, for legal and regulatory compliance purposes, account deletion is currently unavailable.',
description: 'This text is visible when user is not allowed to delete account',
},
'account.settings.delete.account.header': {
id: 'account.settings.delete.account.header',
defaultMessage: 'Delete My Account',
@@ -13,22 +18,27 @@ const messages = defineMessages({
},
'account.settings.delete.account.text.1': {
id: 'account.settings.delete.account.text.1',
defaultMessage: 'Please note: Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.',
defaultMessage: 'Please note: Deletion of your account and personal data is permanent and cannot be undone. {siteName} will not be able to recover your account or the data that is deleted.',
description: 'A message in the user account deletion area',
},
'account.settings.delete.account.text.2': {
id: 'account.settings.delete.account.text.2',
defaultMessage: 'Once your account is deleted, you cannot use it to take courses on {siteName}.',
description: 'A message in the user account deletion area',
},
'account.settings.delete.account.text.2.edX': {
id: 'account.settings.delete.account.text.2.edX',
defaultMessage: 'Once your account is deleted, you cannot use it to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employers or universitys system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.',
description: 'A message in the user account deletion area',
},
'account.settings.delete.account.text.3.link': {
id: 'account.settings.delete.account.text.3.link',
defaultMessage: 'follow the instructions for printing or downloading a certificate',
description: 'This text will be a link to a technical support page; it will go in the phrase If you want to make a copy of these for your records, ______ .',
defaultMessage: 'Follow these instructions for printing or downloading a certificate',
description: 'This text is a link to a technical support page where users can learn how to print or download their certificates.',
},
'account.settings.delete.account.text.warning': {
id: 'account.settings.delete.account.text.warning',
defaultMessage: 'Warning: Account deletion is permanent. Please read the above carefully before proceeding. This is an irreversible action, and you will no longer be able to use the same email on edX.',
defaultMessage: 'Warning: Account deletion is permanent. Please read the above carefully before proceeding. This is an irreversible action, and you will no longer be able to use the same email on {siteName}.',
description: 'A message in the user account deletion area',
},
'account.settings.delete.account.text.change.instead': {
@@ -39,13 +49,18 @@ const messages = defineMessages({
'account.settings.delete.account.button': {
id: 'account.settings.delete.account.button',
defaultMessage: 'Delete My Account',
description: 'Button label to permanently delete your edX account',
description: 'Button label to permanently delete your platform account',
},
'account.settings.delete.account.please.activate': {
id: 'account.settings.delete.account.please.activate',
defaultMessage: 'activate your account',
description: 'This is the text on a link that goes to the support page. It is part of this sentence: Before proceeding, please activate your account.',
},
'account.settings.delete.account.please.confirm': {
id: 'account.settings.delete.account.please.confirm',
defaultMessage: 'confirm your account',
description: 'This is the text on a link that goes to the support page. It is part of this sentence: Before proceeding, please confirm your account.',
},
'account.settings.delete.account.please.unlink': {
id: 'account.settings.delete.account.please.unlink',
defaultMessage: 'unlink all social media accounts',
@@ -58,11 +73,16 @@ const messages = defineMessages({
},
'account.settings.delete.account.modal.text.1': {
id: 'account.settings.delete.account.modal.text.1',
defaultMessage: 'You have selected "Delete My Account". Deletion of your account and personal data is permanent and cannot be undone. edX will not be able to recover your account or the data that is deleted.',
defaultMessage: 'You have selected "Delete My Account". Deletion of your account and personal data is permanent and cannot be undone. {siteName} will not be able to recover your account or the data that is deleted.',
description: 'Messaging in the dialog asking user to confirm that they want to delete their entire account',
},
'account.settings.delete.account.modal.text.2': {
id: 'account.settings.delete.account.modal.text.2',
defaultMessage: 'If you proceed, you will be unable to use this account to take courses on {siteName}.',
description: 'Messaging in the dialog asking user to confirm that they want to delete their entire account',
},
'account.settings.delete.account.modal.text.2.edX': {
id: 'account.settings.delete.account.modal.text.2.edX',
defaultMessage: 'If you proceed, you will be unable to use this account to take courses on the edX app, edx.org, or any other site hosted by edX. This includes access to edx.org from your employer\'s or university\'s system and access to private sites offered by MIT Open Learning, Wharton Executive Education, and Harvard Medical School.',
description: 'Messaging in the dialog asking user to confirm that they want to delete their entire account',
},
@@ -91,6 +111,11 @@ const messages = defineMessages({
defaultMessage: 'A password is required',
description: 'Error message when user has not entered their password',
},
'account.settings.delete.account.error.invalid.password': {
id: 'account.settings.delete.account.error.invalid.password',
defaultMessage: 'Password is incorrect',
description: 'Error message when user has entered incorrect password',
},
'account.settings.delete.account.error.unable.to.delete.details': {
id: 'account.settings.delete.account.error.unable.to.delete.details',
defaultMessage: 'Sorry, there was an error trying to process your request. Please try again later.',

View File

@@ -0,0 +1,19 @@
import React from 'react';
import { useLocation, useNavigate } from 'react-router-dom';
export const withNavigate = Component => {
const WrappedComponent = props => {
const navigate = useNavigate();
return <Component {...props} navigate={navigate} />;
};
return WrappedComponent;
};
export const withLocation = Component => {
const WrappedComponent = props => {
const location = useLocation();
return <Component {...props} location={location.pathname} />;
};
return WrappedComponent;
};

View File

@@ -0,0 +1,38 @@
import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react';
import { withLocation, withNavigate } from './hoc';
const mockedNavigator = jest.fn();
jest.mock('react-router-dom', () => ({
useNavigate: () => mockedNavigator,
useLocation: () => ({
pathname: '/current-location',
}),
}));
// eslint-disable-next-line react/prop-types
const MockComponent = ({ navigate, location }) => (
// eslint-disable-next-line react/button-has-type, react/prop-types
<button data-testid="btn" onClick={() => navigate('/some-route')}>{location}</button>
);
const WrappedComponent = withNavigate(withLocation(MockComponent));
test('Provide Navigation to Component', () => {
render(
<WrappedComponent />,
);
const btn = screen.getByTestId('btn');
fireEvent.click(btn);
expect(mockedNavigator).toHaveBeenCalledWith('/some-route');
});
test('Provide Location Pathname to Component', () => {
render(
<WrappedComponent />,
);
expect(screen.getByTestId('btn').textContent).toContain('/current-location');
});

View File

@@ -1,3 +1,4 @@
// eslint-disable-next-line no-restricted-exports
export { default } from './AccountSettingsPage';
export { default as reducer } from './data/reducers';
export { default as saga } from './data/sagas';

View File

@@ -0,0 +1,202 @@
import { useCallback, useEffect, useState } from 'react';
import { connect, useDispatch } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import PropTypes from 'prop-types';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
import { useIntl } from '@edx/frontend-platform/i18n';
import {
ActionRow,
Alert,
Button,
Col,
Form,
ModalDialog,
StatefulButton,
} from '@openedx/paragon';
import { closeForm, saveSettingsReset } from '../data/actions';
import { nameChangeSelector } from '../data/selectors';
import { requestNameChange, requestNameChangeFailure, requestNameChangeReset } from './data/actions';
import messages from './messages';
const NameChangeModal = ({
targetFormId,
errors,
formValues,
saveState,
}) => {
const dispatch = useDispatch();
const navigate = useNavigate();
const { username } = getAuthenticatedUser();
const [verifiedNameInput, setVerifiedNameInput] = useState(formValues.verified_name || '');
const [confirmedWarning, setConfirmedWarning] = useState(false);
const intl = useIntl();
const resetLocalState = useCallback(() => {
setConfirmedWarning(false);
dispatch(requestNameChangeReset());
}, [dispatch]);
const handleChange = (e) => {
setVerifiedNameInput(e.target.value);
};
const handleClose = useCallback(() => {
resetLocalState();
dispatch(closeForm(targetFormId));
dispatch(saveSettingsReset());
}, [dispatch, resetLocalState, targetFormId]);
const handleSubmit = (e) => {
e.preventDefault();
if (saveState === 'pending') {
return;
}
if (!verifiedNameInput) {
dispatch(requestNameChangeFailure({
verified_name: intl.formatMessage(messages['account.settings.name.change.error.valid.name']),
}));
} else {
const draftProfileName = targetFormId === 'name' ? formValues.name : null;
dispatch(requestNameChange(username, draftProfileName, verifiedNameInput));
}
};
useEffect(() => {
if (saveState === 'complete') {
handleClose();
navigate(`/id-verification?next=${encodeURIComponent('account/settings')}`);
}
}, [handleClose, navigate, saveState]);
function renderErrors() {
if (Object.keys(errors).length > 0) {
return (
<>
{Object.entries(errors).map(([key, value]) => (
<Form.Control.Feedback type="invalid" key={key}>
{
key === 'general_error'
? intl.formatMessage(messages['account.settings.name.change.error.general'])
: value
}
</Form.Control.Feedback>
))}
</>
);
}
return null;
}
function renderTitle() {
if (!confirmedWarning) {
return intl.formatMessage(messages['account.settings.name.change.title.id']);
}
return intl.formatMessage(messages['account.settings.name.change.title.begin']);
}
function renderBody() {
if (!confirmedWarning) {
return (
<Alert variant="warning">
<p>
{intl.formatMessage(messages['account.settings.name.change.warning.one'])}
</p>
<p>
{intl.formatMessage(messages['account.settings.name.change.warning.two'])}
</p>
</Alert>
);
}
return (
<Form.Group as={Col} isInvalid={Object.keys(errors).length > 0}>
<Form.Label>
{intl.formatMessage(messages['account.settings.name.change.id.name.label'])}
</Form.Label>
<Form.Control
type="text"
name="verifiedName"
placeholder={intl.formatMessage(messages['account.settings.name.change.id.name.placeholder'])}
value={verifiedNameInput}
onChange={handleChange}
/>
{renderErrors()}
</Form.Group>
);
}
function renderContinueButton() {
if (!confirmedWarning) {
return (
<Button variant="primary" onClick={() => setConfirmedWarning(true)}>
{intl.formatMessage(messages['account.settings.name.change.continue'])}
</Button>
);
}
return (
<StatefulButton
type="submit"
state={saveState}
labels={{
default: intl.formatMessage(messages['account.settings.name.change.continue']),
}}
disabledStates={[]}
/>
);
}
return (
<ModalDialog
title={renderTitle()}
isOpen
hasCloseButton={false}
onClose={handleClose}
>
<Form onSubmit={handleSubmit}>
<ModalDialog.Header>
<ModalDialog.Title>
{renderTitle()}
</ModalDialog.Title>
</ModalDialog.Header>
<ModalDialog.Body className="mb-3 overflow-hidden">
{renderBody()}
</ModalDialog.Body>
<ModalDialog.Footer>
<ActionRow>
<ModalDialog.CloseButton variant="tertiary">
{intl.formatMessage(messages['account.settings.name.change.cancel'])}
</ModalDialog.CloseButton>
{renderContinueButton()}
</ActionRow>
</ModalDialog.Footer>
</Form>
</ModalDialog>
);
};
NameChangeModal.propTypes = {
targetFormId: PropTypes.string.isRequired,
errors: PropTypes.shape({}).isRequired,
formValues: PropTypes.shape({
name: PropTypes.string,
verified_name: PropTypes.string,
}).isRequired,
saveState: PropTypes.string,
};
NameChangeModal.defaultProps = {
saveState: null,
};
export default connect(nameChangeSelector)(NameChangeModal);

View File

@@ -0,0 +1,25 @@
import { AsyncActionType } from '../../data/utils';
export const REQUEST_NAME_CHANGE = new AsyncActionType('ACCOUNT_SETTINGS', 'REQUEST_NAME_CHANGE');
export const requestNameChange = (username, profileName, verifiedName) => ({
type: REQUEST_NAME_CHANGE.BASE,
payload: { username, profileName, verifiedName },
});
export const requestNameChangeBegin = () => ({
type: REQUEST_NAME_CHANGE.BEGIN,
});
export const requestNameChangeSuccess = () => ({
type: REQUEST_NAME_CHANGE.SUCCESS,
});
export const requestNameChangeFailure = errors => ({
type: REQUEST_NAME_CHANGE.FAILURE,
payload: { errors },
});
export const requestNameChangeReset = () => ({
type: REQUEST_NAME_CHANGE.RESET,
});

View File

@@ -0,0 +1,44 @@
import { REQUEST_NAME_CHANGE } from './actions';
export const defaultState = {
saveState: null,
errors: {},
};
const reducer = (state = defaultState, action = null) => {
if (action !== null) {
switch (action.type) {
case REQUEST_NAME_CHANGE.BEGIN:
return {
...state,
saveState: 'pending',
errors: {},
};
case REQUEST_NAME_CHANGE.SUCCESS:
return {
...state,
saveState: 'complete',
};
case REQUEST_NAME_CHANGE.FAILURE:
return {
...state,
saveState: 'error',
errors: action.payload.errors || { general_error: 'A technical error occurred. Please try again.' },
};
case REQUEST_NAME_CHANGE.RESET:
return {
...state,
saveState: null,
errors: {},
};
default:
}
}
return state;
};
export default reducer;

View File

@@ -0,0 +1,40 @@
import { put, call, takeEvery } from 'redux-saga/effects';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
import { postVerifiedName } from '../../data/service';
import {
REQUEST_NAME_CHANGE,
requestNameChangeBegin,
requestNameChangeSuccess,
requestNameChangeFailure,
} from './actions';
import { postNameChange } from './service';
export function* handleRequestNameChange(action) {
let { name: profileName } = getAuthenticatedUser();
try {
yield put(requestNameChangeBegin());
if (action.payload.profileName) {
yield call(postNameChange, action.payload.profileName);
profileName = action.payload.profileName;
}
yield call(postVerifiedName, {
username: action.payload.username,
verified_name: action.payload.verifiedName,
profile_name: profileName,
});
yield put(requestNameChangeSuccess());
} catch (err) {
if (err.customAttributes?.httpErrorResponseData) {
yield put(requestNameChangeFailure(JSON.parse(err.customAttributes.httpErrorResponseData)));
} else {
yield put(requestNameChangeFailure());
}
}
}
export default function* saga() {
yield takeEvery(REQUEST_NAME_CHANGE.BASE, handleRequestNameChange);
}

View File

@@ -0,0 +1,17 @@
import { getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { handleRequestError } from '../../data/utils';
// eslint-disable-next-line import/prefer-default-export
export async function postNameChange(name) {
// Requests a pending name change, rather than saving the account name immediately
const requestConfig = { headers: { Accept: 'application/json' } };
const requestUrl = `${getConfig().LMS_BASE_URL}/api/user/v1/accounts/name_change/`;
const { data } = await getAuthenticatedHttpClient()
.post(requestUrl, { name }, requestConfig)
.catch(error => handleRequestError(error));
return data;
}

View File

@@ -0,0 +1,56 @@
import { getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { handleRequestError } from '../../data/utils';
import { postNameChange } from './service';
jest.mock('@edx/frontend-platform');
jest.mock('@edx/frontend-platform/auth');
jest.mock('../../data/utils');
describe('postNameChange', () => {
const mockPost = jest.fn();
beforeEach(() => {
jest.resetAllMocks();
getConfig.mockReturnValue({
LMS_BASE_URL: 'http://testserver',
});
getAuthenticatedHttpClient.mockReturnValue({
post: mockPost,
});
});
it('posts a name change request successfully', async () => {
const mockResponse = { data: { success: true, updated: true } };
mockPost.mockResolvedValueOnce(mockResponse);
const result = await postNameChange('New Name');
expect(getConfig).toHaveBeenCalled();
expect(getAuthenticatedHttpClient).toHaveBeenCalled();
expect(mockPost).toHaveBeenCalledWith(
'http://testserver/api/user/v1/accounts/name_change/',
{ name: 'New Name' },
{ headers: { Accept: 'application/json' } },
);
expect(result).toEqual(mockResponse.data);
});
it('calls handleRequestError and throws when request fails', async () => {
const mockError = new Error('Request failed');
mockPost.mockRejectedValueOnce(mockError);
handleRequestError.mockImplementation(() => {
throw mockError;
});
await expect(postNameChange('Bad Name')).rejects.toThrow('Request failed');
expect(handleRequestError).toHaveBeenCalledWith(mockError);
});
});

View File

@@ -0,0 +1,5 @@
// eslint-disable-next-line no-restricted-exports
export { default } from './NameChange';
export { default as reducer } from './data/reducers';
export { default as saga } from './data/sagas';
export { REQUEST_NAME_CHANGE } from './data/actions';

View File

@@ -0,0 +1,56 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
'account.settings.name.change.title.id': {
id: 'account.settings.name.change.title.id',
defaultMessage: 'This name change requires identity verification',
description: 'Inform the user that changing their name requires identity verification',
},
'account.settings.name.change.title.begin': {
id: 'account.settings.name.change.title.begin',
defaultMessage: 'Before we begin',
description: 'Title before beginning the ID verification process',
},
'account.settings.name.change.warning.one': {
id: 'account.settings.name.change.warning.one',
defaultMessage: 'Warning: This action updates the name that appears on all certificates that have been earned on this account in the past and any certificates you are currently earning or will earn in the future.',
description: 'Warning informing the user that a name change will update the name on all of their certificates.',
},
'account.settings.name.change.warning.two': {
id: 'account.settings.name.change.warning.two',
defaultMessage: 'This action cannot be undone without verifying your identity.',
description: 'Warning informing the user that a name change cannot be undone without ID verification.',
},
'account.settings.name.change.id.name.label': {
id: 'account.settings.name.change.id.name.label',
defaultMessage: 'Enter your name as it appears on your identification card.',
description: 'Form label instructing the user to enter the name on their ID.',
},
'account.settings.name.change.id.name.placeholder': {
id: 'account.settings.name.change.id.name.placeholder',
defaultMessage: 'Enter the name on your photo ID',
description: 'Form label instructing the user to enter the name on their ID.',
},
'account.settings.name.change.error.valid.name': {
id: 'account.settings.name.change.error.valid.name',
defaultMessage: 'Please enter a valid name.',
description: 'Error that appears when the user doesnt enter a valid name.',
},
'account.settings.name.change.error.general': {
id: 'account.settings.name.change.error.general',
defaultMessage: 'A technical error occurred. Please try again.',
description: 'Generic error message.',
},
'account.settings.name.change.continue': {
id: 'account.settings.name.change.continue',
defaultMessage: 'Continue',
description: 'Continue button.',
},
'account.settings.name.change.cancel': {
id: 'account.settings.name.change.cancel',
defaultMessage: 'Cancel',
description: 'Cancel button.',
},
});
export default messages;

View File

@@ -0,0 +1,168 @@
/* eslint-disable no-import-assign */
import { Provider } from 'react-redux';
import { BrowserRouter as Router } from 'react-router-dom';
import configureStore from 'redux-mock-store';
import {
fireEvent,
render,
screen,
} from '@testing-library/react';
import * as auth from '@edx/frontend-platform/auth';
import { IntlProvider } from '@edx/frontend-platform/i18n';
// Modal creates a portal. Overriding createPortal allows portals to be tested in jest.
jest.mock('react-dom', () => ({
...jest.requireActual('react-dom'),
createPortal: jest.fn(node => node), // Mock portal behavior
}));
import NameChange from '../NameChange'; // eslint-disable-line import/first
const mockDispatch = jest.fn();
jest.mock('react-redux', () => ({
...jest.requireActual('react-redux'),
useDispatch: () => mockDispatch,
}));
jest.mock('@edx/frontend-platform/auth');
jest.mock('../../data/selectors', () => jest.fn().mockImplementation(() => ({ nameChangeSelector: () => ({}) })));
const mockStore = configureStore();
describe('NameChange', () => {
let props = {};
let store = {};
const reduxWrapper = children => (
<Router>
<IntlProvider locale="en">
<Provider store={store}>{children}</Provider>
</IntlProvider>
</Router>
);
beforeEach(() => {
store = mockStore();
props = {
targetFormId: 'test_form',
errors: {},
formValues: {
name: 'edx edx',
verified_name: 'edX Verified',
},
saveState: null,
};
auth.getAuthenticatedHttpClient = jest.fn(() => ({
patch: async () => ({
data: { status: 200 },
catch: () => {},
}),
}));
auth.getAuthenticatedUser = jest.fn(() => ({ userId: 3, username: 'edx' }));
});
afterEach(() => jest.clearAllMocks());
it('renders populated input after clicking continue if verified_name in form data', async () => {
const getInput = () => screen.queryByPlaceholderText('Enter the name on your photo ID');
render(reduxWrapper(<NameChange {...props} />));
expect(getInput()).toBeNull();
const continueButton = screen.getByText('Continue');
fireEvent.click(continueButton);
expect(getInput().value).toBe('edX Verified');
});
it('renders empty input after clicking continue if verified_name not in form data', async () => {
const getInput = () => screen.queryByPlaceholderText('Enter the name on your photo ID');
const formProps = {
...props,
formValues: {
name: 'edx edx',
},
};
render(reduxWrapper(<NameChange {...formProps} />));
const continueButton = screen.getByText('Continue');
fireEvent.click(continueButton);
expect(getInput().value).toBe('');
});
it('dispatches verifiedName on submit if targetForm is not "name"', async () => {
const dispatchData = {
payload: {
profileName: null,
username: 'edx',
verifiedName: 'Verified Name',
},
type: 'ACCOUNT_SETTINGS__REQUEST_NAME_CHANGE',
};
render(reduxWrapper(<NameChange {...props} />));
const continueButton = screen.getByText('Continue');
fireEvent.click(continueButton);
const input = screen.getByPlaceholderText('Enter the name on your photo ID');
fireEvent.change(input, { target: { value: 'Verified Name' } });
const submitButton = screen.getByText('Continue');
fireEvent.click(submitButton);
expect(mockDispatch).toHaveBeenCalledWith(dispatchData);
});
it('dispatches both profileName and verifiedName on submit if the targetForm is "name"', async () => {
const dispatchData = {
payload: {
profileName: 'edx edx',
username: 'edx',
verifiedName: 'Verified Name',
},
type: 'ACCOUNT_SETTINGS__REQUEST_NAME_CHANGE',
};
const formProps = {
...props,
targetFormId: 'name',
};
render(reduxWrapper(<NameChange {...formProps} />));
const continueButton = screen.getByText('Continue');
fireEvent.click(continueButton);
const input = screen.getByPlaceholderText('Enter the name on your photo ID');
fireEvent.change(input, { target: { value: 'Verified Name' } });
const submitButton = screen.getByText('Continue');
fireEvent.click(submitButton);
expect(mockDispatch).toHaveBeenCalledWith(dispatchData);
});
it('does not dispatch action while pending', async () => {
props.saveState = 'pending';
render(reduxWrapper(<NameChange {...props} />));
const continueButton = screen.getByText('Continue');
fireEvent.click(continueButton);
const input = screen.getByPlaceholderText('Enter the name on your photo ID');
fireEvent.change(input, { target: { value: 'Verified Name' } });
const submitButton = screen.getByText('Continue');
fireEvent.click(submitButton);
expect(mockDispatch).not.toHaveBeenCalled();
});
it('routes to IDV when name change request is successful', async () => {
props.saveState = 'complete';
render(reduxWrapper(<NameChange {...props} />));
expect(window.location.pathname).toEqual('/id-verification');
});
});

View File

@@ -1,7 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import { getConfig } from '@edx/frontend-platform';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { Hyperlink } from '@edx/paragon';
import { Hyperlink } from '@openedx/paragon';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
@@ -12,7 +13,7 @@ const ConfirmationAlert = (props) => {
const technicalSupportLink = (
<Hyperlink
destination="https://support.edx.org/hc/en-us/articles/206212088-What-if-I-did-not-receive-a-password-reset-message-"
destination={getConfig().PASSWORD_RESET_SUPPORT_LINK}
>
<FormattedMessage
id="account.settings.editable.field.password.reset.button.confirmation.support.link"

View File

@@ -5,20 +5,17 @@ import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import Alert from '../Alert';
const RequestInProgressAlert = (props) => {
return (
<Alert
className="alert-warning mt-n2"
icon={<FontAwesomeIcon className="mr-2" icon={faExclamationTriangle} />}
>
<FormattedMessage
id="account.settings.editable.field.password.reset.button.forbidden"
defaultMessage="Your previous request is in progress, please try again in few moments."
description="A message displayed when a previous password reset request is still in progress."
/>
</Alert>
);
};
const RequestInProgressAlert = () => (
<Alert
className="alert-warning mt-n2"
icon={<FontAwesomeIcon className="mr-2" icon={faExclamationTriangle} />}
>
<FormattedMessage
id="account.settings.editable.field.password.reset.button.forbidden"
defaultMessage="Your previous request is in progress, please try again in few moments."
description="A message displayed when a previous password reset request is still in progress."
/>
</Alert>
);
export default RequestInProgressAlert;

View File

@@ -1,8 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';
import { StatefulButton } from '@edx/paragon';
import { useIntl, FormattedMessage } from '@edx/frontend-platform/i18n';
import { StatefulButton } from '@openedx/paragon';
import { resetPassword } from './data/actions';
import messages from './messages';
@@ -10,7 +9,9 @@ import ConfirmationAlert from './ConfirmationAlert';
import RequestInProgressAlert from './RequestInProgressAlert';
const ResetPassword = (props) => {
const { email, intl, status } = props;
const { email, status } = props;
const intl = useIntl();
return (
<div className="form-group">
<h6 aria-level="3">
@@ -22,7 +23,7 @@ const ResetPassword = (props) => {
</h6>
<p>
<StatefulButton
className="btn-link"
variant="link"
state={status}
onClick={(e) => {
// Swallow clicks if the state is pending.
@@ -51,7 +52,6 @@ const ResetPassword = (props) => {
ResetPassword.propTypes = {
email: PropTypes.string,
intl: intlShape.isRequired,
resetPassword: PropTypes.func.isRequired,
status: PropTypes.string,
};
@@ -68,4 +68,4 @@ export default connect(
{
resetPassword,
},
)(injectIntl(ResetPassword));
)(ResetPassword);

View File

@@ -1,6 +1,8 @@
import { put, call, takeEvery } from 'redux-saga/effects';
import { resetPasswordBegin, resetPasswordForbidden, resetPasswordSuccess, RESET_PASSWORD } from './actions';
import {
resetPasswordBegin, resetPasswordForbidden, resetPasswordSuccess, RESET_PASSWORD,
} from './actions';
import { postResetPassword } from './service';
function* handleResetPassword(action) {

View File

@@ -0,0 +1,65 @@
import { getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import formurlencoded from 'form-urlencoded';
import { handleRequestError } from '../../data/utils';
import { postResetPassword } from './service';
jest.mock('@edx/frontend-platform');
jest.mock('@edx/frontend-platform/auth');
jest.mock('form-urlencoded');
jest.mock('../../data/utils');
describe('postResetPassword', () => {
const mockPost = jest.fn();
beforeEach(() => {
jest.resetAllMocks();
getConfig.mockReturnValue({
LMS_BASE_URL: 'http://testserver',
});
getAuthenticatedHttpClient.mockReturnValue({
post: mockPost,
});
formurlencoded.mockImplementation(obj => `encoded:${JSON.stringify(obj)}`);
});
it('posts reset password request with email', async () => {
const mockResponse = { data: { success: true, email_sent: true } };
mockPost.mockResolvedValueOnce(mockResponse);
const result = await postResetPassword('user@example.com');
expect(getConfig).toHaveBeenCalled();
expect(getAuthenticatedHttpClient).toHaveBeenCalled();
expect(formurlencoded).toHaveBeenCalledWith({ email: 'user@example.com' });
expect(mockPost).toHaveBeenCalledWith(
'http://testserver/password_reset/',
'encoded:{"email":"user@example.com"}',
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
},
);
expect(result).toEqual(mockResponse.data);
});
it('calls handleRequestError and throws when request fails', async () => {
const mockError = new Error('Reset password failed');
mockPost.mockRejectedValueOnce(mockError);
handleRequestError.mockImplementation(() => {
throw mockError;
});
await expect(postResetPassword('bad@example.com')).rejects.toThrow('Reset password failed');
expect(handleRequestError).toHaveBeenCalledWith(mockError);
});
});

View File

@@ -1,3 +1,4 @@
// eslint-disable-next-line no-restricted-exports
export { default } from './ResetPassword';
export { default as reducer } from './data/reducers';
export { RESET_PASSWORD } from './data/actions';

Some files were not shown because too many files have changed in this diff Show More