Compare commits

..

97 Commits

Author SHA1 Message Date
Renovate Bot
8aebaf7583 fix(deps): update dependency core-js to v3.16.0 2021-08-02 05:23:04 +00:00
Renovate Bot
77d28e2aba fix(deps): update dependency @edx/frontend-platform to v1.11.3 2021-08-02 05:10:30 +00:00
Renovate Bot
40b9526f92 fix(deps): update dependency @edx/paragon to v16.6.1 2021-07-26 08:35:06 +00:00
Renovate Bot
462b64f720 chore(deps): update dependency @edx/frontend-build to v7.1.0 2021-07-26 08:25:13 +00:00
Renovate Bot
931710d489 fix(deps): update dependency regenerator-runtime to v0.13.9 2021-07-26 04:35:23 +00:00
Renovate Bot
b871bb6835 chore(deps): update dependency codecov to v3.8.3 2021-07-26 04:25:26 +00:00
stvn
4dbd66a615 merge(#446): renovate/husky-7.x
commits
=======
- chore(deps): update dependency husky to v7
2021-07-21 11:27:35 -07:00
Renovate Bot
c797320d71 chore(deps): update dependency husky to v7 2021-07-19 07:07:02 +00:00
Renovate Bot
b1ca0dd34e fix(deps): update dependency @edx/paragon to v16.3.1 2021-07-19 07:02:11 +00:00
Renovate Bot
6b256eb09e fix(deps): update dependency @edx/frontend-platform to v1.11.1 2021-07-19 06:51:12 +00:00
Renovate Bot
78267b5326 chore(deps): update dependency @edx/frontend-build to v7.0.6 2021-07-19 06:38:56 +00:00
David Joy
f06bea9612 fix: remove purgecss (#440)
PurgeCSS doesn’t work with the latest version of PostCSS, far as I can tell.  It ends up removing too many classes.  It has always been sort of problematic and we only use it in a few MFEs, so I think it’s time for it to go.
2021-07-13 10:16:09 -04:00
Renovate Bot
242a03ee72 fix(deps): update dependency @edx/paragon to v16.1.0 2021-07-12 07:28:45 +00:00
Renovate Bot
4b35d64d13 chore(deps): update dependency @edx/frontend-build to v7.0.3 2021-07-12 07:19:22 +00:00
David Joy
16d179d9b0 fix: upgrades frontend build to v7 and paragon to v16 (#439)
* build: upgrades frontend build to v7 and paragon to v16

* test: update test snapshots

Three things happening here:

- The implementation of the external link icon in Hyperlink was updated to have an SVG icon instead of font-awesome
- The implementation of StatefulButton also changed to use SVG icons instead of font-awesome
- Dropdown appears to be setting an onKeyDown handler that it wasn’t before.  This is fine.
2021-07-07 16:01:45 -04:00
renovate[bot]
42d6535969 chore(deps): update dependency @edx/frontend-build to v6 (#438)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-07-06 14:44:56 -04:00
Renovate Bot
ba1036c7ed fix(deps): update dependency core-js to v3.15.2 2021-07-05 07:38:42 +00:00
Renovate Bot
c6fb8acb63 fix(deps): update dependency core-js to v3.15.1 2021-06-28 07:25:46 +00:00
Renovate Bot
78c816dd68 chore(deps): update dependency es-check to v5.2.4 2021-06-28 07:12:56 +00:00
Sarina Canelake
6f2253e904 Merge pull request #433 from edx/renovate/husky-6.x
chore(deps): update dependency husky to v6
2021-06-21 16:59:07 -04:00
Renovate Bot
1b618bcbab chore(deps): update dependency husky to v6 2021-06-21 06:52:58 +00:00
Renovate Bot
6511559261 fix(deps): update dependency redux to v4.1.0 2021-06-21 06:46:14 +00:00
Renovate Bot
e96c3c9c31 fix(deps): update dependency core-js to v3.15.0 2021-06-21 06:30:11 +00:00
stvn
0052682b6e merge(#403): renovate/major-commitlint-monorepo
commits
=======
- chore(deps): update commitlint monorepo to v12
2021-06-15 00:37:17 -07:00
Renovate Bot
6b36f988fe chore(deps): update commitlint monorepo to v12 2021-06-15 07:26:20 +00:00
Renovate Bot
ebca3d7620 fix(deps): update dependency @edx/frontend-component-header to v2.3.0 2021-06-15 07:19:16 +00:00
Renovate Bot
d964848714 fix(deps): update dependency core-js to v3.14.0 2021-06-14 07:38:55 +00:00
Renovate Bot
8bfecc1309 fix(deps): update dependency classnames to v2.3.1 2021-06-14 07:22:41 +00:00
Renovate Bot
0294b5d5b6 fix(deps): update dependency @edx/frontend-platform to v1.11.0 2021-06-11 21:34:25 +00:00
Renovate Bot
8f7e4f3059 chore(deps): update dependency @edx/frontend-build to v5.6.14 2021-06-11 21:15:08 +00:00
Renovate Bot
3e2881fa27 fix(deps): update reactrouter monorepo to v5.2.0 2021-06-08 12:24:32 +00:00
Renovate Bot
aa49509606 fix(deps): update dependency react-transition-group to v4.4.2 2021-06-08 11:32:37 +00:00
Renovate Bot
b60c97667e fix(deps): update dependency history to v4.10.1 2021-06-08 11:11:17 +00:00
Renovate Bot
e9a2cb3df1 fix(deps): update dependency react-redux to v7.2.4 2021-06-08 09:53:13 +00:00
dependabot[bot]
de024e175d Merge pull request #425 from edx/dependabot/npm_and_yarn/ws-6.2.2 2021-06-08 05:53:19 +00:00
Renovate Bot
e0252f3c01 fix(deps): update dependency redux-devtools-extension to v2.13.9 2021-06-07 07:40:14 +00:00
Renovate Bot
7238b36b4d fix(deps): update dependency @edx/frontend-component-footer to v10.1.5 2021-06-07 07:13:49 +00:00
Renovate Bot
72a40132de chore(deps): update dependency glob to v7.1.7 2021-06-07 06:46:47 +00:00
dependabot[bot]
6dd5db108e build(deps): bump ws from 6.2.1 to 6.2.2
Bumps [ws](https://github.com/websockets/ws) from 6.2.1 to 6.2.2.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/commits)

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

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-05 10:58:19 +00:00
Renovate Bot
6182141bfb chore(deps): update dependency enzyme to v3.11.0 2021-06-05 08:40:49 +00:00
stvn
1a8cfbb397 merge(#424): build/renovate
commits
=======
- build(renovate): fix json syntax
2021-06-04 23:44:17 -07:00
stvn
cd510ee704 build(renovate): fix json syntax 2021-06-04 23:19:26 -07:00
dependabot[bot]
1063092c5d Merge pull request #409 from edx/dependabot/npm_and_yarn/elliptic-6.5.4 2021-06-05 06:18:34 +00:00
stvn
68b5ca22a8 merge(#423): build/renovate-stricter
commits
=======
- build(renovate): be more selective about automerging devDependencies
2021-06-04 13:44:13 -07:00
dependabot[bot]
c2108f2909 build(deps): bump elliptic from 6.5.3 to 6.5.4
Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.3 to 6.5.4.
- [Release notes](https://github.com/indutny/elliptic/releases)
- [Commits](https://github.com/indutny/elliptic/compare/v6.5.3...v6.5.4)

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-04 20:34:57 +00:00
stvn
5823842f80 build(renovate): be more selective about automerging devDependencies 2021-06-04 13:21:22 -07:00
stvn
9cf776cfbc merge(#414): dependabot/npm_and_yarn/url-parse-1.5.1
commits
=======
- build(deps): bump url-parse from 1.4.7 to 1.5.1
2021-06-04 12:56:23 -07:00
Matjaz Gregoric
8ae748c6f7 fix: hide 'View My Records' button if no credentials service (#421)
This hides the 'View My Records' button from users profile page if
`CREDENTIALS_BASE_URL` is not configured.
2021-06-04 12:22:36 -04:00
Renovate Bot
00b2dee8a8 fix(deps): update dependency redux-saga to v1.1.3 2021-06-04 02:53:55 +00:00
Renovate Bot
bbb2224762 chore(deps): update dependency react-test-renderer to v16.14.0 2021-06-04 02:25:56 +00:00
dependabot[bot]
c78e8a608a Merge pull request #411 from edx/dependabot/npm_and_yarn/ssri-6.0.2 2021-06-04 00:44:52 +00:00
Renovate Bot
d6011917b3 fix(deps): update dependency newrelic to v5.13.1 2021-06-04 00:34:39 +00:00
Renovate Bot
7eb4d33037 chore(deps): update dependency es-check to v5.2.3 2021-06-04 00:02:12 +00:00
dependabot[bot]
808348bdce Merge pull request #419 from edx/dependabot/npm_and_yarn/dns-packet-1.3.4 2021-06-03 22:37:35 +00:00
dependabot[bot]
4855b5aacb build(deps): bump url-parse from 1.4.7 to 1.5.1
Bumps [url-parse](https://github.com/unshiftio/url-parse) from 1.4.7 to 1.5.1.
- [Release notes](https://github.com/unshiftio/url-parse/releases)
- [Commits](https://github.com/unshiftio/url-parse/compare/1.4.7...1.5.1)

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-03 21:15:00 +00:00
dependabot[bot]
494787942d build(deps): bump ssri from 6.0.1 to 6.0.2
Bumps [ssri](https://github.com/npm/ssri) from 6.0.1 to 6.0.2.
- [Release notes](https://github.com/npm/ssri/releases)
- [Changelog](https://github.com/npm/ssri/blob/v6.0.2/CHANGELOG.md)
- [Commits](https://github.com/npm/ssri/compare/v6.0.1...v6.0.2)

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-03 21:14:52 +00:00
dependabot[bot]
5d94d12670 build(deps): bump dns-packet from 1.3.1 to 1.3.4
Bumps [dns-packet](https://github.com/mafintosh/dns-packet) from 1.3.1 to 1.3.4.
- [Release notes](https://github.com/mafintosh/dns-packet/releases)
- [Changelog](https://github.com/mafintosh/dns-packet/blob/master/CHANGELOG.md)
- [Commits](https://github.com/mafintosh/dns-packet/compare/v1.3.1...v1.3.4)

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-03 21:13:23 +00:00
dependabot[bot]
2b68a2cfe6 Merge pull request #416 from edx/dependabot/npm_and_yarn/hosted-git-info-2.8.9 2021-06-03 20:38:13 +00:00
Renovate Bot
3074dae40f chore(deps): update dependency codecov to v3.8.2 2021-06-03 19:50:58 +00:00
stvn
9ca7f57102 merge(#422): build/renovate
commits
=======
- build(renovate): be more liberal about what automerges
2021-06-03 11:15:04 -07:00
stvn
b33ed94fc6 build(renovate): be more liberal about what automerges
based on https://github.com/edx/frontend-app-account .
2021-06-03 10:42:01 -07:00
stvn
e1fa025eea merge: stvn/own/code 2021-05-26 13:42:18 -07:00
stvn
6bd5be71b8 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:40 -07:00
David Joy
8cf271f434 chore: let renovate be more liberal about what it merges (#417)
We've adopted a looser renovate config that will auto-merge more things in other repositories, and it's worked out fine.  I just lifted this config from frontend-platform, which was itself based on prospectus (private).
2021-05-18 16:28:24 -04:00
dependabot[bot]
563bcc48c9 build(deps): bump hosted-git-info from 2.8.8 to 2.8.9
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-17 14:24:42 +00:00
renovate[bot]
283c6c143a chore(deps): update commitlint monorepo (#358)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-05-17 10:23:06 -04:00
David Joy
889b9a4482 fix: use the SITE_NAME env var in index.html (#413) 2021-05-04 14:06:04 -04:00
David Joy
fae8500223 build: add module.config.js to .gitignore (#412) 2021-05-04 09:22:12 -04:00
Renovate Bot
e4b218a47e fix(deps): update dependency @edx/frontend-component-header to v2.2.4 2021-02-08 20:35:27 +00:00
Renovate Bot
1f6cfefe18 fix(deps): update dependency @edx/frontend-platform to v1.8.4 2021-02-05 23:03:57 +00:00
Renovate Bot
efa68ef0be fix(deps): update dependency @edx/frontend-component-header to v2.2.3 2021-02-05 21:26:13 +00:00
Renovate Bot
aeebd4de33 fix(deps): update dependency @edx/frontend-component-footer to v10.1.4 2021-02-05 20:39:02 +00:00
Renovate Bot
849b0101e3 chore(deps): update dependency enzyme-adapter-react-16 to v1.15.6 2021-02-05 19:24:14 +00:00
David Joy
e3b692b9f2 Update Paragon to 13 and make the app brandable (#397)
* fix: fixing broken linter script and linting

The linter script in package.json didn’t specify which files to lint, so it never linted anything.  As soon as I fixed the script line, there was suddenly a ton of stuff that needed linting.

This fixes the script and cleans up all the things that needed linting.  The vast majority were formatting auto-fixes in VSCode for me.

* fix: setting NODE_ENV to production in .env

Without this change, the NODE_ENV comes through as “null” (a string!) in the app.  This causes a number of third party dependencies like React and Redux to potentially go into development mode, slowing them down, or to not realize they’re in production mode, causing them to throw some warnings.

* style: some additional linting

* feat: upgrading to modern paragon and a brand package

This commit updates the app from Paragon 7 to 12 and fixes the breaking changes in between.  Mostly small changes to Button and Dropdown, as well as using “container” instead of “container-fluid” to preserve the page’s width as closely as possible.

It also adds the brand package, which is why it’s a feature.  Using the brand package allows the MFE to be rebranded by using an npm alias to override the source of the brand.

* test: fixing test snapshots that failed when updating paragon

The test snapshots got a bit out of date when updating paragon.  Also removing an unncessary “type” from Dropdown.Toggle which does nothing.

- container has been replaced by container-fluid
- The Button component is a different implementation, which adds slightly different properties to the rendered button.  i.e., onKeyDown and disabled, but doesn’t add the id or onBlur.
- The Dropdown doesn’t render its contents until it’s opened, which is why “Upload Photo” and “Remove” are no longer in the snapshot.
-

* build: bumping paragon to 13

* fix: fixing broken test snapshot

btn-outline is definitely not a correct button type.

* fix: using the ‘size’ property on Button

* fix: updating dependencies

We needed to upgrade paragon to 13.1.2 to fix a transpilation issue that was causing ES6 code to be included in the build artifact.  All other upgrades here were attempts at fixing that, but they’re all also perfectly valid and good to update, so I left them.

babel-polyfill has been replaced by including core-js and regenerator-runtime.

Upgrading frontend-build fixed an issue with eslint configuration that emerged during the other upgrades.

* fix: switch back to container-fluid

We want to leave it as container-fluid and solve the max width problem through paragon.

* style: cleanup and formatting of SCSS

Also removing an unnecessary variant of primary on a button.

* test: fix broken snapshot test
2021-02-05 13:38:36 -05:00
David Joy
616c620432 fix: bumping frontend-platform to latest (#395) 2021-01-05 16:39:51 -05:00
edX Transifex Bot
fb508152f3 fix(i18n): update translations 2020-12-27 15:40:47 -05:00
Renovate Bot
e68c280b11 chore(deps): update dependency enzyme-adapter-react-16 to v1.15.5 2020-12-21 06:11:08 +00:00
Adam Stankiewicz
cb738286ed fix: upgrade header and footer to use logo from shared config (#390) 2020-12-16 11:25:21 -05:00
morenol
cd24d0da8d fix: update frontend-build and frontend-platform (#386)
* fix: update frontend-build

* Upgrade frontend-platform
2020-11-10 16:31:52 -05:00
Zainab Amir
2f9f573698 feat: add hotjar suppression to PII (#385) 2020-09-22 11:13:01 +05:00
Renovate Bot
563cd4b8df fix(deps): update dependency @edx/frontend-component-footer to v10.0.11 2020-09-20 22:39:15 +00:00
Renovate Bot
25911bdfeb chore(deps): update dependency enzyme-adapter-react-16 to v1.15.4 2020-09-20 21:39:11 +00:00
Renovate Bot
179fbaa452 chore(deps): update dependency codecov to v3.7.2 2020-09-20 20:37:15 +00:00
Jeff LaJoie
73df912f29 Merge pull request #378 from edx/jlajoie/ENT-3302
fix: ENTERPRISE_LEARNER_PORTAL_HOSTNAME added for header updates
2020-08-06 07:25:49 -04:00
Jeff LaJoie
d64e497407 fix: ENTERPRISE_LEARNER_PORTAL_HOSTNAME added for header updates 2020-08-06 07:09:53 -04:00
dependabot[bot]
3725a36b30 build(deps-dev): bump codecov from 3.6.5 to 3.7.1 (#376)
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:54 -04:00
Renovate Bot
3cd6999886 fix(deps): update dependency @edx/frontend-component-footer to v10.0.9 2020-04-26 11:11:35 +00:00
David Joy
3ff2a0a359 fix: fixing LOGOUT_URL for dev and test (#374) 2020-04-21 13:06:15 -04:00
Renovate Bot
5b0f79f6f9 fix(deps): update dependency @edx/frontend-component-footer to v10.0.7 2020-02-07 17:13:29 +00:00
Renovate Bot
d3490985d2 chore(deps): update dependency codecov to v3.6.5 2020-02-07 15:10:42 +00:00
Renovate Bot
82300bf99a chore(deps): update dependency codecov to v3.6.4 2020-02-01 03:11:48 +00:00
Renovate Bot
c096bf3d28 chore(deps): update dependency codecov to v3.6.3 2020-01-31 14:12:36 +00:00
Renovate Bot
ac885e49d7 chore(deps): update dependency @edx/frontend-build to v2.0.6 2020-01-29 21:12:22 +00:00
Renovate Bot
c925666501 fix(deps): update dependency @edx/frontend-platform to v1.1.14 2020-01-28 13:13:15 +00:00
Renovate Bot
c3e30e8163 chore(deps): update dependency codecov to v3.6.2 2020-01-23 19:13:36 +00:00
Renovate Bot
d5d2279797 fix(deps): update dependency @edx/frontend-component-header to v2.0.5 2020-01-22 18:41:21 +00:00
Renovate Bot
8bb03e1404 fix(deps): update dependency @edx/frontend-component-header to v2.0.4 2020-01-21 19:10:39 +00:00
44 changed files with 12944 additions and 10954 deletions

9
.env
View File

@@ -1,4 +1,4 @@
NODE_ENV=null
NODE_ENV='production'
ACCESS_TOKEN_COOKIE_NAME=null
BASE_URL=null
CREDENTIALS_BASE_URL=null
@@ -12,10 +12,11 @@ MARKETING_SITE_BASE_URL=null
ORDER_HISTORY_URL=null
REFRESH_ACCESS_TOKEN_ENDPOINT=null
SEGMENT_KEY=null
SITE_NAME=null
SITE_NAME=''
USER_INFO_COOKIE_NAME=null
APPLE_APP_STORE_URL=null
CONTACT_URL=null
ENTERPRISE_LEARNER_PORTAL_HOSTNAME=null
ENTERPRISE_MARKETING_FOOTER_UTM_MEDIUM=null
ENTERPRISE_MARKETING_URL=null
ENTERPRISE_MARKETING_UTM_CAMPAIGN=null
@@ -30,3 +31,7 @@ SUPPORT_URL=null
TERMS_OF_SERVICE_URL=null
TWITTER_URL=null
YOU_TUBE_URL=null
LOGO_URL=''
LOGO_TRADEMARK_URL=''
LOGO_WHITE_URL=''
FAVICON_URL=''

View File

@@ -8,15 +8,16 @@ ECOMMERCE_BASE_URL='http://localhost:18130'
LANGUAGE_PREFERENCE_COOKIE_NAME='openedx-language-preference'
LMS_BASE_URL='http://localhost:18000'
LOGIN_URL='http://localhost:18000/login'
LOGOUT_URL='http://localhost:18000/login'
LOGOUT_URL='http://localhost:18000/logout'
MARKETING_SITE_BASE_URL='http://localhost:18000'
ORDER_HISTORY_URL='localhost:1996/orders'
REFRESH_ACCESS_TOKEN_ENDPOINT='http://localhost:18000/login_refresh'
SEGMENT_KEY=null
SITE_NAME='edX'
SITE_NAME=localhost
USER_INFO_COOKIE_NAME='edx-user-info'
APPLE_APP_STORE_URL='https://www.apple.com/ios/app-store/'
CONTACT_URL='http://localhost:18000/contact'
ENTERPRISE_LEARNER_PORTAL_HOSTNAME='http://localhost:8080'
ENTERPRISE_MARKETING_FOOTER_UTM_MEDIUM='Footer'
ENTERPRISE_MARKETING_URL='http://example.com'
ENTERPRISE_MARKETING_UTM_CAMPAIGN='my_campaign'
@@ -31,3 +32,7 @@ SUPPORT_URL='http://localhost:18000/support'
TERMS_OF_SERVICE_URL='http://localhost:18000/terms-of-service'
TWITTER_URL='https://twitter.com'
YOU_TUBE_URL='https://www.youtube.com'
LOGO_URL=https://edx-cdn.org/v3/default/logo.svg
LOGO_TRADEMARK_URL=https://edx-cdn.org/v3/default/logo-trademark.svg
LOGO_WHITE_URL=https://edx-cdn.org/v3/default/logo-white.svg
FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico

View File

@@ -6,10 +6,14 @@ ECOMMERCE_BASE_URL='http://localhost:18130'
LANGUAGE_PREFERENCE_COOKIE_NAME='openedx-language-preference'
LMS_BASE_URL='http://localhost:18000'
LOGIN_URL='http://localhost:18000/login'
LOGOUT_URL='http://localhost:18000/login'
LOGOUT_URL='http://localhost:18000/logout'
MARKETING_SITE_BASE_URL='http://localhost:18000'
ORDER_HISTORY_URL='localhost:1996/orders'
REFRESH_ACCESS_TOKEN_ENDPOINT='http://localhost:18000/login_refresh'
SEGMENT_KEY=null
SITE_NAME='edX'
SITE_NAME=localhost
USER_INFO_COOKIE_NAME='edx-user-info'
LOGO_URL=https://edx-cdn.org/v3/default/logo.svg
LOGO_TRADEMARK_URL=https://edx-cdn.org/v3/default/logo-trademark.svg
LOGO_WHITE_URL=https://edx-cdn.org/v3/default/logo-white.svg
FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico

1
.github/CODEOWNERS vendored Normal file
View File

@@ -0,0 +1 @@
* @edx/community-engineering

1
.gitignore vendored
View File

@@ -17,3 +17,4 @@ temp/babel-plugin-react-intl
/.vscode
/temp
/npm-dist
/module.config.js

View File

@@ -49,13 +49,6 @@ This MFE is configured via node environment variables supplied at build time. Se
For more information see the document: `Micro-frontend applications in Open
edX <https://edx.readthedocs.io/projects/edx-developer-docs/en/latest/micro-frontends-in-open-edx.html>`__.
----------
Notes
-----
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.**
.. |Build Status| image:: https://api.travis-ci.org/edx/frontend-app-profile.svg?branch=master
:target: https://travis-ci.org/edx/frontend-app-profile
.. |Codecov| image:: https://img.shields.io/codecov/c/github/edx/frontend-app-profile

21729
package-lock.json generated

File diff suppressed because it is too large Load Diff

59
package.json Executable file → Normal file
View File

@@ -14,7 +14,7 @@
"npm-build": "make npm-build",
"i18n_extract": "BABEL_ENV=i18n fedx-scripts babel src --quiet > /dev/null",
"is-es5": "es-check es5 ./dist/*.js",
"lint": "fedx-scripts eslint",
"lint": "fedx-scripts eslint --ext .js --ext .jsx .",
"snapshot": "fedx-scripts jest --updateSnapshot",
"start": "fedx-scripts webpack-dev-server --progress",
"test": "fedx-scripts jest --coverage --passWithNoTests"
@@ -40,54 +40,55 @@
"ie 11"
],
"dependencies": {
"@edx/frontend-component-footer": "10.0.6",
"@edx/frontend-component-header": "2.0.3",
"@edx/frontend-platform": "1.1.13",
"@edx/paragon": "7.1.3",
"@edx/brand": "npm:@edx/brand-openedx@1.1.0",
"@edx/frontend-component-footer": "10.1.5",
"@edx/frontend-component-header": "2.3.0",
"@edx/frontend-platform": "1.11.3",
"@edx/paragon": "16.6.1",
"@fortawesome/fontawesome-svg-core": "1.2.25",
"@fortawesome/free-brands-svg-icons": "5.7.2",
"@fortawesome/free-regular-svg-icons": "5.7.2",
"@fortawesome/free-solid-svg-icons": "5.7.2",
"@fortawesome/react-fontawesome": "0.1.8",
"babel-polyfill": "6.26.0",
"classnames": "2.2.6",
"classnames": "2.3.1",
"core-js": "3.16.0",
"email-prop-type": "1.1.7",
"font-awesome": "4.7.0",
"form-urlencoded": "3.0.2",
"history": "4.7.2",
"history": "4.10.1",
"lodash.camelcase": "4.3.0",
"lodash.get": "4.4.2",
"lodash.memoize": "4.1.2",
"lodash.pick": "4.4.0",
"lodash.snakecase": "4.1.1",
"newrelic": "5.5.0",
"newrelic": "5.13.1",
"prop-types": "15.7.2",
"react": "16.9.0",
"react-dom": "16.9.0",
"react-redux": "7.1.3",
"react-router": "5.0.1",
"react-router-dom": "5.0.1",
"react-transition-group": "4.3.0",
"redux": "4.0.5",
"redux-devtools-extension": "2.13.8",
"react": "16.14.0",
"react-dom": "16.14.0",
"react-redux": "7.2.4",
"react-router": "5.2.0",
"react-router-dom": "5.2.0",
"react-transition-group": "4.4.2",
"redux": "4.1.0",
"redux-devtools-extension": "2.13.9",
"redux-logger": "3.0.6",
"redux-saga": "1.0.5",
"redux-saga": "1.1.3",
"redux-thunk": "2.3.0",
"regenerator-runtime": "0.13.9",
"reselect": "4.0.0",
"universal-cookie": "3.1.0"
},
"devDependencies": {
"@commitlint/cli": "8.2.0",
"@commitlint/config-angular": "8.2.0",
"@edx/frontend-build": "2.0.5",
"codecov": "3.6.1",
"enzyme": "3.10.0",
"enzyme-adapter-react-16": "1.15.2",
"es-check": "5.0.0",
"glob": "7.1.6",
"husky": "3.1.0",
"purgecss-webpack-plugin": "1.6.0",
"react-test-renderer": "16.9.0",
"@commitlint/cli": "12.1.4",
"@commitlint/config-angular": "12.1.4",
"@edx/frontend-build": "7.1.0",
"codecov": "3.8.3",
"enzyme": "3.11.0",
"enzyme-adapter-react-16": "1.15.6",
"es-check": "5.2.4",
"glob": "7.1.7",
"husky": "7.0.1",
"react-test-renderer": "16.14.0",
"reactifex": "1.1.1",
"redux-mock-store": "1.5.4"
}

View File

@@ -1,12 +1,12 @@
<!doctype html>
<html lang="en-us">
<head>
<title>Learner Profile | edX</title>
<title>Learner Profile | <%= process.env.SITE_NAME %></title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="preconnect" href="https://fonts.gstatic.com/" crossorigin>
<link href="https://fonts.googleapis.com/css?family=Roboto:400,500,700" rel="stylesheet">
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
<link rel="shortcut icon" href="<%=webpackConfig.output.publicPath%>favicon.ico" type="image/x-icon" />
</head>
<body>
<div id="root"></div>

View File

@@ -1,9 +1,28 @@
{
"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
}
],
"timezone": "America/New_York"
}

View File

@@ -2,9 +2,8 @@ import { combineReducers } from 'redux';
import { reducer as profilePage } from '../profile';
const createRootReducer = () =>
combineReducers({
profilePage,
});
const createRootReducer = () => combineReducers({
profilePage,
});
export default createRootReducer;

View File

@@ -1,51 +1,51 @@
{
"profile.age.headline": "Your profile cannot be shared.",
"profile.age.details": "To share your profile with other edX learners, you must confirm that you are over the age of 13.",
"profile.age.set.date": "Set your date of birth",
"profile.datejoined.member.since": "Member since {year}",
"profile.bio.empty": "Add a short bio",
"profile.bio.about.me": "About Me",
"profile.certificate.organization.label": "From",
"profile.certificate.completion.date.label": "Completed on {date}",
"profile.no.certificates": "You don't have any certificates yet.",
"profile.certificates.my.certificates": "My Certificates",
"profile.certificates.view.certificate": "View Certificate",
"profile.certificates.types.verified": "Verified Certificate",
"profile.certificates.types.professional": "Professional Certificate",
"profile.certificates.types.unknown": "Certificate",
"profile.country.label": "Location",
"profile.country.empty": "Add location",
"profile.education.empty": "Add education",
"profile.education.education": "Education",
"profile.education.levels.p": "Doctorate",
"profile.education.levels.m": "Master's or professional degree",
"profile.education.levels.b": "Bachelor's Degree",
"profile.education.levels.a": "Associate's degree",
"profile.education.levels.hs": "Secondary/high school",
"profile.education.levels.jhs": "Junior secondary/junior high/middle school",
"profile.education.levels.el": "Elementary/primary school",
"profile.education.levels.none": "No formal education",
"profile.education.levels.o": "Other education",
"profile.editbutton.edit": "Edit",
"profile.formcontrols.who.can.see": "Who can see this:",
"profile.formcontrols.button.cancel": "Cancel",
"profile.formcontrols.button.save": "Save",
"profile.formcontrols.button.saving": "Saving",
"profile.formcontrols.button.saved": "Saved",
"profile.visibility.who.just.me": "Just me",
"profile.visibility.who.everyone": "Everyone on edX",
"profile.name.full.name": "Full Name",
"profile.name.details": "This is the name that appears in your account and on your certificates.",
"profile.name.empty": "Add name",
"profile.preferredlanguage.empty": "Add language",
"profile.preferredlanguage.label": "Primary Language Spoken",
"profile.profileavatar.upload-button": "Upload Photo",
"profile.profileavatar.remove.button": "Remove",
"profile.image.alt.attribute": "profile avatar",
"profile.profileavatar.change-button": "Change",
"profile.sociallinks.add": "Add {network}",
"profile.sociallinks.social.links": "Social Links",
"profile.notfound.message": "The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again.",
"profile.viewMyRecords": "View My Records",
"profile.loading": "Profile loading..."
"profile.age.headline": "لا يمكن مشاركة ملفك الشخصي",
"profile.age.details": "لمشاركة ملفك الشخصي مع متعلمي edX الآخرين يجب التحقق من أن يكون عمرك أكثر من ١٣ سنة",
"profile.age.set.date": "اضبط تاريخ ميلاك",
"profile.datejoined.member.since": "عضو منذُ {year}",
"profile.bio.empty": "أضف نبذة قصيرة",
"profile.bio.about.me": "نبذة عنّي",
"profile.certificate.organization.label": "من",
"profile.certificate.completion.date.label": "مكتمل في",
"profile.no.certificates": "لم تحصل على أية شهادات حتى الآن.",
"profile.certificates.my.certificates": "شهاداتي",
"profile.certificates.view.certificate": "معاينة الشهادة",
"profile.certificates.types.verified": "شهادة موثقة",
"profile.certificates.types.professional": "شهادة مهنية",
"profile.certificates.types.unknown": "شهادة",
"profile.country.label": "الموقع",
"profile.country.empty": "أضف موقعًا",
"profile.education.empty": "أضف مؤهلًا تعليميًا",
"profile.education.education": "المستوى التعليمي",
"profile.education.levels.p": "شهادة دكتوراه",
"profile.education.levels.m": "ماجستير أو شهادة مهنيّة",
"profile.education.levels.b": "درجة البكالوريوس",
"profile.education.levels.a": "درجة الزمالة",
"profile.education.levels.hs": "شهادة الثانوية العامة",
"profile.education.levels.jhs": "شهادة الثانوية الصغرى/الإعدادية/المرحلة المتوسّطة",
"profile.education.levels.el": "شهادة المدرسة الابتدائية",
"profile.education.levels.none": "لا يوجد تعليم رسمي",
"profile.education.levels.o": "نوع آخر من التعليم",
"profile.editbutton.edit": "تحرير",
"profile.formcontrols.who.can.see": "من يمكنه مشاهدة هذا:",
"profile.formcontrols.button.cancel": "إلغاء",
"profile.formcontrols.button.save": "حفظ",
"profile.formcontrols.button.saving": "جاري الحفظ",
"profile.formcontrols.button.saved": "تم الحفظ",
"profile.visibility.who.just.me": "أنا فقط",
"profile.visibility.who.everyone": "جميع أعضاء edX",
"profile.name.full.name": "الاسم الكامل",
"profile.name.details": "هذا هو الاسم الذي سيظهر في حسابك وفي شهاداتك",
"profile.name.empty": "إضافة اسم",
"profile.preferredlanguage.empty": "إضافة اللغة",
"profile.preferredlanguage.label": "لغة التحدث الأم",
"profile.profileavatar.upload-button": "تحميل صورة",
"profile.profileavatar.remove.button": "حذف",
"profile.image.alt.attribute": "صورة عرض الملف الشخصي",
"profile.profileavatar.change-button": "تغيير",
"profile.sociallinks.add": "إضافة {network}",
"profile.sociallinks.social.links": "روابط قنوات التواصل الاجتماعي",
"profile.notfound.message": "الصفحة التي تبحث عنها غير متوفرة أو هناك خطأ في نص الرابط. الرجاء التحقق من الرابط والمحاولة مجددا.",
"profile.viewMyRecords": "عرض سجلّاتي",
"profile.loading": "جاري تحميل الملف الشخصي ..."
}

View File

@@ -1,7 +1,16 @@
import 'babel-polyfill';
import 'core-js/stable';
import 'regenerator-runtime/runtime';
import { APP_INIT_ERROR, APP_READY, initialize, subscribe } from '@edx/frontend-platform';
import { AppProvider, ErrorPage } from '@edx/frontend-platform/react';
import {
APP_INIT_ERROR,
APP_READY,
initialize,
subscribe,
} from '@edx/frontend-platform';
import {
AppProvider,
ErrorPage,
} from '@edx/frontend-platform/react';
import React from 'react';
import ReactDOM from 'react-dom';

View File

@@ -1,7 +1,11 @@
@import '~@edx/paragon/scss/edx/theme.scss';
@import '~@edx/paragon/scss/edx/fonts.scss'; // Roboto
@import './profile/index.scss';
$fa-font-path: "~font-awesome/fonts";
@import "~font-awesome/scss/font-awesome";
@import "~@edx/brand/paragon/fonts";
@import "~@edx/brand/paragon/variables";
@import "~@edx/paragon/scss/core/core";
@import "~@edx/brand/paragon/overrides";
@import "~@edx/frontend-component-header/dist/index";
@import "~@edx/frontend-component-footer/dist/footer";
@import './profile/index';

View File

@@ -7,8 +7,8 @@ function AgeMessage({ accountSettingsUrl }) {
return (
<StatusAlert
alertType="info"
dialog={
<React.Fragment>
dialog={(
<>
<FormattedMessage
id="profile.age.headline"
defaultMessage="Your profile cannot be shared."
@@ -28,8 +28,8 @@ function AgeMessage({ accountSettingsUrl }) {
description="label on a link to set birthday"
/>
</a>
</React.Fragment>
}
</>
)}
dismissible={false}
open
/>

View File

@@ -3,7 +3,9 @@ import PropTypes from 'prop-types';
import { FormattedMessage, FormattedDate } from '@edx/frontend-platform/i18n';
function DateJoined({ date }) {
if (date == null) return null;
if (date == null) {
return null;
}
return (
<p className="mb-0">

View File

@@ -44,9 +44,10 @@ ensureConfig(['CREDENTIALS_BASE_URL', 'LMS_BASE_URL'], 'ProfilePage');
class ProfilePage extends React.Component {
constructor(props, context) {
super(props, context);
const credentialsBaseUrl = context.config.CREDENTIALS_BASE_URL;
this.state = {
viewMyRecordsUrl: `${context.config.CREDENTIALS_BASE_URL}/records`,
viewMyRecordsUrl: credentialsBaseUrl ? `${credentialsBaseUrl}/records` : null,
accountSettingsUrl: `${context.config.LMS_BASE_URL}/account/settings`,
};
@@ -95,7 +96,7 @@ class ProfilePage extends React.Component {
// Inserted into the DOM in two places (for responsive layout)
renderViewMyRecordsButton() {
if (!this.isAuthenticatedUserProfile()) {
if (!(this.state.viewMyRecordsUrl && this.isAuthenticatedUserProfile())) {
return null;
}
@@ -111,11 +112,13 @@ class ProfilePage extends React.Component {
const { dateJoined } = this.props;
return (
<React.Fragment>
<h1 className="h2 mb-0 font-weight-bold">{this.props.match.params.username}</h1>
<DateJoined date={dateJoined} />
<hr className="d-none d-md-block" />
</React.Fragment>
<>
<span data-hj-suppress>
<h1 className="h2 mb-0 font-weight-bold">{this.props.match.params.username}</h1>
<DateJoined date={dateJoined} />
<hr className="d-none d-md-block" />
</span>
</>
);
}
@@ -177,7 +180,6 @@ class ProfilePage extends React.Component {
changeHandler: this.handleChange,
};
return (
<div className="container-fluid">
<div className="row align-items-center pt-4 mb-4 pt-md-0 mb-md-0">

View File

@@ -145,6 +145,28 @@ describe('<ProfilePage />', () => {
const tree = renderer.create(component).toJSON();
expect(tree).toMatchSnapshot();
});
it('without credentials service', () => {
const config = getConfig();
config.CREDENTIALS_BASE_URL = '';
const component = (
<AppContext.Provider
value={{
authenticatedUser: { userId: 123, username: 'staff', administrator: true },
config,
}}
>
<IntlProvider locale="en">
<Provider store={mockStore(storeMocks.viewOwnProfile)}>
<ProfilePage {...requiredProfilePageProps} />
</Provider>
</IntlProvider>
</AppContext.Provider>
);
const tree = renderer.create(component).toJSON();
expect(tree).toMatchSnapshot();
});
});
describe('handles analytics', () => {

View File

@@ -86,24 +86,28 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
<div
className="d-md-none"
>
<h1
className="h2 mb-0 font-weight-bold"
<span
data-hj-suppress={true}
>
verified
</h1>
<p
className="mb-0"
>
<span>
Member since
<h1
className="h2 mb-0 font-weight-bold"
>
verified
</h1>
<p
className="mb-0"
>
<span>
2017
Member since
<span>
2017
</span>
</span>
</span>
</p>
<hr
className="d-none d-md-block"
/>
</p>
<hr
className="d-none d-md-block"
/>
</span>
</div>
<div
className="d-none d-md-block float-right"
@@ -119,24 +123,28 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
<div
className="d-none d-md-block mb-4"
>
<h1
className="h2 mb-0 font-weight-bold"
<span
data-hj-suppress={true}
>
verified
</h1>
<p
className="mb-0"
>
<span>
Member since
<h1
className="h2 mb-0 font-weight-bold"
>
verified
</h1>
<p
className="mb-0"
>
<span>
2017
Member since
<span>
2017
</span>
</span>
</span>
</p>
<hr
className="d-none d-md-block"
/>
</p>
<hr
className="d-none d-md-block"
/>
</span>
</div>
<div
className="d-md-none mb-4"
@@ -241,42 +249,19 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
<button
aria-expanded={false}
aria-haspopup={true}
className="dropdown-toggle btn btn-light"
id="pgn__dropdown-trigger-0"
className="dropdown-toggle btn btn-primary"
disabled={false}
onClick={[Function]}
type="btn-outline"
type="button"
>
Change
</button>
<div
aria-hidden={true}
aria-labelledby="pgn__dropdown-trigger-0"
className="dropdown-menu"
onKeyDown={[Function]}
role="menu"
>
<button
className="dropdown-item"
onClick={[Function]}
>
<span>
Upload Photo
</span>
</button>
<button
className="dropdown-item"
onClick={[Function]}
>
<span>
Remove
</span>
</button>
</div>
</div>
</div>
<img
alt="profile avatar"
className="w-100 h-100 d-block rounded-circle overflow-hidden"
data-hj-suppress={true}
src="http://localhost:18000/media/profile-images/d2a9bdc2ba165dcefc73265c54bf9a20_500.jpg?v=1552495012"
style={
Object {
@@ -307,24 +292,28 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
<div
className="d-md-none"
>
<h1
className="h2 mb-0 font-weight-bold"
<span
data-hj-suppress={true}
>
staff
</h1>
<p
className="mb-0"
>
<span>
Member since
<h1
className="h2 mb-0 font-weight-bold"
>
staff
</h1>
<p
className="mb-0"
>
<span>
2017
Member since
<span>
2017
</span>
</span>
</span>
</p>
<hr
className="d-none d-md-block"
/>
</p>
<hr
className="d-none d-md-block"
/>
</span>
</div>
<div
className="d-none d-md-block float-right"
@@ -337,14 +326,36 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
target="_blank"
>
View My Records
<span>
<span
className="d-inline-block align-text-top"
>
<span
aria-hidden={false}
aria-label="Opens in a new window"
className="fa fa-external-link"
title="Opens in a new window"
/>
className="pgn__icon"
style={
Object {
"height": "1em",
"width": "1em",
}
}
>
<svg
aria-hidden={true}
aria-label=""
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
fill="currentColor"
/>
</svg>
</span>
</span>
</a>
</div>
@@ -359,24 +370,28 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
<div
className="d-none d-md-block mb-4"
>
<h1
className="h2 mb-0 font-weight-bold"
<span
data-hj-suppress={true}
>
staff
</h1>
<p
className="mb-0"
>
<span>
Member since
<h1
className="h2 mb-0 font-weight-bold"
>
staff
</h1>
<p
className="mb-0"
>
<span>
2017
Member since
<span>
2017
</span>
</span>
</span>
</p>
<hr
className="d-none d-md-block"
/>
</p>
<hr
className="d-none d-md-block"
/>
</span>
</div>
<div
className="d-md-none mb-4"
@@ -389,14 +404,36 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
target="_blank"
>
View My Records
<span>
<span
className="d-inline-block align-text-top"
>
<span
aria-hidden={false}
aria-label="Opens in a new window"
className="fa fa-external-link"
title="Opens in a new window"
/>
className="pgn__icon"
style={
Object {
"height": "1em",
"width": "1em",
}
}
>
<svg
aria-hidden={true}
aria-label=""
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
fill="currentColor"
/>
</svg>
</span>
</span>
</a>
</div>
@@ -424,10 +461,9 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
>
Full Name
<button
className="btn btn-sm btn-link float-right px-0"
onBlur={[Function]}
className="float-right px-0 btn btn-link btn-sm"
disabled={false}
onClick={[Function]}
onKeyDown={[Function]}
style={
Object {
"marginTop": "-.35rem",
@@ -485,6 +521,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
</div>
<p
className="h5"
data-hj-suppress={true}
>
Lemon Seltzer
</p>
@@ -519,10 +556,9 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
>
Location
<button
className="btn btn-sm btn-link float-right px-0"
onBlur={[Function]}
className="float-right px-0 btn btn-link btn-sm"
disabled={false}
onClick={[Function]}
onKeyDown={[Function]}
style={
Object {
"marginTop": "-.35rem",
@@ -580,6 +616,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
</div>
<p
className="h5"
data-hj-suppress={true}
>
Montenegro
</p>
@@ -609,10 +646,9 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
>
Primary Language Spoken
<button
className="btn btn-sm btn-link float-right px-0"
onBlur={[Function]}
className="float-right px-0 btn btn-link btn-sm"
disabled={false}
onClick={[Function]}
onKeyDown={[Function]}
style={
Object {
"marginTop": "-.35rem",
@@ -670,6 +706,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
</div>
<p
className="h5"
data-hj-suppress={true}
>
Yoruba
</p>
@@ -699,10 +736,9 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
>
Education
<button
className="btn btn-sm btn-link float-right px-0"
onBlur={[Function]}
className="float-right px-0 btn btn-link btn-sm"
disabled={false}
onClick={[Function]}
onKeyDown={[Function]}
style={
Object {
"marginTop": "-.35rem",
@@ -760,6 +796,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
</div>
<p
className="h5"
data-hj-suppress={true}
>
Elementary/primary school
</p>
@@ -789,10 +826,9 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
>
Social Links
<button
className="btn btn-sm btn-link float-right px-0"
onBlur={[Function]}
className="float-right px-0 btn btn-link btn-sm"
disabled={false}
onClick={[Function]}
onKeyDown={[Function]}
style={
Object {
"marginTop": "-.35rem",
@@ -914,6 +950,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
onClick={[Function]}
onKeyDown={[Function]}
tabIndex={0}
type="button"
>
<svg
aria-hidden="true"
@@ -968,10 +1005,9 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
>
About Me
<button
className="btn btn-sm btn-link float-right px-0"
onBlur={[Function]}
className="float-right px-0 btn btn-link btn-sm"
disabled={false}
onClick={[Function]}
onKeyDown={[Function]}
style={
Object {
"marginTop": "-.35rem",
@@ -1029,6 +1065,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
</div>
<p
className="lead"
data-hj-suppress={true}
>
This is my bio
</p>
@@ -1058,10 +1095,9 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
>
My Certificates
<button
className="btn btn-sm btn-link float-right px-0"
onBlur={[Function]}
className="float-right px-0 btn btn-link btn-sm"
disabled={false}
onClick={[Function]}
onKeyDown={[Function]}
style={
Object {
"marginTop": "-.35rem",
@@ -1185,14 +1221,36 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
target="_blank"
>
View Certificate
<span>
<span
className="d-inline-block align-text-top"
>
<span
aria-hidden={false}
aria-label="Opens in a new window"
className="fa fa-external-link"
title="Opens in a new window"
/>
className="pgn__icon"
style={
Object {
"height": "1em",
"width": "1em",
}
}
>
<svg
aria-hidden={true}
aria-label=""
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
fill="currentColor"
/>
</svg>
</span>
</span>
</a>
</div>
@@ -1242,42 +1300,19 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
<button
aria-expanded={false}
aria-haspopup={true}
className="dropdown-toggle btn btn-light"
id="pgn__dropdown-trigger-1"
className="dropdown-toggle btn btn-primary"
disabled={false}
onClick={[Function]}
type="btn-outline"
type="button"
>
Change
</button>
<div
aria-hidden={true}
aria-labelledby="pgn__dropdown-trigger-1"
className="dropdown-menu"
onKeyDown={[Function]}
role="menu"
>
<button
className="dropdown-item"
onClick={[Function]}
>
<span>
Upload Photo
</span>
</button>
<button
className="dropdown-item"
onClick={[Function]}
>
<span>
Remove
</span>
</button>
</div>
</div>
</div>
<img
alt="profile avatar"
className="w-100 h-100 d-block rounded-circle overflow-hidden"
data-hj-suppress={true}
src="http://localhost:18000/media/profile-images/d2a9bdc2ba165dcefc73265c54bf9a20_500.jpg?v=1552495012"
style={
Object {
@@ -1308,24 +1343,28 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
<div
className="d-md-none"
>
<h1
className="h2 mb-0 font-weight-bold"
<span
data-hj-suppress={true}
>
staff
</h1>
<p
className="mb-0"
>
<span>
Member since
<h1
className="h2 mb-0 font-weight-bold"
>
staff
</h1>
<p
className="mb-0"
>
<span>
2017
Member since
<span>
2017
</span>
</span>
</span>
</p>
<hr
className="d-none d-md-block"
/>
</p>
<hr
className="d-none d-md-block"
/>
</span>
</div>
<div
className="d-none d-md-block float-right"
@@ -1338,14 +1377,36 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
target="_blank"
>
View My Records
<span>
<span
className="d-inline-block align-text-top"
>
<span
aria-hidden={false}
aria-label="Opens in a new window"
className="fa fa-external-link"
title="Opens in a new window"
/>
className="pgn__icon"
style={
Object {
"height": "1em",
"width": "1em",
}
}
>
<svg
aria-hidden={true}
aria-label=""
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
fill="currentColor"
/>
</svg>
</span>
</span>
</a>
</div>
@@ -1360,24 +1421,28 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
<div
className="d-none d-md-block mb-4"
>
<h1
className="h2 mb-0 font-weight-bold"
<span
data-hj-suppress={true}
>
staff
</h1>
<p
className="mb-0"
>
<span>
Member since
<h1
className="h2 mb-0 font-weight-bold"
>
staff
</h1>
<p
className="mb-0"
>
<span>
2017
Member since
<span>
2017
</span>
</span>
</span>
</p>
<hr
className="d-none d-md-block"
/>
</p>
<hr
className="d-none d-md-block"
/>
</span>
</div>
<div
className="d-md-none mb-4"
@@ -1390,14 +1455,36 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
target="_blank"
>
View My Records
<span>
<span
className="d-inline-block align-text-top"
>
<span
aria-hidden={false}
aria-label="Opens in a new window"
className="fa fa-external-link"
title="Opens in a new window"
/>
className="pgn__icon"
style={
Object {
"height": "1em",
"width": "1em",
}
}
>
<svg
aria-hidden={true}
aria-label=""
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
fill="currentColor"
/>
</svg>
</span>
</span>
</a>
</div>
@@ -1425,10 +1512,9 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
>
Full Name
<button
className="btn btn-sm btn-link float-right px-0"
onBlur={[Function]}
className="float-right px-0 btn btn-link btn-sm"
disabled={false}
onClick={[Function]}
onKeyDown={[Function]}
style={
Object {
"marginTop": "-.35rem",
@@ -1486,6 +1572,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
</div>
<p
className="h5"
data-hj-suppress={true}
>
Lemon Seltzer
</p>
@@ -1520,10 +1607,9 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
>
Location
<button
className="btn btn-sm btn-link float-right px-0"
onBlur={[Function]}
className="float-right px-0 btn btn-link btn-sm"
disabled={false}
onClick={[Function]}
onKeyDown={[Function]}
style={
Object {
"marginTop": "-.35rem",
@@ -1581,6 +1667,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
</div>
<p
className="h5"
data-hj-suppress={true}
>
Montenegro
</p>
@@ -1610,10 +1697,9 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
>
Primary Language Spoken
<button
className="btn btn-sm btn-link float-right px-0"
onBlur={[Function]}
className="float-right px-0 btn btn-link btn-sm"
disabled={false}
onClick={[Function]}
onKeyDown={[Function]}
style={
Object {
"marginTop": "-.35rem",
@@ -1671,6 +1757,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
</div>
<p
className="h5"
data-hj-suppress={true}
>
Yoruba
</p>
@@ -1700,10 +1787,9 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
>
Education
<button
className="btn btn-sm btn-link float-right px-0"
onBlur={[Function]}
className="float-right px-0 btn btn-link btn-sm"
disabled={false}
onClick={[Function]}
onKeyDown={[Function]}
style={
Object {
"marginTop": "-.35rem",
@@ -1761,6 +1847,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
</div>
<p
className="h5"
data-hj-suppress={true}
>
Elementary/primary school
</p>
@@ -1790,10 +1877,9 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
>
Social Links
<button
className="btn btn-sm btn-link float-right px-0"
onBlur={[Function]}
className="float-right px-0 btn btn-link btn-sm"
disabled={false}
onClick={[Function]}
onKeyDown={[Function]}
style={
Object {
"marginTop": "-.35rem",
@@ -1915,6 +2001,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
onClick={[Function]}
onKeyDown={[Function]}
tabIndex={0}
type="button"
>
<svg
aria-hidden="true"
@@ -2053,10 +2140,9 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
<button
aria-disabled={false}
aria-live="assertive"
className="btn pgn__stateful-btn pgn__stateful-btn-state-pending btn-primary"
onBlur={[Function]}
className="pgn__stateful-btn pgn__stateful-btn-state-pending btn btn-primary"
disabled={false}
onClick={[Function]}
onKeyDown={[Function]}
type="submit"
>
<span
@@ -2066,19 +2152,37 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
className="pgn__stateful-btn-icon"
>
<span
aria-hidden={true}
className="icon fa fa-spinner fa-spin"
id="Icon2"
/>
className="pgn__icon icon-spin"
>
<svg
aria-hidden={true}
aria-label=""
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M22 12A10 10 0 116.122 3.91l1.176 1.618A8 8 0 1020 12h2z"
fill="currentColor"
/>
</svg>
</span>
</span>
<span
className=""
>
Saving
</span>
Saving
</span>
</button>
<button
className="btn btn-link"
onBlur={[Function]}
disabled={false}
onClick={[Function]}
onKeyDown={[Function]}
type="button"
>
Cancel
@@ -2113,10 +2217,9 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
>
My Certificates
<button
className="btn btn-sm btn-link float-right px-0"
onBlur={[Function]}
className="float-right px-0 btn btn-link btn-sm"
disabled={false}
onClick={[Function]}
onKeyDown={[Function]}
style={
Object {
"marginTop": "-.35rem",
@@ -2240,14 +2343,1005 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
target="_blank"
>
View Certificate
<span>
<span
className="d-inline-block align-text-top"
>
<span
aria-hidden={false}
aria-label="Opens in a new window"
className="fa fa-external-link"
title="Opens in a new window"
/>
className="pgn__icon"
style={
Object {
"height": "1em",
"width": "1em",
}
}
>
<svg
aria-hidden={true}
aria-label=""
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
fill="currentColor"
/>
</svg>
</span>
</span>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`<ProfilePage /> Renders correctly in various states without credentials service 1`] = `
<div
className="profile-page"
>
<div
className="profile-page-bg-banner bg-primary d-none d-md-block p-relative"
/>
<div
className="container-fluid"
>
<div
className="row align-items-center pt-4 mb-4 pt-md-0 mb-md-0"
>
<div
className="col-auto col-md-4 col-lg-3"
>
<div
className="d-flex align-items-center d-md-block"
>
<div
className="profile-avatar-wrap position-relative"
>
<div
className="profile-avatar rounded-circle bg-light"
>
<div
className="profile-avatar-menu-container"
>
<div
className="dropdown"
>
<button
aria-expanded={false}
aria-haspopup={true}
className="dropdown-toggle btn btn-primary"
disabled={false}
onClick={[Function]}
type="button"
>
Change
</button>
</div>
</div>
<img
alt="profile avatar"
className="w-100 h-100 d-block rounded-circle overflow-hidden"
data-hj-suppress={true}
src="http://localhost:18000/media/profile-images/d2a9bdc2ba165dcefc73265c54bf9a20_500.jpg?v=1552495012"
style={
Object {
"objectFit": "cover",
}
}
/>
</div>
<form
encType="multipart/form-data"
onSubmit={[Function]}
>
<input
accept=".jpg, .jpeg, .png"
className="d-none form-control-file"
id="photo-file"
name="file"
onChange={[Function]}
type="file"
/>
</form>
</div>
</div>
</div>
<div
className="col pl-0"
>
<div
className="d-md-none"
>
<span
data-hj-suppress={true}
>
<h1
className="h2 mb-0 font-weight-bold"
>
staff
</h1>
<p
className="mb-0"
>
<span>
Member since
<span>
2017
</span>
</span>
</p>
<hr
className="d-none d-md-block"
/>
</span>
</div>
<div
className="d-none d-md-block float-right"
/>
</div>
</div>
<div
className="row"
>
<div
className="col-md-4 col-lg-4"
>
<div
className="d-none d-md-block mb-4"
>
<span
data-hj-suppress={true}
>
<h1
className="h2 mb-0 font-weight-bold"
>
staff
</h1>
<p
className="mb-0"
>
<span>
Member since
<span>
2017
</span>
</span>
</p>
<hr
className="d-none d-md-block"
/>
</span>
</div>
<div
className="d-md-none mb-4"
/>
<div
className="pgn-transition-replace-group position-relative mb-5"
style={
Object {
"height": null,
}
}
>
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
className="editable-item-header mb-2"
>
<h2
className="edit-section-header"
id={null}
>
Full Name
<button
className="float-right px-0 btn btn-link btn-sm"
disabled={false}
onClick={[Function]}
style={
Object {
"marginTop": "-.35rem",
}
}
type="button"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-pencil-alt fa-w-16 mr-1"
data-icon="pencil-alt"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M497.9 142.1l-46.1 46.1c-4.7 4.7-12.3 4.7-17 0l-111-111c-4.7-4.7-4.7-12.3 0-17l46.1-46.1c18.7-18.7 49.1-18.7 67.9 0l60.1 60.1c18.8 18.7 18.8 49.1 0 67.9zM284.2 99.8L21.6 362.4.4 483.9c-2.9 16.4 11.4 30.6 27.8 27.8l121.5-21.3 262.6-262.6c4.7-4.7 4.7-12.3 0-17l-111-111c-4.8-4.7-12.4-4.7-17.1 0zM124.1 339.9c-5.5-5.5-5.5-14.3 0-19.8l154-154c5.5-5.5 14.3-5.5 19.8 0s5.5 14.3 0 19.8l-154 154c-5.5 5.5-14.3 5.5-19.8 0zM88 424h48v36.3l-64.5 11.3-31.1-31.1L51.7 376H88v48z"
fill="currentColor"
style={Object {}}
/>
</svg>
Edit
</button>
</h2>
<p
className="mb-0"
>
<span
className="ml-auto small text-muted"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-eye-slash fa-w-20 "
data-icon="eye-slash"
data-prefix="far"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 640 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M634 471L36 3.51A16 16 0 0 0 13.51 6l-10 12.49A16 16 0 0 0 6 41l598 467.49a16 16 0 0 0 22.49-2.49l10-12.49A16 16 0 0 0 634 471zM296.79 146.47l134.79 105.38C429.36 191.91 380.48 144 320 144a112.26 112.26 0 0 0-23.21 2.47zm46.42 219.07L208.42 260.16C210.65 320.09 259.53 368 320 368a113 113 0 0 0 23.21-2.46zM320 112c98.65 0 189.09 55 237.93 144a285.53 285.53 0 0 1-44 60.2l37.74 29.5a333.7 333.7 0 0 0 52.9-75.11 32.35 32.35 0 0 0 0-29.19C550.29 135.59 442.93 64 320 64c-36.7 0-71.71 7-104.63 18.81l46.41 36.29c18.94-4.3 38.34-7.1 58.22-7.1zm0 288c-98.65 0-189.08-55-237.93-144a285.47 285.47 0 0 1 44.05-60.19l-37.74-29.5a333.6 333.6 0 0 0-52.89 75.1 32.35 32.35 0 0 0 0 29.19C89.72 376.41 197.08 448 320 448c36.7 0 71.71-7.05 104.63-18.81l-46.41-36.28C359.28 397.2 339.89 400 320 400z"
fill="currentColor"
style={Object {}}
/>
</svg>
Just me
</span>
</p>
</div>
<p
className="h5"
data-hj-suppress={true}
>
Lemon Seltzer
</p>
<small
className="form-text text-muted"
>
This is the name that appears in your account and on your certificates.
</small>
</div>
</div>
<div
className="pgn-transition-replace-group position-relative mb-5"
style={
Object {
"height": null,
}
}
>
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
className="editable-item-header mb-2"
>
<h2
className="edit-section-header"
id={null}
>
Location
<button
className="float-right px-0 btn btn-link btn-sm"
disabled={false}
onClick={[Function]}
style={
Object {
"marginTop": "-.35rem",
}
}
type="button"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-pencil-alt fa-w-16 mr-1"
data-icon="pencil-alt"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M497.9 142.1l-46.1 46.1c-4.7 4.7-12.3 4.7-17 0l-111-111c-4.7-4.7-4.7-12.3 0-17l46.1-46.1c18.7-18.7 49.1-18.7 67.9 0l60.1 60.1c18.8 18.7 18.8 49.1 0 67.9zM284.2 99.8L21.6 362.4.4 483.9c-2.9 16.4 11.4 30.6 27.8 27.8l121.5-21.3 262.6-262.6c4.7-4.7 4.7-12.3 0-17l-111-111c-4.8-4.7-12.4-4.7-17.1 0zM124.1 339.9c-5.5-5.5-5.5-14.3 0-19.8l154-154c5.5-5.5 14.3-5.5 19.8 0s5.5 14.3 0 19.8l-154 154c-5.5 5.5-14.3 5.5-19.8 0zM88 424h48v36.3l-64.5 11.3-31.1-31.1L51.7 376H88v48z"
fill="currentColor"
style={Object {}}
/>
</svg>
Edit
</button>
</h2>
<p
className="mb-0"
>
<span
className="ml-auto small text-muted"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-eye fa-w-18 "
data-icon="eye"
data-prefix="far"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 576 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M288 144a110.94 110.94 0 0 0-31.24 5 55.4 55.4 0 0 1 7.24 27 56 56 0 0 1-56 56 55.4 55.4 0 0 1-27-7.24A111.71 111.71 0 1 0 288 144zm284.52 97.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400c-98.65 0-189.09-55-237.93-144C98.91 167 189.34 112 288 112s189.09 55 237.93 144C477.1 345 386.66 400 288 400z"
fill="currentColor"
style={Object {}}
/>
</svg>
Everyone on edX
</span>
</p>
</div>
<p
className="h5"
data-hj-suppress={true}
>
Montenegro
</p>
</div>
</div>
<div
className="pgn-transition-replace-group position-relative mb-5"
style={
Object {
"height": null,
}
}
>
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
className="editable-item-header mb-2"
>
<h2
className="edit-section-header"
id={null}
>
Primary Language Spoken
<button
className="float-right px-0 btn btn-link btn-sm"
disabled={false}
onClick={[Function]}
style={
Object {
"marginTop": "-.35rem",
}
}
type="button"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-pencil-alt fa-w-16 mr-1"
data-icon="pencil-alt"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M497.9 142.1l-46.1 46.1c-4.7 4.7-12.3 4.7-17 0l-111-111c-4.7-4.7-4.7-12.3 0-17l46.1-46.1c18.7-18.7 49.1-18.7 67.9 0l60.1 60.1c18.8 18.7 18.8 49.1 0 67.9zM284.2 99.8L21.6 362.4.4 483.9c-2.9 16.4 11.4 30.6 27.8 27.8l121.5-21.3 262.6-262.6c4.7-4.7 4.7-12.3 0-17l-111-111c-4.8-4.7-12.4-4.7-17.1 0zM124.1 339.9c-5.5-5.5-5.5-14.3 0-19.8l154-154c5.5-5.5 14.3-5.5 19.8 0s5.5 14.3 0 19.8l-154 154c-5.5 5.5-14.3 5.5-19.8 0zM88 424h48v36.3l-64.5 11.3-31.1-31.1L51.7 376H88v48z"
fill="currentColor"
style={Object {}}
/>
</svg>
Edit
</button>
</h2>
<p
className="mb-0"
>
<span
className="ml-auto small text-muted"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-eye fa-w-18 "
data-icon="eye"
data-prefix="far"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 576 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M288 144a110.94 110.94 0 0 0-31.24 5 55.4 55.4 0 0 1 7.24 27 56 56 0 0 1-56 56 55.4 55.4 0 0 1-27-7.24A111.71 111.71 0 1 0 288 144zm284.52 97.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400c-98.65 0-189.09-55-237.93-144C98.91 167 189.34 112 288 112s189.09 55 237.93 144C477.1 345 386.66 400 288 400z"
fill="currentColor"
style={Object {}}
/>
</svg>
Everyone on edX
</span>
</p>
</div>
<p
className="h5"
data-hj-suppress={true}
>
Yoruba
</p>
</div>
</div>
<div
className="pgn-transition-replace-group position-relative mb-5"
style={
Object {
"height": null,
}
}
>
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
className="editable-item-header mb-2"
>
<h2
className="edit-section-header"
id={null}
>
Education
<button
className="float-right px-0 btn btn-link btn-sm"
disabled={false}
onClick={[Function]}
style={
Object {
"marginTop": "-.35rem",
}
}
type="button"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-pencil-alt fa-w-16 mr-1"
data-icon="pencil-alt"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M497.9 142.1l-46.1 46.1c-4.7 4.7-12.3 4.7-17 0l-111-111c-4.7-4.7-4.7-12.3 0-17l46.1-46.1c18.7-18.7 49.1-18.7 67.9 0l60.1 60.1c18.8 18.7 18.8 49.1 0 67.9zM284.2 99.8L21.6 362.4.4 483.9c-2.9 16.4 11.4 30.6 27.8 27.8l121.5-21.3 262.6-262.6c4.7-4.7 4.7-12.3 0-17l-111-111c-4.8-4.7-12.4-4.7-17.1 0zM124.1 339.9c-5.5-5.5-5.5-14.3 0-19.8l154-154c5.5-5.5 14.3-5.5 19.8 0s5.5 14.3 0 19.8l-154 154c-5.5 5.5-14.3 5.5-19.8 0zM88 424h48v36.3l-64.5 11.3-31.1-31.1L51.7 376H88v48z"
fill="currentColor"
style={Object {}}
/>
</svg>
Edit
</button>
</h2>
<p
className="mb-0"
>
<span
className="ml-auto small text-muted"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-eye-slash fa-w-20 "
data-icon="eye-slash"
data-prefix="far"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 640 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M634 471L36 3.51A16 16 0 0 0 13.51 6l-10 12.49A16 16 0 0 0 6 41l598 467.49a16 16 0 0 0 22.49-2.49l10-12.49A16 16 0 0 0 634 471zM296.79 146.47l134.79 105.38C429.36 191.91 380.48 144 320 144a112.26 112.26 0 0 0-23.21 2.47zm46.42 219.07L208.42 260.16C210.65 320.09 259.53 368 320 368a113 113 0 0 0 23.21-2.46zM320 112c98.65 0 189.09 55 237.93 144a285.53 285.53 0 0 1-44 60.2l37.74 29.5a333.7 333.7 0 0 0 52.9-75.11 32.35 32.35 0 0 0 0-29.19C550.29 135.59 442.93 64 320 64c-36.7 0-71.71 7-104.63 18.81l46.41 36.29c18.94-4.3 38.34-7.1 58.22-7.1zm0 288c-98.65 0-189.08-55-237.93-144a285.47 285.47 0 0 1 44.05-60.19l-37.74-29.5a333.6 333.6 0 0 0-52.89 75.1 32.35 32.35 0 0 0 0 29.19C89.72 376.41 197.08 448 320 448c36.7 0 71.71-7.05 104.63-18.81l-46.41-36.28C359.28 397.2 339.89 400 320 400z"
fill="currentColor"
style={Object {}}
/>
</svg>
Just me
</span>
</p>
</div>
<p
className="h5"
data-hj-suppress={true}
>
Elementary/primary school
</p>
</div>
</div>
<div
className="pgn-transition-replace-group position-relative mb-5"
style={
Object {
"height": null,
}
}
>
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
className="editable-item-header mb-2"
>
<h2
className="edit-section-header"
id={null}
>
Social Links
<button
className="float-right px-0 btn btn-link btn-sm"
disabled={false}
onClick={[Function]}
style={
Object {
"marginTop": "-.35rem",
}
}
type="button"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-pencil-alt fa-w-16 mr-1"
data-icon="pencil-alt"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M497.9 142.1l-46.1 46.1c-4.7 4.7-12.3 4.7-17 0l-111-111c-4.7-4.7-4.7-12.3 0-17l46.1-46.1c18.7-18.7 49.1-18.7 67.9 0l60.1 60.1c18.8 18.7 18.8 49.1 0 67.9zM284.2 99.8L21.6 362.4.4 483.9c-2.9 16.4 11.4 30.6 27.8 27.8l121.5-21.3 262.6-262.6c4.7-4.7 4.7-12.3 0-17l-111-111c-4.8-4.7-12.4-4.7-17.1 0zM124.1 339.9c-5.5-5.5-5.5-14.3 0-19.8l154-154c5.5-5.5 14.3-5.5 19.8 0s5.5 14.3 0 19.8l-154 154c-5.5 5.5-14.3 5.5-19.8 0zM88 424h48v36.3l-64.5 11.3-31.1-31.1L51.7 376H88v48z"
fill="currentColor"
style={Object {}}
/>
</svg>
Edit
</button>
</h2>
<p
className="mb-0"
>
<span
className="ml-auto small text-muted"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-eye fa-w-18 "
data-icon="eye"
data-prefix="far"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 576 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M288 144a110.94 110.94 0 0 0-31.24 5 55.4 55.4 0 0 1 7.24 27 56 56 0 0 1-56 56 55.4 55.4 0 0 1-27-7.24A111.71 111.71 0 1 0 288 144zm284.52 97.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400c-98.65 0-189.09-55-237.93-144C98.91 167 189.34 112 288 112s189.09 55 237.93 144C477.1 345 386.66 400 288 400z"
fill="currentColor"
style={Object {}}
/>
</svg>
Everyone on edX
</span>
</p>
</div>
<ul
className="list-unstyled"
>
<li
className="form-group"
>
<a
className="font-weight-bold"
href="https://www.twitter.com/ALOHA"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-twitter fa-w-16 mr-2"
data-icon="twitter"
data-prefix="fab"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"
fill="currentColor"
style={Object {}}
/>
</svg>
Twitter
</a>
</li>
<li
className="form-group"
>
<a
className="font-weight-bold"
href="https://www.facebook.com/aloha"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-facebook fa-w-14 mr-2"
data-icon="facebook"
data-prefix="fab"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 448 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M448 56.7v398.5c0 13.7-11.1 24.7-24.7 24.7H309.1V306.5h58.2l8.7-67.6h-67v-43.2c0-19.6 5.4-32.9 33.5-32.9h35.8v-60.5c-6.2-.8-27.4-2.7-52.2-2.7-51.6 0-87 31.5-87 89.4v49.9h-58.4v67.6h58.4V480H24.7C11.1 480 0 468.9 0 455.3V56.7C0 43.1 11.1 32 24.7 32h398.5c13.7 0 24.8 11.1 24.8 24.7z"
fill="currentColor"
style={Object {}}
/>
</svg>
Facebook
</a>
</li>
<li
className="form-group"
>
<div>
<button
className="pl-0 text-left btn btn-link"
onClick={[Function]}
onKeyDown={[Function]}
tabIndex={0}
type="button"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-plus fa-w-14 fa-xs mr-2"
data-icon="plus"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 448 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M416 208H272V64c0-17.67-14.33-32-32-32h-32c-17.67 0-32 14.33-32 32v144H32c-17.67 0-32 14.33-32 32v32c0 17.67 14.33 32 32 32h144v144c0 17.67 14.33 32 32 32h32c17.67 0 32-14.33 32-32V304h144c17.67 0 32-14.33 32-32v-32c0-17.67-14.33-32-32-32z"
fill="currentColor"
style={Object {}}
/>
</svg>
Add
LinkedIn
</button>
</div>
</li>
</ul>
</div>
</div>
</div>
<div
className="pt-md-3 col-md-8 col-lg-7 offset-lg-1"
>
<div
className="pgn-transition-replace-group position-relative mb-5"
style={
Object {
"height": null,
}
}
>
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
className="editable-item-header mb-2"
>
<h2
className="edit-section-header"
id={null}
>
About Me
<button
className="float-right px-0 btn btn-link btn-sm"
disabled={false}
onClick={[Function]}
style={
Object {
"marginTop": "-.35rem",
}
}
type="button"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-pencil-alt fa-w-16 mr-1"
data-icon="pencil-alt"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M497.9 142.1l-46.1 46.1c-4.7 4.7-12.3 4.7-17 0l-111-111c-4.7-4.7-4.7-12.3 0-17l46.1-46.1c18.7-18.7 49.1-18.7 67.9 0l60.1 60.1c18.8 18.7 18.8 49.1 0 67.9zM284.2 99.8L21.6 362.4.4 483.9c-2.9 16.4 11.4 30.6 27.8 27.8l121.5-21.3 262.6-262.6c4.7-4.7 4.7-12.3 0-17l-111-111c-4.8-4.7-12.4-4.7-17.1 0zM124.1 339.9c-5.5-5.5-5.5-14.3 0-19.8l154-154c5.5-5.5 14.3-5.5 19.8 0s5.5 14.3 0 19.8l-154 154c-5.5 5.5-14.3 5.5-19.8 0zM88 424h48v36.3l-64.5 11.3-31.1-31.1L51.7 376H88v48z"
fill="currentColor"
style={Object {}}
/>
</svg>
Edit
</button>
</h2>
<p
className="mb-0"
>
<span
className="ml-auto small text-muted"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-eye fa-w-18 "
data-icon="eye"
data-prefix="far"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 576 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M288 144a110.94 110.94 0 0 0-31.24 5 55.4 55.4 0 0 1 7.24 27 56 56 0 0 1-56 56 55.4 55.4 0 0 1-27-7.24A111.71 111.71 0 1 0 288 144zm284.52 97.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400c-98.65 0-189.09-55-237.93-144C98.91 167 189.34 112 288 112s189.09 55 237.93 144C477.1 345 386.66 400 288 400z"
fill="currentColor"
style={Object {}}
/>
</svg>
Everyone on edX
</span>
</p>
</div>
<p
className="lead"
data-hj-suppress={true}
>
This is my bio
</p>
</div>
</div>
<div
className="pgn-transition-replace-group position-relative mb-4"
style={
Object {
"height": null,
}
}
>
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
className="editable-item-header mb-2"
>
<h2
className="edit-section-header"
id={null}
>
My Certificates
<button
className="float-right px-0 btn btn-link btn-sm"
disabled={false}
onClick={[Function]}
style={
Object {
"marginTop": "-.35rem",
}
}
type="button"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-pencil-alt fa-w-16 mr-1"
data-icon="pencil-alt"
data-prefix="fas"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M497.9 142.1l-46.1 46.1c-4.7 4.7-12.3 4.7-17 0l-111-111c-4.7-4.7-4.7-12.3 0-17l46.1-46.1c18.7-18.7 49.1-18.7 67.9 0l60.1 60.1c18.8 18.7 18.8 49.1 0 67.9zM284.2 99.8L21.6 362.4.4 483.9c-2.9 16.4 11.4 30.6 27.8 27.8l121.5-21.3 262.6-262.6c4.7-4.7 4.7-12.3 0-17l-111-111c-4.8-4.7-12.4-4.7-17.1 0zM124.1 339.9c-5.5-5.5-5.5-14.3 0-19.8l154-154c5.5-5.5 14.3-5.5 19.8 0s5.5 14.3 0 19.8l-154 154c-5.5 5.5-14.3 5.5-19.8 0zM88 424h48v36.3l-64.5 11.3-31.1-31.1L51.7 376H88v48z"
fill="currentColor"
style={Object {}}
/>
</svg>
Edit
</button>
</h2>
<p
className="mb-0"
>
<span
className="ml-auto small text-muted"
>
<svg
aria-hidden="true"
className="svg-inline--fa fa-eye fa-w-18 "
data-icon="eye"
data-prefix="far"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 576 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M288 144a110.94 110.94 0 0 0-31.24 5 55.4 55.4 0 0 1 7.24 27 56 56 0 0 1-56 56 55.4 55.4 0 0 1-27-7.24A111.71 111.71 0 1 0 288 144zm284.52 97.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400c-98.65 0-189.09-55-237.93-144C98.91 167 189.34 112 288 112s189.09 55 237.93 144C477.1 345 386.66 400 288 400z"
fill="currentColor"
style={Object {}}
/>
</svg>
Everyone on edX
</span>
</p>
</div>
<div
className="row align-items-stretch"
>
<div
className="col col-sm-6 d-flex align-items-stretch"
>
<div
className="card mb-4 certificate flex-grow-1"
>
<div
className="certificate-type-illustration"
style={
Object {
"backgroundImage": "url(icon/mock/path)",
}
}
/>
<div
className="card-body d-flex flex-column"
>
<div
className="card-title"
>
<p
className="small mb-0"
>
Verified Certificate
</p>
<h4
className="certificate-title"
>
edX Demonstration Course
</h4>
</div>
<p
className="small mb-0"
>
<span>
From
</span>
</p>
<p
className="h6 mb-4"
>
edX
</p>
<div
className="flex-grow-1"
/>
<p
className="small mb-2"
>
<span>
Completed on
<span>
3/4/2019
</span>
</span>
</p>
<div>
<a
className="btn btn-outline-primary"
href="http://www.example.com/"
onClick={[Function]}
rel="noopener"
target="_blank"
>
View Certificate
<span
className="d-inline-block align-text-top"
>
<span
className="pgn__icon"
style={
Object {
"height": "1em",
"width": "1em",
}
}
>
<svg
aria-hidden={true}
aria-label=""
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
fill="currentColor"
/>
</svg>
</span>
</span>
</a>
</div>

View File

@@ -92,7 +92,6 @@ describe('SAVE profile actions', () => {
});
});
describe('SAVE profile photo actions', () => {
it('should create an action to signal the start of a profile photo save', () => {
const formData = 'multipart form data';
@@ -141,7 +140,6 @@ describe('SAVE profile photo actions', () => {
});
});
describe('DELETE profile photo actions', () => {
it('should create an action to signal the start of a profile photo deletion', () => {
const expectedAction = {
@@ -179,7 +177,6 @@ describe('DELETE profile photo actions', () => {
});
});
describe('Editable field opening and closing actions', () => {
const formId = 'name';

View File

@@ -57,13 +57,13 @@ const profilePage = (state = initialState, action) => {
// Account is always replaced completely.
account: action.payload.account !== null ? action.payload.account : state.account,
// Preferences changes get merged in.
preferences: Object.assign({}, state.preferences, action.payload.preferences),
preferences: { ...state.preferences, ...action.payload.preferences },
};
case SAVE_PROFILE.FAILURE:
return {
...state,
saveState: 'error',
errors: Object.assign({}, state.errors, action.payload.errors),
errors: { ...state.errors, ...action.payload.errors },
};
case SAVE_PROFILE.RESET:
return {
@@ -82,7 +82,7 @@ const profilePage = (state = initialState, action) => {
return {
...state,
// Merge in new profile image data
account: Object.assign({}, state.account, { profileImage: action.payload.profileImage }),
account: { ...state.account, profileImage: action.payload.profileImage },
savePhotoState: 'complete',
errors: {},
};
@@ -90,7 +90,7 @@ const profilePage = (state = initialState, action) => {
return {
...state,
savePhotoState: 'error',
errors: Object.assign({}, state.errors, { photo: action.payload.error }),
errors: { ...state.errors, photo: action.payload.error },
};
case SAVE_PROFILE_PHOTO.RESET:
return {
@@ -109,7 +109,7 @@ const profilePage = (state = initialState, action) => {
return {
...state,
// Merge in new profile image data (should be empty or default image)
account: Object.assign({}, state.account, { profileImage: action.payload.profileImage }),
account: { ...state.account, profileImage: action.payload.profileImage },
savePhotoState: 'complete',
errors: {},
};
@@ -117,7 +117,7 @@ const profilePage = (state = initialState, action) => {
return {
...state,
savePhotoState: 'error',
errors: Object.assign({}, state.errors, action.payload.errors),
errors: { ...state.errors, ...action.payload.errors },
};
case DELETE_PROFILE_PHOTO.RESET:
return {
@@ -129,9 +129,7 @@ const profilePage = (state = initialState, action) => {
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 },
};
case RESET_DRAFTS:

View File

@@ -1,7 +1,14 @@
import { history } from '@edx/frontend-platform';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
import pick from 'lodash.pick';
import { all, call, delay, put, select, takeEvery } from 'redux-saga/effects';
import {
all,
call,
delay,
put,
select,
takeEvery,
} from 'redux-saga/effects';
import {
closeForm,
deleteProfilePhotoBegin,

View File

@@ -1,4 +1,11 @@
import { takeEvery, put, call, delay, select, all } from 'redux-saga/effects';
import {
takeEvery,
put,
call,
delay,
select,
all,
} from 'redux-saga/effects';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
import * as profileActions from './actions';

View File

@@ -22,8 +22,7 @@ export const savePhotoStateSelector = state => state.profilePage.savePhotoState;
export const isLoadingProfileSelector = state => state.profilePage.isLoadingProfile;
export const currentlyEditingFieldSelector = state => state.profilePage.currentlyEditingField;
export const accountErrorsSelector = state => state.profilePage.errors;
export const isAuthenticatedUserProfileSelector = state =>
state.profilePage.isAuthenticatedUserProfile;
export const isAuthenticatedUserProfileSelector = state => state.profilePage.isAuthenticatedUserProfile;
export const editableFormModeSelector = createSelector(
profileAccountSelector,
@@ -147,13 +146,12 @@ export const certificatesSelector = createSelector(
export const profileImageSelector = createSelector(
profileAccountSelector,
account =>
(account.profileImage != null
? {
src: account.profileImage.imageUrlFull,
isDefault: !account.profileImage.hasImage,
}
: {}),
account => (account.profileImage != null
? {
src: account.profileImage.imageUrlFull,
isDefault: !account.profileImage.hasImage,
}
: {}),
);
/**

View File

@@ -121,12 +121,12 @@ function transformCertificateData(data) {
data.forEach((cert) => {
// download_url may be full url or absolute path.
// note: using the URL() api breaks in ie 11
const urlIsPath = typeof cert.download_url === 'string' &&
cert.download_url.search(/http[s]?:\/\//) !== 0;
const urlIsPath = typeof cert.download_url === 'string'
&& cert.download_url.search(/http[s]?:\/\//) !== 0;
const downloadUrl = urlIsPath ?
`${getConfig().LMS_BASE_URL}${cert.download_url}` :
cert.download_url;
const downloadUrl = urlIsPath
? `${getConfig().LMS_BASE_URL}${cert.download_url}`
: cert.download_url;
transformedData.push({
...camelCaseObject(cert),

View File

@@ -83,7 +83,7 @@ class Bio extends React.Component {
</div>
),
editable: (
<React.Fragment>
<>
<EditableItemHeader
content={intl.formatMessage(messages['profile.bio.about.me'])}
showEditButton
@@ -91,11 +91,11 @@ class Bio extends React.Component {
showVisibility={visibilityBio !== null}
visibility={visibilityBio}
/>
<p className="lead">{bio}</p>
</React.Fragment>
<p data-hj-suppress className="lead">{bio}</p>
</>
),
empty: (
<React.Fragment>
<>
<EditableItemHeader content={intl.formatMessage(messages['profile.bio.about.me'])} />
<EmptyContent onClick={this.handleOpen}>
<FormattedMessage
@@ -104,13 +104,13 @@ class Bio extends React.Component {
description="instructions when the user hasn't written an About Me"
/>
</EmptyContent>
</React.Fragment>
</>
),
static: (
<React.Fragment>
<>
<EditableItemHeader content={intl.formatMessage(messages['profile.bio.about.me'])} />
<p className="lead">{bio}</p>
</React.Fragment>
<p data-hj-suppress className="lead">{bio}</p>
</>
),
}}
/>

View File

@@ -1,6 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedDate, FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import {
FormattedDate, FormattedMessage, injectIntl, intlShape,
} from '@edx/frontend-platform/i18n';
import { Hyperlink } from '@edx/paragon';
import { connect } from 'react-redux';
import get from 'lodash.get';
@@ -113,11 +115,13 @@ class Certificates extends React.Component {
renderCertificates() {
if (this.props.certificates === null || this.props.certificates.length === 0) {
return (<FormattedMessage
id="profile.no.certificates"
defaultMessage="You don't have any certificates yet."
description="displays when user has no course completion certificates"
/>);
return (
<FormattedMessage
id="profile.no.certificates"
defaultMessage="You don't have any certificates yet."
description="displays when user has no course completion certificates"
/>
);
}
return (
@@ -154,7 +158,7 @@ class Certificates extends React.Component {
</div>
),
editable: (
<React.Fragment>
<>
<EditableItemHeader
content={intl.formatMessage(messages['profile.certificates.my.certificates'])}
showEditButton
@@ -163,10 +167,10 @@ class Certificates extends React.Component {
visibility={visibilityCourseCertificates}
/>
{this.renderCertificates()}
</React.Fragment>
</>
),
empty: (
<React.Fragment>
<>
<EditableItemHeader
content={intl.formatMessage(messages['profile.certificates.my.certificates'])}
showEditButton
@@ -175,13 +179,13 @@ class Certificates extends React.Component {
visibility={visibilityCourseCertificates}
/>
{this.renderCertificates()}
</React.Fragment>
</>
),
static: (
<React.Fragment>
<>
<EditableItemHeader content={intl.formatMessage(messages['profile.certificates.my.certificates'])} />
{this.renderCertificates()}
</React.Fragment>
</>
),
}}
/>

View File

@@ -76,6 +76,7 @@ class Country extends React.Component {
{intl.formatMessage(messages['profile.country.label'])}
</label>
<select
data-hj-suppress
className="form-control"
type="select"
id={formId}
@@ -83,7 +84,7 @@ class Country extends React.Component {
value={country}
onChange={this.handleChange}
>
<option value="" />
<option value="">&nbsp;</option>
{sortedCountries.map(({ code, name }) => (
<option key={code} value={code}>{name}</option>
))}
@@ -100,7 +101,7 @@ class Country extends React.Component {
</div>
),
editable: (
<React.Fragment>
<>
<EditableItemHeader
content={intl.formatMessage(messages['profile.country.label'])}
showEditButton
@@ -108,26 +109,26 @@ class Country extends React.Component {
showVisibility={visibilityCountry !== null}
visibility={visibilityCountry}
/>
<p className="h5">{countryMessages[country]}</p>
</React.Fragment>
<p data-hj-suppress className="h5">{countryMessages[country]}</p>
</>
),
empty: (
<React.Fragment>
<>
<EditableItemHeader
content={intl.formatMessage(messages['profile.country.label'])}
/>
<EmptyContent onClick={this.handleOpen}>
{intl.formatMessage(messages['profile.country.empty'])}
</EmptyContent>
</React.Fragment>
</>
),
static: (
<React.Fragment>
<>
<EditableItemHeader
content={intl.formatMessage(messages['profile.country.label'])}
/>
<p className="h5">{countryMessages[country]}</p>
</React.Fragment>
<p data-hj-suppress className="h5">{countryMessages[country]}</p>
</>
),
}}
/>

View File

@@ -72,13 +72,14 @@ class Education extends React.Component {
{intl.formatMessage(messages['profile.education.education'])}
</label>
<select
data-hj-suppress
className="form-control"
id={formId}
name={formId}
value={levelOfEducation}
onChange={this.handleChange}
>
<option value="" />
<option value="">&nbsp;</option>
{EDUCATION_LEVELS.map(level => (
<option key={level} value={level}>
{intl.formatMessage(get(
@@ -101,7 +102,7 @@ class Education extends React.Component {
</div>
),
editable: (
<React.Fragment>
<>
<EditableItemHeader
content={intl.formatMessage(messages['profile.education.education'])}
showEditButton
@@ -109,17 +110,17 @@ class Education extends React.Component {
showVisibility={visibilityLevelOfEducation !== null}
visibility={visibilityLevelOfEducation}
/>
<p className="h5">
<p data-hj-suppress className="h5">
{intl.formatMessage(get(
messages,
`profile.education.levels.${levelOfEducation}`,
messages['profile.education.levels.o'],
))}
</p>
</React.Fragment>
</>
),
empty: (
<React.Fragment>
<>
<EditableItemHeader content={intl.formatMessage(messages['profile.education.education'])} />
<EmptyContent onClick={this.handleOpen}>
<FormattedMessage
@@ -128,19 +129,19 @@ class Education extends React.Component {
description="instructions when the user doesn't have their level of education set"
/>
</EmptyContent>
</React.Fragment>
</>
),
static: (
<React.Fragment>
<>
<EditableItemHeader content={intl.formatMessage(messages['profile.education.education'])} />
<p className="h5">
<p data-hj-suppress className="h5">
{intl.formatMessage(get(
messages,
`profile.education.levels.${levelOfEducation}`,
messages['profile.education.levels.o'],
))}
</p>
</React.Fragment>
</>
),
}}
/>

View File

@@ -68,7 +68,7 @@ class Name extends React.Component {
Once we're super sure we don't want it back, you could delete the name props and
such to fully get rid of it.
*/}
<p className="h5">{name}</p>
<p data-hj-suppress className="h5">{name}</p>
<small className="form-text text-muted" id={`${formId}-help-text`}>
{intl.formatMessage(messages['profile.name.details'])}
</small>
@@ -84,7 +84,7 @@ class Name extends React.Component {
</div>
),
editable: (
<React.Fragment>
<>
<EditableItemHeader
content={intl.formatMessage(messages['profile.name.full.name'])}
showEditButton
@@ -92,14 +92,14 @@ class Name extends React.Component {
showVisibility={visibilityName !== null}
visibility={visibilityName}
/>
<p className="h5">{name}</p>
<p data-hj-suppress className="h5">{name}</p>
<small className="form-text text-muted">
{intl.formatMessage(messages['profile.name.details'])}
</small>
</React.Fragment>
</>
),
empty: (
<React.Fragment>
<>
<EditableItemHeader content={intl.formatMessage(messages['profile.name.full.name'])} />
<EmptyContent onClick={this.handleOpen}>
{intl.formatMessage(messages['profile.name.empty'])}
@@ -107,13 +107,13 @@ class Name extends React.Component {
<small className="form-text text-muted">
{intl.formatMessage(messages['profile.name.details'])}
</small>
</React.Fragment>
</>
),
static: (
<React.Fragment>
<>
<EditableItemHeader content={intl.formatMessage(messages['profile.name.full.name'])} />
<p className="h5">{name}</p>
</React.Fragment>
<p data-hj-suppress className="h5">{name}</p>
</>
),
}}
/>

View File

@@ -86,13 +86,14 @@ class PreferredLanguage extends React.Component {
{intl.formatMessage(messages['profile.preferredlanguage.label'])}
</label>
<select
data-hj-suppress
id={formId}
name={formId}
className="form-control"
value={value}
onChange={this.handleChange}
>
<option value="" />
<option value="">&nbsp;</option>
{sortedLanguages.map(({ code, name }) => (
<option key={code} value={code}>{name}</option>
))}
@@ -109,7 +110,7 @@ class PreferredLanguage extends React.Component {
</div>
),
editable: (
<React.Fragment>
<>
<EditableItemHeader
content={intl.formatMessage(messages['profile.preferredlanguage.label'])}
showEditButton
@@ -117,26 +118,26 @@ class PreferredLanguage extends React.Component {
showVisibility={visibilityLanguageProficiencies !== null}
visibility={visibilityLanguageProficiencies}
/>
<p className="h5">{languageMessages[value]}</p>
</React.Fragment>
<p data-hj-suppress className="h5">{languageMessages[value]}</p>
</>
),
empty: (
<React.Fragment>
<>
<EditableItemHeader
content={intl.formatMessage(messages['profile.preferredlanguage.label'])}
/>
<EmptyContent onClick={this.handleOpen}>
{intl.formatMessage(messages['profile.preferredlanguage.empty'])}
</EmptyContent>
</React.Fragment>
</>
),
static: (
<React.Fragment>
<>
<EditableItemHeader
content={intl.formatMessage(messages['profile.preferredlanguage.label'])}
/>
<p className="h5">{languageMessages[value]}</p>
</React.Fragment>
<p data-hj-suppress className="h5">{languageMessages[value]}</p>
</>
),
}}
/>

View File

@@ -33,7 +33,9 @@ class ProfileAvatar extends React.Component {
}
onSubmit(e) {
if (e) e.preventDefault();
if (e) {
e.preventDefault();
}
this.props.onSave(new FormData(this.form.current));
this.form.current.reset();
}
@@ -55,7 +57,9 @@ class ProfileAvatar extends React.Component {
if (this.props.isDefault) {
return (
<Button
className="text-white btn-block btn-sm btn-link"
variant="link"
size="sm"
className="text-white btn-block"
onClick={this.onClickUpload}
>
<FormattedMessage
@@ -69,9 +73,9 @@ class ProfileAvatar extends React.Component {
return (
<Dropdown>
<Dropdown.Button type="btn-outline">
<Dropdown.Toggle>
{intl.formatMessage(messages['profile.profileavatar.change-button'])}
</Dropdown.Button>
</Dropdown.Toggle>
<Dropdown.Menu>
<Dropdown.Item type="button" onClick={this.onClickUpload}>
<FormattedMessage
@@ -93,7 +97,9 @@ class ProfileAvatar extends React.Component {
}
renderMenu() {
if (!this.props.isEditable) return null;
if (!this.props.isEditable) {
return null;
}
return (
<div className="profile-avatar-menu-container">
@@ -109,6 +115,7 @@ class ProfileAvatar extends React.Component {
<DefaultAvatar className="text-muted" role="img" aria-hidden focusable="false" viewBox="0 0 24 24" />
) : (
<img
data-hj-suppress
className="w-100 h-100 d-block rounded-circle overflow-hidden"
style={{ objectFit: 'cover' }}
alt={intl.formatMessage(messages['profile.image.alt.attribute'])}

View File

@@ -102,7 +102,7 @@ class SocialLinks extends React.Component {
expression={editMode}
cases={{
empty: (
<React.Fragment>
<>
<EditableItemHeader content={intl.formatMessage(messages['profile.sociallinks.social.links'])} />
<ul className="list-unstyled">
{socialLinks.map(({ platform }) => (
@@ -113,10 +113,10 @@ class SocialLinks extends React.Component {
/>
))}
</ul>
</React.Fragment>
</>
),
static: (
<React.Fragment>
<>
<EditableItemHeader
content={intl.formatMessage(messages['profile.sociallinks.social.links'])}
/>
@@ -130,13 +130,12 @@ class SocialLinks extends React.Component {
url={socialLink}
platform={platform}
/>
))
}
))}
</ul>
</React.Fragment>
</>
),
editable: (
<React.Fragment>
<>
<EditableItemHeader
content={intl.formatMessage(messages['profile.sociallinks.social.links'])}
showEditButton
@@ -155,7 +154,7 @@ class SocialLinks extends React.Component {
/>
))}
</ul>
</React.Fragment>
</>
),
editing: (
<div role="dialog" aria-labelledby="social-links-label">

View File

@@ -1,6 +1,5 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faPencilAlt } from '@fortawesome/free-solid-svg-icons';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
@@ -13,7 +12,9 @@ function EditButton({
}) {
return (
<Button
className={classNames('btn-sm btn-link', className)}
variant="link"
size="sm"
className={className}
onClick={onClick}
style={style}
>

View File

@@ -13,7 +13,7 @@ function EditableItemHeader({
headingId,
}) {
return (
<React.Fragment>
<>
<div className="editable-item-header mb-2">
<h2 className="edit-section-header" id={headingId}>
{content}
@@ -21,7 +21,7 @@ function EditableItemHeader({
</h2>
{showVisibility ? <p className="mb-0"><Visibility to={visibility} /></p> : null}
</div>
</React.Fragment>
</>
);
}

View File

@@ -8,9 +8,10 @@ function EmptyContent({ children, onClick, showPlusIcon }) {
<div>
{onClick ? (
<button
type="button"
className="pl-0 text-left btn btn-link"
onClick={onClick}
onKeyDown={(e) => { if (e.key === 'Enter') onClick(); }}
onKeyDown={(e) => { if (e.key === 'Enter') { onClick(); } }}
tabIndex={0}
>
{showPlusIcon ? <FontAwesomeIcon size="xs" className="mr-2" icon={faPlus} /> : null}
@@ -21,7 +22,6 @@ function EmptyContent({ children, onClick, showPlusIcon }) {
);
}
export default EmptyContent;
EmptyContent.propTypes = {

View File

@@ -31,7 +31,6 @@ function FormControls({
<div className="form-group flex-shrink-0 flex-grow-1">
<StatefulButton
type="submit"
className="btn-primary"
state={buttonState}
labels={{
default: intl.formatMessage(messages['profile.formcontrols.button.save']),
@@ -46,11 +45,13 @@ function FormControls({
// 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 (buttonState === 'pending') e.preventDefault();
if (buttonState === 'pending') {
e.preventDefault();
}
}}
disabledStates={[]}
/>
<Button className="btn-link" onClick={cancelHandler}>
<Button variant="link" onClick={cancelHandler}>
{intl.formatMessage(messages['profile.formcontrols.button.cancel'])}
</Button>
</div>

View File

@@ -2,7 +2,6 @@ import React from 'react';
import PropTypes from 'prop-types';
import { TransitionReplace } from '@edx/paragon';
const onChildExit = (htmlNode) => {
// If the leaving child has focus, take control and redirect it
if (htmlNode.contains(document.activeElement)) {
@@ -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,7 +22,6 @@ const onChildExit = (htmlNode) => {
}
};
function SwitchContent({ expression, cases, className }) {
const getContent = (caseKey) => {
if (cases[caseKey]) {
@@ -29,7 +29,8 @@ function SwitchContent({ expression, cases, className }) {
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);
}
@@ -49,7 +50,6 @@ function SwitchContent({ expression, cases, className }) {
);
}
SwitchContent.propTypes = {
expression: PropTypes.string,
cases: PropTypes.objectOf(PropTypes.node).isRequired,
@@ -61,5 +61,4 @@ SwitchContent.defaultProps = {
className: null,
};
export default SwitchContent;

View File

@@ -6,12 +6,11 @@ import { faEyeSlash, faEye } from '@fortawesome/free-regular-svg-icons';
import messages from './Visibility.messages';
function Visibility({ to, intl }) {
const icon = to === 'private' ? faEyeSlash : faEye;
const label = to === 'private' ?
intl.formatMessage(messages['profile.visibility.who.just.me']) :
intl.formatMessage(messages['profile.visibility.who.everyone']);
const label = to === 'private'
? intl.formatMessage(messages['profile.visibility.who.just.me'])
: intl.formatMessage(messages['profile.visibility.who.everyone']);
return (
<span className="ml-auto small text-muted">
@@ -30,7 +29,6 @@ Visibility.defaultProps = {
to: 'private',
};
function VisibilitySelect({ intl, className, ...props }) {
const { value } = props;
const icon = value === 'private' ? faEyeSlash : faEye;
@@ -73,7 +71,6 @@ VisibilitySelect.defaultProps = {
const intlVisibility = injectIntl(Visibility);
const intlVisibilitySelect = injectIntl(VisibilitySelect);
export {
intlVisibility as Visibility,
intlVisibilitySelect as VisibilitySelect,

View File

@@ -1,8 +1,3 @@
$fa-font-path: "~font-awesome/fonts";
@import "~font-awesome/scss/font-awesome";
.word-break-all {
word-break: break-all !important;
}
@@ -36,9 +31,11 @@ $fa-font-path: "~font-awesome/fonts";
letter-spacing: 0;
margin: 0;
}
label.edit-section-header {
margin-bottom: $spacer * .5;
}
.profile-avatar-wrap {
@include media-breakpoint-up(md) {
max-width: 12rem;
@@ -57,10 +54,12 @@ $fa-font-path: "~font-awesome/fonts";
justify-content: center;
align-items: center;
border-radius: 50%;
@include media-breakpoint-up(md) {
background: linear-gradient(to top, rgba(0,0,0,.65) 4rem, rgba(0,0,0,0) 4rem);
align-items: flex-end;
}
.btn {
text-decoration: none;
@include media-breakpoint-up(md) {
@@ -72,6 +71,7 @@ $fa-font-path: "~font-awesome/fonts";
@include media-breakpoint-up(md) {
margin-bottom: 1.2rem;
}
.btn {
color: $white;
background: transparent;
@@ -118,10 +118,12 @@ $fa-font-path: "~font-awesome/fonts";
.certificate {
position: relative;
.certificate-title {
font-family: $font-family-serif;
font-weight: 400;
}
.certificate-type-illustration {
position: absolute;
top: 1rem;
@@ -133,9 +135,9 @@ $fa-font-path: "~font-awesome/fonts";
background-repeat: no-repeat;
background-position: right top;
}
.card-body {
position: relative;
}
}
}

View File

@@ -4,9 +4,9 @@ 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))
object === undefined
|| object === null
|| (typeof object !== 'object' && !Array.isArray(object))
) {
return object;
}

View File

@@ -1,4 +1,10 @@
import { AsyncActionType, modifyObjectKeys, camelCaseObject, snakeCaseObject, convertKeyNames } from './utils';
import {
AsyncActionType,
modifyObjectKeys,
camelCaseObject,
snakeCaseObject,
convertKeyNames,
} from './utils';
describe('modifyObjectKeys', () => {
it('should use the provided modify function to change all keys in and object and its children', () => {

View File

@@ -1,3 +1,6 @@
import 'core-js/stable';
import 'regenerator-runtime/runtime';
import Enzyme from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';

View File

@@ -1,21 +0,0 @@
const glob = require('glob');
const PurgecssPlugin = require('purgecss-webpack-plugin');
const { createConfig } = require('@edx/frontend-build');
module.exports = createConfig('webpack-prod', {
plugins: [
// Scan files for class names and ids and remove unused css
new PurgecssPlugin({
paths: [].concat(
// Scan files in this app
glob.sync('src/**/*', { nodir: true }),
// Scan files in any edx frontend-component
glob.sync('node_modules/@edx/frontend-component*/**/*', { nodir: true }),
// Scan files in paragon
glob.sync('node_modules/@edx/paragon/**/*', { nodir: true }),
),
// Protect react-css-transition class names
whitelistPatterns: [/-enter/, /-appear/, /-exit/],
}),
],
});