Compare commits

..

1 Commits

Author SHA1 Message Date
Diana Catalina Olarte
91cc25f50a fix: show site name instead edX 2022-05-24 08:10:53 +01:00
98 changed files with 29329 additions and 20543 deletions

6
.env
View File

@@ -22,8 +22,6 @@ LOGO_URL=''
LOGO_TRADEMARK_URL='' LOGO_TRADEMARK_URL=''
LOGO_WHITE_URL='' LOGO_WHITE_URL=''
FAVICON_URL='' FAVICON_URL=''
ENABLE_LEARNER_RECORD_MFE=''
LEARNER_RECORD_MFE_BASE_URL=''
COLLECT_YEAR_OF_BIRTH=true COLLECT_YEAR_OF_BIRTH=true
APP_ID=''
MFE_CONFIG_API_URL=''
SEARCH_CATALOG_URL=''
ENABLE_SKILLS_BUILDER_PROFILE=''

View File

@@ -23,8 +23,6 @@ LOGO_URL=https://edx-cdn.org/v3/default/logo.svg
LOGO_TRADEMARK_URL=https://edx-cdn.org/v3/default/logo-trademark.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 LOGO_WHITE_URL=https://edx-cdn.org/v3/default/logo-white.svg
FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico
ENABLE_LEARNER_RECORD_MFE=''
LEARNER_RECORD_MFE_BASE_URL='http://localhost:1990'
COLLECT_YEAR_OF_BIRTH=true COLLECT_YEAR_OF_BIRTH=true
APP_ID=''
MFE_CONFIG_API_URL=''
SEARCH_CATALOG_URL='http://localhost:18000/courses'
ENABLE_SKILLS_BUILDER_PROFILE=''

View File

@@ -18,8 +18,5 @@ LOGO_TRADEMARK_URL=https://edx-cdn.org/v3/default/logo-trademark.svg
LOGO_WHITE_URL=https://edx-cdn.org/v3/default/logo-white.svg LOGO_WHITE_URL=https://edx-cdn.org/v3/default/logo-white.svg
FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico
ENABLE_LEARNER_RECORD_MFE='' ENABLE_LEARNER_RECORD_MFE=''
ENABLE_SKILLS_BUILDER_PROFILE=''
LEARNER_RECORD_MFE_BASE_URL='http://localhost:1990' LEARNER_RECORD_MFE_BASE_URL='http://localhost:1990'
COLLECT_YEAR_OF_BIRTH=true COLLECT_YEAR_OF_BIRTH=true
APP_ID=''
MFE_CONFIG_API_URL=''

View File

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

View File

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

View File

@@ -11,19 +11,20 @@ jobs:
matrix: matrix:
npm-test: npm-test:
- i18n_extract - i18n_extract
- is-es5
- lint - lint
- test - test
node: [12, 14, 16]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v2
- name: Setup Nodejs Env - uses: actions/setup-node@v2
run: echo "NODE_VER=`cat .nvmrc`" >> $GITHUB_ENV
- uses: actions/setup-node@v3
with: with:
node-version: ${{ env.NODE_VER }} node-version: ${{ matrix.node }}
- run: npm install -g npm@8.x.x
- run: make requirements - run: make requirements
- run: make test NPM_TESTS=build - run: make test NPM_TESTS=build
- run: make test NPM_TESTS=${{ matrix.npm-test }} - run: make test NPM_TESTS=${{ matrix.npm-test }}
- name: upload coverage - name: upload coverage
uses: codecov/codecov-action@v3 uses: codecov/codecov-action@v2
with: with:
fail_ci_if_error: false fail_ci_if_error: false

View File

@@ -7,4 +7,4 @@ on:
jobs: jobs:
commitlint: commitlint:
uses: openedx/.github/.github/workflows/commitlint.yml@master uses: edx/.github/.github/workflows/commitlint.yml@master

View File

@@ -1,13 +0,0 @@
#check package-lock file version
name: Lockfile Version check
on:
push:
branches:
- master
pull_request:
jobs:
version-check:
uses: openedx/.github/.github/workflows/lockfile-check.yml@master

View File

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

View File

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

1
.nvmrc
View File

@@ -1 +0,0 @@
18

29
Makefile Normal file → Executable file
View File

@@ -1,15 +1,17 @@
export TRANSIFEX_RESOURCE = frontend-app-profile export TRANSIFEX_RESOURCE = frontend-app-profile
transifex_resource = frontend-app-profile transifex_resource = frontend-app-profile
transifex_langs = "ar,de,de_DE,es_419,fa_IR,fr,fr_CA,hi,it,it_IT,pt,pt_PT,ru,uk,zh_CN" transifex_langs = "ar,fr,es_419,zh_CN"
intl_imports = ./node_modules/.bin/intl-imports.js
transifex_utils = ./node_modules/.bin/transifex-utils.js transifex_utils = ./node_modules/.bin/transifex-utils.js
i18n = ./src/i18n i18n = ./src/i18n
transifex_input = $(i18n)/transifex_input.json transifex_input = $(i18n)/transifex_input.json
# This directory must match .babelrc . tx_url1 = https://www.transifex.com/api/2/project/edx-platform/resource/$(transifex_resource)/translation/en/strings/
transifex_temp = ./temp/babel-plugin-formatjs tx_url2 = https://www.transifex.com/api/2/project/edx-platform/resource/$(transifex_resource)/source/
NPM_TESTS=build i18n_extract lint test # This directory must match .babelrc .
transifex_temp = ./temp/babel-plugin-react-intl
NPM_TESTS=build i18n_extract lint test is-es5
.PHONY: test .PHONY: test
test: $(addprefix test.npm.,$(NPM_TESTS)) ## validate ci suite test: $(addprefix test.npm.,$(NPM_TESTS)) ## validate ci suite
@@ -50,24 +52,9 @@ push_translations:
# Pushing comments to Transifex... # Pushing comments to Transifex...
./node_modules/@edx/reactifex/bash_scripts/put_comments_v3.sh ./node_modules/@edx/reactifex/bash_scripts/put_comments_v3.sh
ifeq ($(OPENEDX_ATLAS_PULL),)
# Pulls translations from Transifex. # Pulls translations from Transifex.
pull_translations: pull_translations:
tx pull -t -f --mode reviewed --languages=$(transifex_langs) tx pull -f --mode reviewed --languages=$(transifex_langs)
else
# Experimental: OEP-58 Pulls translations using atlas
pull_translations:
rm -rf src/i18n/messages
mkdir src/i18n/messages
cd src/i18n/messages \
&& atlas pull --filter=$(transifex_langs) \
translations/paragon/src/i18n/messages:paragon \
translations/frontend-component-header/src/i18n/messages:frontend-component-header \
translations/frontend-component-footer/src/i18n/messages:frontend-component-footer \
translations/frontend-app-profile/src/i18n/messages:frontend-app-profile
$(intl_imports) paragon frontend-component-header frontend-component-footer frontend-app-profile
endif
# This target is used by Travis. # This target is used by Travis.
validate-no-uncommitted-package-lock-changes: validate-no-uncommitted-package-lock-changes:

View File

@@ -1,147 +1,57 @@
##################### |Build Status| |Codecov| |license|
frontend-app-profile frontend-app-profile
##################### ====================
|license-badge| |status-badge| |ci-badge| |codecov-badge| This is a micro-frontend application responsible for the display and updating of user profiles. Please tag **@edx/arch-fed** on any PRs or issues.
.. |license-badge| image:: https://img.shields.io/github/license/openedx/frontend-app-profile.svg
:target: https://github.com/openedx/frontend-app-profile/blob/main/LICENSE
:alt: License
.. |status-badge| image:: https://img.shields.io/badge/Status-Maintained-brightgreen
.. |ci-badge| image:: https://github.com/openedx/frontend-app-profile/actions/workflows/ci.yml/badge.svg
:target: https://github.com/openedx/frontend-app-profile/actions/workflows/ci.yml
:alt: Continuous Integration
.. |codecov-badge| image:: https://codecov.io/github/openedx/frontend-app-profile/coverage.svg?branch=main
:target: https://codecov.io/github/openedx/frontend-app-profile?branch=main
:alt: Codecov
********
Purpose
********
This is a micro-frontend application responsible for the display and updating of user profiles.
When a user views their own profile, they're given fields to edit their full name, location, primary spoken language, education, social links, and bio. Each field also has a dropdown to select the visibility of that field - i.e., whether it can be viewed by other learners. When a user views their own profile, they're given fields to edit their full name, location, primary spoken language, education, social links, and bio. Each field also has a dropdown to select the visibility of that field - i.e., whether it can be viewed by other learners.
When a user views someone else's profile, they see all those fields that that user set as public. When a user views someone else's profile, they see all those fields that that user set as public.
*************** ----------
Getting Started
***************
Installation Development
============ -----------
Follow these steps to provision, run, and enable an instance of the Start Devstack
Profile MFE for local development via the `devstack`_. ^^^^^^^^^^^^^^
.. _devstack: https://github.com/openedx/devstack#getting-started To use this application `devstack <https://github.com/edx/devstack>`__ must be running and you must be logged into it.
#. To use this application, `devstack <https://github.com/openedx/devstack>`__ must be running and you must be logged into it. - Start devstack
- Log in (http://localhost:18000/login)
* Start devstack Start the development server
* Log in (http://localhost:18000/login) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. To run Profile, install requirements and start the development server by running: In this project, install requirements and start the development server by running:
.. code-block:: .. code:: bash
1. Clone your new repo: npm install
npm start # The server will run on port 1995
``git clone https://github.com/openedx/frontend-app-profile.git`` Once the dev server is up visit http://localhost:1995/u/staff.
2. Use node v18.x. ----------
The current version of the micro-frontend build scripts support node 18. Configuration and Deployment
Using other major versions of node *may* work, but this is unsupported. For ----------------------------
convenience, this repository includes an .nvmrc file to help in setting the
correct node version via `nvm <https://github.com/nvm-sh/nvm>`_.
3. Install npm dependencies:
``cd frontend-app-profile && npm ci``
4. Start the dev server:
``npm start``
The server will run on port 1995
Once the dev server is up, visit http://localhost:1995/u/staff.
Configuration
=============
This MFE is configured via node environment variables supplied at build time. See the .env file for the list of required environment variables. Example build syntax with a single environment variable: This MFE is configured via node environment variables supplied at build time. See the .env file for the list of required environment variables. Example build syntax with a single environment variable:
.. code-block:: .. code:: bash
NODE_ENV=production ACCESS_TOKEN_COOKIE_NAME='edx-jwt-cookie-header-payload' npm run build NODE_ENV=production ACCESS_TOKEN_COOKIE_NAME='edx-jwt-cookie-header-payload' npm run build
Getting Help
============
If you're having trouble, we have discussion forums at For more information see the document: `Micro-frontend applications in Open
https://discuss.openedx.org where you can connect with others in the community. edX <https://edx.readthedocs.io/projects/edx-developer-docs/en/latest/micro-frontends-in-open-edx.html>`__.
Our real-time conversations are on Slack. You can request a `Slack .. |Build Status| image:: https://api.travis-ci.org/edx/frontend-app-profile.svg?branch=master
invitation`_, then join our `community Slack workspace`_. Because this is a :target: https://travis-ci.org/edx/frontend-app-profile
frontend repository, the best place to discuss it would be in the `#wg-frontend .. |Codecov| image:: https://img.shields.io/codecov/c/github/edx/frontend-app-profile
channel`_. :target: https://codecov.io/gh/edx/frontend-app-profile
.. |license| image:: https://img.shields.io/npm/l/@edx/frontend-app-profile.svg
For anything non-trivial, the best path is to open an issue in this repository :target: @edx/frontend-app-profile
with as many details about the issue you are facing as you can provide. Please tag **@openedx/2u-aperture** on any PRs or issues.
https://github.com/openedx/frontend-app-profile/issues
For more information about these options, see the `Getting Help`_ page.
.. _Slack invitation: https://openedx.org/slack
.. _community Slack workspace: https://openedx.slack.com/
.. _#wg-frontend channel: https://openedx.slack.com/archives/C04BM6YC7A6
.. _Getting Help: https://openedx.org/getting-help
License
=======
The code in this repository is licensed under the AGPLv3 unless otherwise
noted.
Please see `LICENSE <LICENSE>`_ for details.
Contributing
============
Contributions are very welcome. Please read `How To Contribute`_ for details.
.. _How To Contribute: https://openedx.org/r/how-to-contribute
This project is currently accepting all types of contributions, bug fixes,
security fixes, maintenance work, or new features. However, please make sure
to have a discussion about your new feature idea with the maintainers prior to
beginning development to maximize the chances of your change being accepted.
You can start a conversation by creating a new issue on this repo summarizing
your idea.
The Open edX Code of Conduct
============================
All community members are expected to follow the `Open edX Code of Conduct`_.
.. _Open edX Code of Conduct: https://openedx.org/code-of-conduct/
People
======
The assigned maintainers for this component and other project details may be
found in `Backstage`_. Backstage pulls this data from the ``catalog-info.yaml``
file in this repo.
.. _Backstage: https://backstage.herokuapp.com/catalog/default/component/frontend-app-profile
Reporting Security Issues
=========================
Please do not report security issues in public. Email security@openedx.org instead.

View File

@@ -1,24 +0,0 @@
# This file records information about this repo. Its use is described in OEP-55:
# https://open-edx-proposals.readthedocs.io/en/latest/processes/oep-0055-proc-project-maintainers.html
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: 'Profile'
description: 'This is a micro-frontend application responsible for the display and updating of user profiles.'
links:
- url: 'https://github.com/openedx/frontend-app-profile/blob/master/README.rst'
title: 'Documentation'
icon: 'Article'
annotations:
# (Optional) Annotation keys and values can be whatever you want.
# We use it in Open edX repos to have a comma-separated list of GitHub user
# names that might be interested in changes to the architecture of this
# component.
openedx.org/arch-interest-groups: ""
# This can be multiple comma-separated projects.
openedx.org/add-to-projects: "openedx:23"
spec:
type: 'service'
lifecycle: 'production'
owner: 2U-aperture
# (Optional) An array of different components or resources.

38346
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -6,74 +6,71 @@
"license": "AGPL-3.0", "license": "AGPL-3.0",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/openedx/frontend-app-profile.git" "url": "git+https://github.com/edx/frontend-app-profile.git"
}, },
"scripts": { "scripts": {
"build": "fedx-scripts webpack", "build": "fedx-scripts webpack",
"i18n_extract": "fedx-scripts formatjs extract", "i18n_extract": "BABEL_ENV=i18n fedx-scripts babel src --quiet > /dev/null",
"is-es5": "es-check es5 ./dist/*.js",
"lint": "fedx-scripts eslint --ext .js --ext .jsx .", "lint": "fedx-scripts eslint --ext .js --ext .jsx .",
"snapshot": "fedx-scripts jest --updateSnapshot", "snapshot": "fedx-scripts jest --updateSnapshot",
"start": "fedx-scripts webpack-dev-server --progress", "start": "fedx-scripts webpack-dev-server --progress",
"test": "TZ=UTC fedx-scripts jest --coverage --passWithNoTests", "test": "fedx-scripts jest --coverage --passWithNoTests"
"stubs": "pact-stub-service ./src/pacts/frontend-app-profile-edx-platform.json --port 18000"
}, },
"bugs": { "bugs": {
"url": "https://github.com/openedx/frontend-app-profile/issues" "url": "https://github.com/edx/frontend-app-profile/issues"
}, },
"homepage": "https://github.com/openedx/frontend-app-profile#readme", "homepage": "https://github.com/edx/frontend-app-profile#readme",
"publishConfig": { "publishConfig": {
"access": "public" "access": "public"
}, },
"browserslist": [ "browserslist": [
"extends @edx/browserslist-config" "last 2 versions",
"ie 11"
], ],
"dependencies": { "dependencies": {
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2", "@edx/brand": "npm:@edx/brand-openedx@1.1.0",
"@edx/frontend-component-footer": "12.6.0", "@edx/frontend-component-footer": "10.2.1",
"@edx/frontend-component-header": "4.10.1", "@edx/frontend-component-header": "2.4.5",
"@edx/frontend-platform": "5.6.1", "@edx/frontend-platform": "1.15.1",
"@edx/paragon": "^20.44.0", "@edx/paragon": "19.6.0",
"@fortawesome/fontawesome-svg-core": "1.2.36", "@fortawesome/fontawesome-svg-core": "1.2.36",
"@fortawesome/free-brands-svg-icons": "5.15.4", "@fortawesome/free-brands-svg-icons": "5.15.4",
"@fortawesome/free-regular-svg-icons": "5.15.4", "@fortawesome/free-regular-svg-icons": "5.15.4",
"@fortawesome/free-solid-svg-icons": "5.15.4", "@fortawesome/free-solid-svg-icons": "5.15.4",
"@fortawesome/react-fontawesome": "0.2.0", "@fortawesome/react-fontawesome": "0.1.17",
"@pact-foundation/pact": "^11.0.2", "classnames": "2.3.1",
"classnames": "2.3.2", "core-js": "3.20.3",
"core-js": "3.33.3",
"history": "5.3.0",
"lodash.camelcase": "4.3.0", "lodash.camelcase": "4.3.0",
"lodash.get": "4.4.2", "lodash.get": "4.4.2",
"lodash.pick": "4.4.0", "lodash.pick": "4.4.0",
"lodash.snakecase": "4.1.1", "lodash.snakecase": "4.1.1",
"prop-types": "15.8.1", "prop-types": "15.7.2",
"react": "17.0.2", "react": "16.14.0",
"react-dom": "17.0.2", "react-dom": "16.14.0",
"react-error-boundary": "^4.0.11", "react-redux": "7.2.6",
"react-helmet": "6.1.0", "react-router": "5.2.1",
"react-redux": "7.2.9", "react-router-dom": "5.3.0",
"react-router": "6.16.0", "redux": "4.1.2",
"react-router-dom": "6.16.0",
"redux": "4.2.1",
"redux-devtools-extension": "2.13.9", "redux-devtools-extension": "2.13.9",
"redux-logger": "3.0.6", "redux-logger": "3.0.6",
"redux-saga": "1.2.3", "redux-saga": "1.1.3",
"redux-thunk": "2.4.2", "redux-thunk": "2.4.1",
"regenerator-runtime": "0.14.0", "regenerator-runtime": "0.13.9",
"reselect": "4.1.8", "reselect": "4.1.5",
"universal-cookie": "4.0.4" "universal-cookie": "3.1.0"
}, },
"devDependencies": { "devDependencies": {
"@commitlint/cli": "17.8.1", "@commitlint/cli": "13.2.1",
"@commitlint/config-angular": "17.8.1", "@commitlint/config-angular": "13.2.0",
"@edx/browserslist-config": "^1.1.1", "@edx/reactifex": "1.0.3",
"@edx/frontend-build": "13.0.8", "@edx/frontend-build": "9.1.4",
"@edx/reactifex": "2.2.0", "codecov": "3.8.3",
"@testing-library/react": "12.1.5",
"@wojtekmaj/enzyme-adapter-react-17": "0.8.0",
"enzyme": "3.11.0", "enzyme": "3.11.0",
"glob": "10.3.10", "enzyme-adapter-react-16": "1.15.6",
"react-test-renderer": "17.0.2", "es-check": "5.2.4",
"glob": "7.2.0",
"react-test-renderer": "16.14.0",
"reactifex": "1.1.1", "reactifex": "1.1.1",
"redux-mock-store": "1.5.4" "redux-mock-store": "1.5.4"
} }

View File

@@ -1,93 +0,0 @@
'use client';
import React, {
useEffect, useMemo, useState,
} from 'react';
import PropTypes from 'prop-types';
import { ErrorBoundary } from 'react-error-boundary';
import { logError } from '@edx/frontend-platform/logging';
import {
dispatchMountedEvent, dispatchReadyEvent, dispatchUnmountedEvent, useHostEvent,
} from './data/hooks';
import { PLUGIN_RESIZE } from './data/constants';
// see example-plugin-app/src/PluginOne.jsx for example of customizing errorFallback
function errorFallbackDefault() {
return (
<div>
<h2>
Oops! An error occurred. Please refresh the screen to try again.
</h2>
</div>
);
}
// eslint-disable-next-line react/function-component-definition
export default function Plugin({
children, className, style, ready, errorFallbackProp,
}) {
const [dimensions, setDimensions] = useState({
width: null,
height: null,
});
const finalStyle = useMemo(() => ({
...dimensions,
...style,
}), [dimensions, style]);
const errorFallback = errorFallbackProp || errorFallbackDefault;
// Error logging function
// Need to confirm: When an error is caught here, the logging will be sent to the child MFE's logging service
const logErrorToService = (error, info) => {
logError(error, { stack: info.componentStack });
};
useHostEvent(PLUGIN_RESIZE, ({ payload }) => {
setDimensions({
width: payload.width,
height: payload.height,
});
});
useEffect(() => {
dispatchMountedEvent();
return () => {
dispatchUnmountedEvent();
};
}, []);
useEffect(() => {
if (ready) {
dispatchReadyEvent();
}
}, [ready]);
return (
<div className={className} style={finalStyle}>
<ErrorBoundary
FallbackComponent={errorFallback}
onError={logErrorToService}
>
{children}
</ErrorBoundary>
</div>
);
}
Plugin.propTypes = {
children: PropTypes.node.isRequired,
className: PropTypes.string,
errorFallbackProp: PropTypes.func,
ready: PropTypes.bool,
style: PropTypes.object, // eslint-disable-line
};
Plugin.defaultProps = {
className: null,
errorFallbackProp: null,
style: {},
ready: true,
};

View File

@@ -1,42 +0,0 @@
'use client';
import React from 'react';
// eslint-disable-next-line import/no-extraneous-dependencies
import PluginContainerIframe from './PluginContainerIframe';
import {
IFRAME_PLUGIN,
} from './data/constants';
import { pluginConfigShape } from './data/shapes';
// eslint-disable-next-line react/function-component-definition
export default function PluginContainer({ config, ...props }) {
if (config === null) {
return null;
}
// this will allow for future plugin types to be inserted in the PluginErrorBoundary
let renderer = null;
switch (config.type) {
case IFRAME_PLUGIN:
renderer = (
<PluginContainerIframe config={config} {...props} />
);
break;
// istanbul ignore next: default isn't meaningful, just satisfying linter
default:
}
return (
renderer
);
}
PluginContainer.propTypes = {
config: pluginConfigShape,
};
PluginContainer.defaultProps = {
config: null,
};

View File

@@ -1,99 +0,0 @@
import React, {
useEffect, useState,
} from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import {
PLUGIN_MOUNTED,
PLUGIN_READY,
PLUGIN_RESIZE,
} from './data/constants';
import {
dispatchPluginEvent,
useElementSize,
usePluginEvent,
} from './data/hooks';
import { pluginConfigShape } from './data/shapes';
/**
* Feature policy for iframe, allowing access to certain courseware-related media.
*
* We must use the wildcard (*) origin for each feature, as courseware content
* may be embedded in external iframes. Notably, xblock-lti-consumer is a popular
* block that iframes external course content.
* This policy was selected in conference with the edX Security Working Group.
* Changes to it should be vetted by them (security@edx.org).
*/
export const IFRAME_FEATURE_POLICY = (
'fullscreen; microphone *; camera *; midi *; geolocation *; encrypted-media *'
);
// eslint-disable-next-line react/function-component-definition
export default function PluginContainerIframe({
config, fallback, className, ...props
}) {
const { url } = config;
const { title, scrolling } = props;
const [mounted, setMounted] = useState(false);
const [ready, setReady] = useState(false);
const [iframeRef, iframeElement, width, height] = useElementSize();
useEffect(() => {
if (mounted) {
dispatchPluginEvent(iframeElement, {
type: PLUGIN_RESIZE,
payload: {
width,
height,
},
}, url);
}
}, [iframeElement, mounted, width, height, url]);
usePluginEvent(iframeElement, PLUGIN_MOUNTED, () => {
setMounted(true);
});
usePluginEvent(iframeElement, PLUGIN_READY, () => {
setReady(true);
});
return (
<>
<iframe
ref={iframeRef}
title={title}
src={url}
allow={IFRAME_FEATURE_POLICY}
scrolling={scrolling}
referrerPolicy="origin" // The sent referrer will be limited to the origin of the referring page: its scheme, host, and port.
className={classNames(
'border border-0',
{ 'd-none': !ready },
className,
)}
{...props}
/>
{!ready && fallback}
</>
);
}
PluginContainerIframe.propTypes = {
config: pluginConfigShape,
fallback: PropTypes.node,
scrolling: PropTypes.oneOf(['auto', 'yes', 'no']),
title: PropTypes.string,
className: PropTypes.string,
};
PluginContainerIframe.defaultProps = {
config: null,
fallback: null,
scrolling: 'auto',
title: null,
className: null,
};

View File

@@ -1,45 +0,0 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
// eslint-disable-next-line import/no-extraneous-dependencies
import { FormattedMessage } from 'react-intl';
import { logError } from '@edx/frontend-platform/logging';
export default class PluginErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError() {
// Update state so the next render will show the fallback UI.
return { hasError: true };
}
componentDidCatch(error, info) {
logError(error, { stack: info.componentStack });
}
render() {
if (this.state.hasError) {
// You can render any custom fallback UI
return (
<FormattedMessage
id="plugin.load.failure.text"
defaultMessage="This content failed to load."
description="error message when an unexpected error occurs"
/>
);
}
return this.props.children;
}
}
PluginErrorBoundary.propTypes = {
children: PropTypes.node,
};
PluginErrorBoundary.defaultProps = {
children: null,
};

View File

@@ -1,75 +0,0 @@
/* eslint-disable no-unused-vars */
import React, { forwardRef } from 'react';
import classNames from 'classnames';
import { Spinner } from '@edx/paragon';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
// import { usePluginSlot } from './data/hooks';
import PluginContainer from './PluginContainer';
const PluginSlot = forwardRef(({
as, id, intl, pluginProps, children, ...props
}, ref) => {
/* the plugins below are obtained by the id passed into PluginSlot by the Host MFE. See example/src/PluginsPage.jsx
for an example of how PluginSlot is populated, and example/src/index.jsx for a dummy JS config that holds all plugins
*/
// const { plugins, keepDefault } = usePluginSlot(id);
const { fallback } = pluginProps;
// TODO: Add internationalization to the "Loading" text on the spinner.
let finalFallback = (
<div className={classNames(pluginProps.className, 'd-flex justify-content-center align-items-center')}>
<Spinner animation="border" screenReaderText="Loading" />
</div>
);
if (fallback !== undefined) {
finalFallback = fallback;
}
let finalChildren = [];
// if (plugins.length > 0) {
// if (keepDefault) {
// finalChildren.push(children);
// }
// plugins.forEach((pluginConfig) => {
// finalChildren.push(
// <PluginContainer
// key={pluginConfig.url}
// config={pluginConfig}
// fallback={finalFallback}
// {...pluginProps}
// />,
// );
// });
// } else {
finalChildren = children;
// }
return React.createElement(
as,
{
...props,
ref,
},
finalChildren,
);
});
export default injectIntl(PluginSlot);
PluginSlot.propTypes = {
as: PropTypes.elementType,
children: PropTypes.node,
id: PropTypes.string.isRequired,
intl: intlShape.isRequired,
pluginProps: PropTypes.object, // eslint-disable-line
};
PluginSlot.defaultProps = {
as: 'div',
children: null,
pluginProps: {},
};

View File

@@ -1,8 +0,0 @@
// TODO: We expect other plugin types to be added here, such as LTI_PLUGIN and BUILD_TIME_PLUGIN.
export const IFRAME_PLUGIN = 'IFRAME_PLUGIN'; // loads iframe at the URL, rather than loading a JS file.
// Plugin lifecycle events
export const PLUGIN_MOUNTED = 'PLUGIN_MOUNTED';
export const PLUGIN_READY = 'PLUGIN_READY';
export const PLUGIN_UNMOUNTED = 'PLUGIN_UNMOUNTED';
export const PLUGIN_RESIZE = 'PLUGIN_RESIZE';

View File

@@ -1,96 +0,0 @@
import {
useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState,
} from 'react';
import { PLUGIN_MOUNTED, PLUGIN_READY, PLUGIN_UNMOUNTED } from './constants';
export function useMessageEvent(srcWindow, type, callback) {
useLayoutEffect(() => {
const listener = (event) => {
// Filter messages to those from our source window.
if (event.source === srcWindow) {
if (event.data.type === type) {
callback({ type, payload: event.data.payload });
}
}
};
if (srcWindow !== null) {
global.addEventListener('message', listener);
}
return () => {
global.removeEventListener('message', listener);
};
}, [srcWindow, type, callback]);
}
export function useHostEvent(type, callback) {
useMessageEvent(global.parent, type, callback);
}
export function usePluginEvent(iframeElement, type, callback) {
const contentWindow = iframeElement ? iframeElement.contentWindow : null;
useMessageEvent(contentWindow, type, callback);
}
export function dispatchMessageEvent(targetWindow, message, targetOrigin) {
// Checking targetOrigin falsiness here since '', null or undefined would all be reasons not to
// try to post a message to the origin.
if (targetOrigin) {
targetWindow.postMessage(message, targetOrigin);
}
}
export function dispatchPluginEvent(iframeElement, message, targetOrigin) {
dispatchMessageEvent(iframeElement.contentWindow, message, targetOrigin);
}
export function dispatchHostEvent(message) {
dispatchMessageEvent(global.parent, message, global.document.referrer);
}
export function dispatchReadyEvent() {
dispatchHostEvent({ type: PLUGIN_READY });
}
export function dispatchMountedEvent() {
dispatchHostEvent({ type: PLUGIN_MOUNTED });
}
export function dispatchUnmountedEvent() {
dispatchHostEvent({ type: PLUGIN_UNMOUNTED });
}
export function useElementSize() {
const observerRef = useRef();
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
const [offset, setOffset] = useState({ x: 0, y: 0 });
const [element, setElement] = useState(null);
const measuredRef = useCallback(_element => {
setElement(_element);
}, []);
useEffect(() => {
observerRef.current = new ResizeObserver(() => {
if (element) {
setDimensions({
width: element.clientWidth,
height: element.clientHeight,
});
setOffset({
x: element.offsetLeft,
y: element.offsetTop,
});
}
});
if (element) {
observerRef.current.observe(element);
}
}, [element]);
return useMemo(
() => ([measuredRef, element, dimensions.width, dimensions.height, offset.x, offset.y]),
[measuredRef, element, dimensions, offset],
);
}

View File

@@ -1,10 +0,0 @@
/* eslint-disable import/prefer-default-export */
import PropTypes from 'prop-types';
import { IFRAME_PLUGIN } from './constants';
export const pluginConfigShape = PropTypes.shape({
url: PropTypes.string.isRequired,
type: PropTypes.oneOf([IFRAME_PLUGIN]).isRequired,
// This is a place for us to put any generic props we want to pass to the component. We need it.
props: PropTypes.object, // eslint-disable-line react/forbid-prop-types
});

View File

@@ -1,18 +0,0 @@
// export {
// usePluginSlot,
// } from './data/hooks';
export {
default as Plugin,
} from './Plugin';
export {
default as PluginContainer,
} from './PluginContainer';
export {
default as PluginSlot,
} from './PluginSlot';
export {
IFRAME_PLUGIN,
} from './data/constants';
export {
default as PluginErrorBoundary,
} from './PluginErrorBoundary';

View File

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

View File

@@ -1,21 +0,0 @@
import React from 'react';
import { Helmet } from 'react-helmet';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { getConfig } from '@edx/frontend-platform';
import messages from './messages';
const Head = ({ intl }) => (
<Helmet>
<title>
{intl.formatMessage(messages['profile.page.title'], { siteName: getConfig().SITE_NAME })}
</title>
<link rel="shortcut icon" href={getConfig().FAVICON_URL} type="image/x-icon" />
</Helmet>
);
Head.propTypes = {
intl: intlShape.isRequired,
};
export default injectIntl(Head);

View File

@@ -1,17 +0,0 @@
import React from 'react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { Helmet } from 'react-helmet';
import { mount } from 'enzyme';
import { getConfig } from '@edx/frontend-platform';
import Head from './Head';
describe('Head', () => {
const props = {};
it('should match render title tag and favicon with the site configuration values', () => {
mount(<IntlProvider locale="en"><Head {...props} /></IntlProvider>);
const helmet = Helmet.peek();
expect(helmet.title).toEqual(`Profile | ${getConfig().SITE_NAME}`);
expect(helmet.linkTags[0].rel).toEqual('shortcut icon');
expect(helmet.linkTags[0].href).toEqual(getConfig().FAVICON_URL);
});
});

View File

@@ -1,11 +0,0 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
'profile.page.title': {
id: 'profile.page.title',
defaultMessage: 'Profile | {siteName}',
description: 'Title tag',
},
});
export default messages;

View File

@@ -1,44 +0,0 @@
import { messages as headerMessages } from '@edx/frontend-component-header';
import { messages as footerMessages } from '@edx/frontend-component-footer';
import { messages as paragonMessages } from '@edx/paragon';
import arMessages from './messages/ar.json';
import deMessages from './messages/de.json';
import dedeCAMessages from './messages/de_DE.json';
import es419Messages from './messages/es_419.json';
import faIRMessages from './messages/fa_IR.json';
import frCAMessages from './messages/fr_CA.json';
import itMessages from './messages/it.json';
import ititCAMessages from './messages/it_IT.json';
import frMessages from './messages/fr.json';
import hiMessages from './messages/hi.json';
import ptMessages from './messages/pt.json';
import ptptCAMessages from './messages/pt_PT.json';
import ruMessages from './messages/ru.json';
import ukMessages from './messages/uk.json';
import zhcnMessages from './messages/zh_CN.json';
// no need to import en messages-- they are in the defaultMessage field
const appMessages = {
ar: arMessages,
'es-419': es419Messages,
'fa-ir': faIRMessages,
fr: frMessages,
'zh-cn': zhcnMessages,
pt: ptMessages,
it: itMessages,
de: deMessages,
hi: hiMessages,
'fr-ca': frCAMessages,
ru: ruMessages,
uk: ukMessages,
'de-de': dedeCAMessages,
'it-it': ititCAMessages,
'pt-pt': ptptCAMessages,
};
export default [
headerMessages,
footerMessages,
paragonMessages,
appMessages,
];

32
src/i18n/index.jsx Normal file
View File

@@ -0,0 +1,32 @@
import arMessages from './messages/ar.json';
import caMessages from './messages/ca.json';
// no need to import en messages-- they are in the defaultMessage field
import es419Messages from './messages/es_419.json';
import frMessages from './messages/fr.json';
import zhcnMessages from './messages/zh_CN.json';
import heMessages from './messages/he.json';
import idMessages from './messages/id.json';
import kokrMessages from './messages/ko_kr.json';
import plMessages from './messages/pl.json';
import ptbrMessages from './messages/pt_br.json';
import ruMessages from './messages/ru.json';
import thMessages from './messages/th.json';
import ukMessages from './messages/uk.json';
const messages = {
ar: arMessages,
'es-419': es419Messages,
fr: frMessages,
'zh-cn': zhcnMessages,
ca: caMessages,
he: heMessages,
id: idMessages,
'ko-kr': kokrMessages,
pl: plMessages,
'pt-br': ptbrMessages,
ru: ruMessages,
th: thMessages,
uk: ukMessages,
};
export default messages;

View File

@@ -1,57 +1,52 @@
{ {
"profile.page.title": "الملف الشخصي | {siteName}", "profile.age.headline": ا يمكن مشاركة ملفك الشخصي",
"profile.age.details": "لمشاركة ملفك الشخصي مع بقية متعلمي {siteName}، يجب أن تؤكد أنك عمرك يفوق 13 عامًا.", "profile.age.details": "لمشاركة ملفك الشخصي مع متعلمي {siteName} الآخرين يجب التحقق من أن يكون عمرك أكثر من ١٣ سنة",
"profile.age.set.date": "ضيط تاريخ ميلادك", "profile.age.set.date": "اضبط تاريخ ميلاك",
"profile.datejoined.member.since": "عضو منذ {year}", "profile.datejoined.member.since": "عضو منذُ {year}",
"profile.bio.empty": "إضافة نبذة قصيرة", "profile.bio.empty": "أضف نبذة قصيرة",
"profile.bio.about.me": "نبذة عنّي", "profile.bio.about.me": "نبذة عنّي",
"profile.certificate.organization.label": "من طرف", "profile.certificate.organization.label": "من",
"profile.certificate.completion.date.label": "صدرت بتاريخ {date}", "profile.certificate.completion.date.label": "مكتمل في",
"profile.no.certificates": يست لديك أي شهادات يعد.", "profile.no.certificates": م تحصل على أية شهادات حتى الآن.",
"profile.certificates.my.certificates": "شهاداتي", "profile.certificates.my.certificates": "شهاداتي",
"profile.certificates.view.certificate": "معاينة الشهادة", "profile.certificates.view.certificate": "معاينة الشهادة",
"profile.certificates.types.verified": "شهادة موثقة", "profile.certificates.types.verified": "شهادة موثقة",
"profile.certificates.types.professional": "شهادة مهنية", "profile.certificates.types.professional": "شهادة مهنية",
"profile.certificates.types.unknown": "شهادة", "profile.certificates.types.unknown": "شهادة",
"profile.country.label": "الموقع", "profile.country.label": "الموقع",
"profile.country.empty": "إضافة الموقع", "profile.country.empty": "أضف موقعًا",
"profile.education.empty": "إضافة المستوى التعليمي", "profile.education.empty": "أضف مؤهلًا تعليميًا",
"profile.education.education": "المستوى التعليمي", "profile.education.education": "المستوى التعليمي",
"profile.education.levels.p": "دكتوراه", "profile.education.levels.p": "شهادة دكتوراه",
"profile.education.levels.m": "ماجستير / ماستر أو شهادة مهنيّة", "profile.education.levels.m": "ماجستير أو شهادة مهنيّة",
"profile.education.levels.b": "بكالوريوس / ليسانس", "profile.education.levels.b": "درجة البكالوريوس",
"profile.education.levels.a": "درجة الزمالة / دبلوم الدراسات الجامعية", "profile.education.levels.a": "درجة الزمالة",
"profile.education.levels.hs": "الثانوية العامة / البكالوريا", "profile.education.levels.hs": "شهادة الثانوية العامة",
"profile.education.levels.jhs": "المدرسة الإعدادية / المتوسطة", "profile.education.levels.jhs": "شهادة الثانوية الصغرى/الإعدادية/المرحلة المتوسّطة",
"profile.education.levels.el": "المدرسة الابتدائية / الأساسية", "profile.education.levels.el": "شهادة المدرسة الابتدائية",
"profile.education.levels.none": ون تعليم رسمي", "profile.education.levels.none": "لا يوجد تعليم رسمي",
"profile.education.levels.o": "نوع آخر من التعليم", "profile.education.levels.o": "نوع آخر من التعليم",
"profile.editbutton.edit": عديل", "profile.editbutton.edit": حرير",
"profile.formcontrols.who.can.see": "من يستطيع رؤية هذا:", "profile.formcontrols.who.can.see": "من يمكنه مشاهدة هذا:",
"profile.formcontrols.button.cancel": "إلغاء", "profile.formcontrols.button.cancel": "إلغاء",
"profile.formcontrols.button.save": "حفظ", "profile.formcontrols.button.save": "حفظ",
"profile.formcontrols.button.saving": "الحفظ جارٍ", "profile.formcontrols.button.saving": "جاري الحفظ",
"profile.formcontrols.button.saved": "تم الحفظ", "profile.formcontrols.button.saved": "تم الحفظ",
"profile.visibility.who.just.me": "أنا فقط", "profile.visibility.who.just.me": "أنا فقط",
"profile.visibility.who.everyone": "جميع من على {siteName}", "profile.visibility.who.everyone": "جميع أعضاء {siteName}",
"profile.learningGoal.learningGoal": "هدف التعلم",
"profile.learningGoal.options.start_career": "أريد أن أبدأ مسيرتي المهنية",
"profile.learningGoal.options.advance_career": "أريد أن ارتقي في مسيرتي المهنية",
"profile.learningGoal.options.learn_something_new": "أريد أن أتعلم شيئًا جديدًا",
"profile.learningGoal.options.something_else": "شيء آخر",
"profile.name.full.name": "الاسم الكامل", "profile.name.full.name": "الاسم الكامل",
"profile.name.details": "هذا هو الاسم الذي يظهر في حسابك وفي شهاداتك", "profile.name.details": "هذا هو الاسم الذي سيظهر في حسابك وفي شهاداتك",
"profile.name.empty": "إضافة الاسم", "profile.name.empty": "إضافة اسم",
"profile.preferredlanguage.empty": "إضافة اللغة", "profile.preferredlanguage.empty": "إضافة اللغة",
"profile.preferredlanguage.label": "لغة التحدّث الأساسية", "profile.preferredlanguage.label": "لغة التحدث الأم",
"profile.profileavatar.upload-button": "تحميل صورة", "profile.profileavatar.upload-button": "تحميل صورة",
"profile.profileavatar.remove.button": "حذف", "profile.profileavatar.remove.button": "حذف",
"profile.image.alt.attribute": "صورة الملف الشخصي", "profile.image.alt.attribute": "صورة عرض الملف الشخصي",
"profile.profileavatar.change-button": "تغيير", "profile.profileavatar.change-button": "تغيير",
"profile.sociallinks.add": "إضافة {network}", "profile.sociallinks.add": "إضافة {network}",
"profile.sociallinks.social.links": "روابط التواصل الاجتماعي", "profile.sociallinks.social.links": "روابط قنوات التواصل الاجتماعي",
"profile.notfound.message": "الصفحة التي تبحث عنها غير متوفرة أو هناك خطأ في العنوان. رجاءً تحقق من العنوان و حاول مجدّدًا.", "profile.notfound.message": "الصفحة التي تبحث عنها غير متوفرة أو هناك خطأ في نص الرابط. الرجاء التحقق من الرابط والمحاولة مجددا.",
"profile.viewMyRecords": "عرض سجلّاتي", "profile.viewMyRecords": "عرض سجلّاتي",
"profile.loading": تم تحميل الملف الشخصي...", "profile.loading": "جاري تحميل الملف الشخصي ...",
"profile.username.description": "معلومات ملفك الشخصي تظهر لك فقط. وحده اسم المستخدم الخاص بك يظهر للآخرين على {siteName}." "profile.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}."
} }

View File

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

View File

@@ -1,57 +0,0 @@
{
"profile.page.title": "Profile | {siteName}",
"profile.age.details": "To share your profile with other {siteName} 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 {siteName}",
"profile.learningGoal.learningGoal": "Learning Goal",
"profile.learningGoal.options.start_career": "I want to start my career",
"profile.learningGoal.options.advance_career": "I want to advance my career",
"profile.learningGoal.options.learn_something_new": "I want to learn something new",
"profile.learningGoal.options.something_else": "Something else",
"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.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}."
}

View File

@@ -1,57 +0,0 @@
{
"profile.page.title": "Profil | {siteName}",
"profile.age.details": "Um Ihr Profil mit anderen {siteName}-Lernern zu teilen, müssen Sie bestätigen, dass Sie über 13 Jahre alt sind.",
"profile.age.set.date": "Legen Sie Ihr Geburtsdatum fest",
"profile.datejoined.member.since": "Mitglied seit {year}",
"profile.bio.empty": "Fügen Sie Ihre Kurzbiografie hinzu",
"profile.bio.about.me": "Über mich",
"profile.certificate.organization.label": "Von",
"profile.certificate.completion.date.label": "Abgeschlossen am {date}",
"profile.no.certificates": "Sie haben bisher keine Zertifikate erhalten.",
"profile.certificates.my.certificates": "Meine Zertifikate",
"profile.certificates.view.certificate": "Zertifikat anschauen",
"profile.certificates.types.verified": "Beglaubigtes Zertifikat ",
"profile.certificates.types.professional": "Professional Certificate",
"profile.certificates.types.unknown": "Zertifikat",
"profile.country.label": "Ort",
"profile.country.empty": "Standort hinzufügen",
"profile.education.empty": "Ausbildung hinzufügen",
"profile.education.education": "Bildung",
"profile.education.levels.p": "Doktortitel",
"profile.education.levels.m": "Master oder gleichwertiger akademischer Bildungsgrad",
"profile.education.levels.b": "Bachelor",
"profile.education.levels.a": "Allgemeine Hochschulreife oder gleichwertiger Abschluss",
"profile.education.levels.hs": "Mittlere Reife",
"profile.education.levels.jhs": "Hauptschule",
"profile.education.levels.el": "Grundschule",
"profile.education.levels.none": "Keinen Bildungsabschluss",
"profile.education.levels.o": "Sonstige Bildung",
"profile.editbutton.edit": "Bearbeiten",
"profile.formcontrols.who.can.see": "Wer kann das sehen:",
"profile.formcontrols.button.cancel": "Abbrechen",
"profile.formcontrols.button.save": "Speichern",
"profile.formcontrols.button.saving": "Speichert",
"profile.formcontrols.button.saved": "Gespeichert",
"profile.visibility.who.just.me": "Nur ich",
"profile.visibility.who.everyone": "Alle auf {siteName}",
"profile.learningGoal.learningGoal": "Lernziel",
"profile.learningGoal.options.start_career": "Ich möchte meine Karriere starten",
"profile.learningGoal.options.advance_career": "Ich möchte mich beruflich weiterentwickeln",
"profile.learningGoal.options.learn_something_new": "Ich möchte etwas Neues lernen",
"profile.learningGoal.options.something_else": "Etwas anderes",
"profile.name.full.name": "Vollständiger Name",
"profile.name.details": "Dies ist der Name, der in Ihrem Konto und auf Ihren Zertifikaten erscheint.",
"profile.name.empty": "Name hinzufügen",
"profile.preferredlanguage.empty": "Sprache hinzufügen",
"profile.preferredlanguage.label": "Gesprochene Primärsprache ",
"profile.profileavatar.upload-button": "Foto hochladen",
"profile.profileavatar.remove.button": "Entfernen",
"profile.image.alt.attribute": "Profil Avatar",
"profile.profileavatar.change-button": "Ändern",
"profile.sociallinks.add": "{network} hinzufügen",
"profile.sociallinks.social.links": "Soziale Netzwerke",
"profile.notfound.message": "Die gesuchte Seite ist nicht verfügbar oder es liegt ein Fehler in der URL vor. Bitte überprüfen Sie die URL und versuchen Sie es erneut.",
"profile.viewMyRecords": "Meine Aufzeichnungen anzeigen",
"profile.loading": "Profil lädt...",
"profile.username.description": "Ihre Profilinformationen sind nur für Sie sichtbar. Nur Ihr Benutzername ist für andere auf {siteName} sichtbar."
}

View File

@@ -1,6 +1,6 @@
{ {
"profile.page.title": "Perfil | {siteName}", "profile.age.headline": "Tu perfil no puede ser compartido.",
"profile.age.details": "Para compartir el perfil con otros {siteName} estudiantes, debe confirmar que es mayor de 13 años.", "profile.age.details": "Para compartir tu perfil con otros estudiantes de {siteName}, debes confirmar que tienes más de 13 años.",
"profile.age.set.date": "Establece tu fecha de nacimiento", "profile.age.set.date": "Establece tu fecha de nacimiento",
"profile.datejoined.member.since": "Miembro desde {year}", "profile.datejoined.member.since": "Miembro desde {year}",
"profile.bio.empty": "Añade una breve biografía", "profile.bio.empty": "Añade una breve biografía",
@@ -34,11 +34,6 @@
"profile.formcontrols.button.saved": "Guardado", "profile.formcontrols.button.saved": "Guardado",
"profile.visibility.who.just.me": "Solo yo", "profile.visibility.who.just.me": "Solo yo",
"profile.visibility.who.everyone": "Todos en {siteName}", "profile.visibility.who.everyone": "Todos en {siteName}",
"profile.learningGoal.learningGoal": "Objetivo de aprendizaje",
"profile.learningGoal.options.start_career": "quiero empezar mi carrera",
"profile.learningGoal.options.advance_career": "Quiero avanzar en mi carrera",
"profile.learningGoal.options.learn_something_new": "quiero aprender algo nuevo",
"profile.learningGoal.options.something_else": "Algo más",
"profile.name.full.name": "Nombre completo", "profile.name.full.name": "Nombre completo",
"profile.name.details": "Este es el nombre que aparecerá en tu cuenta y en tus certificados.", "profile.name.details": "Este es el nombre que aparecerá en tu cuenta y en tus certificados.",
"profile.name.empty": "Añade nombre", "profile.name.empty": "Añade nombre",
@@ -53,5 +48,5 @@
"profile.notfound.message": "La página que estas buscando no está disponible o hay un error en la URL. Por favor, comprueba la URL y vuelve a intentarlo.", "profile.notfound.message": "La página que estas buscando no está disponible o hay un error en la URL. Por favor, comprueba la URL y vuelve a intentarlo.",
"profile.viewMyRecords": "Ver mis registros", "profile.viewMyRecords": "Ver mis registros",
"profile.loading": "Cargando perfil...", "profile.loading": "Cargando perfil...",
"profile.username.description": "La información del perfil solo la visualiza usted. Solo el nombre de usuario es visible para los demás en {siteName}." "profile.username.description": "La información de tu perfil es solo visible para ti. Únicamente tu nombre de usuario es visible para otros en {siteName}."
} }

View File

@@ -1,57 +0,0 @@
{
"profile.page.title": "پرونده کاربری {siteName}",
"profile.age.details": "برای اشتراک‌گذاری پرونده کاربری خود با سایر یادگیرندگان {siteName}، باید تأیید کنید که بیش از 13 سال سن دارید.",
"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": "تکمیل شده در {date}",
"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": "هرکسی در {siteName}",
"profile.learningGoal.learningGoal": "هدف یادگیری",
"profile.learningGoal.options.start_career": "من می خواهم کارم را شروع کنم",
"profile.learningGoal.options.advance_career": "من می خواهم حرفه ام را ارتقا دهم",
"profile.learningGoal.options.learn_something_new": "می‌خواهم چیز جدیدی یاد بگیرم",
"profile.learningGoal.options.something_else": "یک چیز دیگر",
"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": "در حال بارگذاری پرونده کاربری...",
"profile.username.description": "اطلاعات پرونده کاربری فقط برای شما قابل مشاهده است. سایرین فقط نام کاربری شما را در {siteName} می‌توانند ببینند."
}

View File

@@ -1,6 +1,6 @@
{ {
"profile.page.title": "Profile | {siteName}", "profile.age.headline": "Votre profil ne peut pas être partagé.",
"profile.age.details": "Pour partager votre profil avec d'autres étudiants {siteName}, vous devez confirmer que vous avez plus de 13 ans.", "profile.age.details": "Pour partager votre profil avec d'autres apprenants {siteName}, vous devez confirmer que vous avez plus de 13 ans.",
"profile.age.set.date": "Définissez votre date de naissance", "profile.age.set.date": "Définissez votre date de naissance",
"profile.datejoined.member.since": "Membre depuis {year}", "profile.datejoined.member.since": "Membre depuis {year}",
"profile.bio.empty": "Ajouter une courte biographie", "profile.bio.empty": "Ajouter une courte biographie",
@@ -34,11 +34,6 @@
"profile.formcontrols.button.saved": "Enregistré", "profile.formcontrols.button.saved": "Enregistré",
"profile.visibility.who.just.me": "Juste moi", "profile.visibility.who.just.me": "Juste moi",
"profile.visibility.who.everyone": "Tout le monde sur {siteName}", "profile.visibility.who.everyone": "Tout le monde sur {siteName}",
"profile.learningGoal.learningGoal": "Learning Goal",
"profile.learningGoal.options.start_career": "I want to start my career",
"profile.learningGoal.options.advance_career": "I want to advance my career",
"profile.learningGoal.options.learn_something_new": "I want to learn something new",
"profile.learningGoal.options.something_else": "Something else",
"profile.name.full.name": "Nom complet", "profile.name.full.name": "Nom complet",
"profile.name.details": "C'est le nom qui apparaît dans votre compte et sur vos certificats.", "profile.name.details": "C'est le nom qui apparaît dans votre compte et sur vos certificats.",
"profile.name.empty": "Ajouter un nom", "profile.name.empty": "Ajouter un nom",
@@ -53,5 +48,5 @@
"profile.notfound.message": "La page que vous recherchez n'est pas disponible ou il y a une erreur dans l'URL. Veuillez vérifier l'URL et réessayer.", "profile.notfound.message": "La page que vous recherchez n'est pas disponible ou il y a une erreur dans l'URL. Veuillez vérifier l'URL et réessayer.",
"profile.viewMyRecords": "Voir mes succès", "profile.viewMyRecords": "Voir mes succès",
"profile.loading": "Chargement du profil....", "profile.loading": "Chargement du profil....",
"profile.username.description": "Les informations de votre profil ne sont visibles que par vous. Seul votre nom d'utilisateur est visible par les autres sur {siteName}." "profile.username.description": "Les informations de ton profil ne sont visibles que par toi. Seul votre nom d'utilisateur est visible par les autres utilisateurs d'{siteName}."
} }

View File

@@ -1,57 +0,0 @@
{
"profile.page.title": "Profil | {siteName}",
"profile.age.details": "Pour partager votre profil avec d'autres apprenants {siteName}, vous devez confirmer que vous avez plus de 13 ans.",
"profile.age.set.date": "Entrez votre date de naissance",
"profile.datejoined.member.since": "Membre depuis {year}",
"profile.bio.empty": "Ajouter une courte biographie",
"profile.bio.about.me": "À propos de moi",
"profile.certificate.organization.label": "De",
"profile.certificate.completion.date.label": "Terminé le {date}",
"profile.no.certificates": "Vous n'avez pas encore d'attestation.",
"profile.certificates.my.certificates": "Mes Attestations",
"profile.certificates.view.certificate": "Voir votre attestation",
"profile.certificates.types.verified": "Attestation vérifiée",
"profile.certificates.types.professional": "Attestation professionnelle",
"profile.certificates.types.unknown": "Attestation",
"profile.country.label": "Adresse",
"profile.country.empty": "Ajouter un emplacement",
"profile.education.empty": "Ajouter formation",
"profile.education.education": "Formation",
"profile.education.levels.p": "Doctorat",
"profile.education.levels.m": "Maîtrise ou diplôme professionnel",
"profile.education.levels.b": "Diplôme de baccalauréat",
"profile.education.levels.a": "Diplôme d'associé",
"profile.education.levels.hs": "Lycée / enseignement secondaire",
"profile.education.levels.jhs": "Collège / enseignement secondaire inférieur",
"profile.education.levels.el": "Enseignement primaire",
"profile.education.levels.none": "Sans formation formelle",
"profile.education.levels.o": "Autre niveau de formation",
"profile.editbutton.edit": "Éditer",
"profile.formcontrols.who.can.see": "Qui peut voir ça :",
"profile.formcontrols.button.cancel": "Annuler",
"profile.formcontrols.button.save": "Sauvegarder",
"profile.formcontrols.button.saving": "Sauvegarde en cours",
"profile.formcontrols.button.saved": "Sauvegardé",
"profile.visibility.who.just.me": "Juste moi",
"profile.visibility.who.everyone": "Tout le monde sur {siteName}",
"profile.learningGoal.learningGoal": "Objectif d'apprentissage",
"profile.learningGoal.options.start_career": "Je veux commencer ma carrière",
"profile.learningGoal.options.advance_career": "Je veux faire progresser ma carrière",
"profile.learningGoal.options.learn_something_new": "Je veux apprendre quelque chose de nouveau",
"profile.learningGoal.options.something_else": "Autre chose",
"profile.name.full.name": "Nom complet",
"profile.name.details": "C'est le nom qui apparaît dans votre compte et sur vos attestations.",
"profile.name.empty": "Ajouter un nom",
"profile.preferredlanguage.empty": "Ajouter une langue",
"profile.preferredlanguage.label": "Langue principale parlée",
"profile.profileavatar.upload-button": "Téléverser une photo",
"profile.profileavatar.remove.button": "Retirer",
"profile.image.alt.attribute": "avatar de profil",
"profile.profileavatar.change-button": "Modifier",
"profile.sociallinks.add": "Ajouter {network}",
"profile.sociallinks.social.links": "Liens vers les réseaux sociaux",
"profile.notfound.message": "La page que vous recherchez n'est pas disponible ou il y a une erreur dans l'URL. Veuillez vérifier l'URL et réessayer.",
"profile.viewMyRecords": "Afficher mes dossiers",
"profile.loading": "Chargement du profil...",
"profile.username.description": "Les informations de votre profil ne sont visibles que par vous. Seul votre nom d'utilisateur est visible par les autres sur {siteName}."
}

View File

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

View File

@@ -1,57 +0,0 @@
{
"profile.page.title": "Profile | {siteName}",
"profile.age.details": "To share your profile with other {siteName} 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 {siteName}",
"profile.learningGoal.learningGoal": "Learning Goal",
"profile.learningGoal.options.start_career": "I want to start my career",
"profile.learningGoal.options.advance_career": "I want to advance my career",
"profile.learningGoal.options.learn_something_new": "I want to learn something new",
"profile.learningGoal.options.something_else": "Something else",
"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.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}."
}

View File

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

View File

@@ -1,57 +0,0 @@
{
"profile.page.title": "Profile | {siteName}",
"profile.age.details": "To share your profile with other {siteName} 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 {siteName}",
"profile.learningGoal.learningGoal": "Learning Goal",
"profile.learningGoal.options.start_career": "I want to start my career",
"profile.learningGoal.options.advance_career": "I want to advance my career",
"profile.learningGoal.options.learn_something_new": "I want to learn something new",
"profile.learningGoal.options.something_else": "Something else",
"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.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}."
}

View File

@@ -1,57 +0,0 @@
{
"profile.page.title": "Profilo | {siteName}",
"profile.age.details": "Per condividere il tuo profilo con altri studenti di {siteName}, devi confermare di avere più di 13 anni.",
"profile.age.set.date": "Imposta la data di nascita ",
"profile.datejoined.member.since": "Membro da {year}",
"profile.bio.empty": "Aggiungi una breve biografia ",
"profile.bio.about.me": "Su di me",
"profile.certificate.organization.label": "Da ",
"profile.certificate.completion.date.label": "Completato il {date}",
"profile.no.certificates": "Non si dispone ancora di alcun certificato. ",
"profile.certificates.my.certificates": "Certificati personali ",
"profile.certificates.view.certificate": "Visualizza il certificato",
"profile.certificates.types.verified": "Certificato Verificato",
"profile.certificates.types.professional": "Certificato professionale ",
"profile.certificates.types.unknown": "Certificato ",
"profile.country.label": "Posizione",
"profile.country.empty": "Aggiungi posizione ",
"profile.education.empty": "Aggiungi titolo di studio ",
"profile.education.education": "Educazione",
"profile.education.levels.p": "Dottorato",
"profile.education.levels.m": "Laurea magistrale o titolo accademico professionale",
"profile.education.levels.b": "Laurea di primo livello ",
"profile.education.levels.a": "Diploma Professionale",
"profile.education.levels.hs": "Scuole superiori/liceo",
"profile.education.levels.jhs": "Scuole Medie",
"profile.education.levels.el": "Scuola Primaria/Elementare",
"profile.education.levels.none": "Nessun livello educativo formale",
"profile.education.levels.o": "Altro livello educativo",
"profile.editbutton.edit": "Modifica",
"profile.formcontrols.who.can.see": "Chi può visualizzare: ",
"profile.formcontrols.button.cancel": "Annulla",
"profile.formcontrols.button.save": "Salva",
"profile.formcontrols.button.saving": "Salvataggio in corso",
"profile.formcontrols.button.saved": "Salvato",
"profile.visibility.who.just.me": "Solo io ",
"profile.visibility.who.everyone": "Tutti su {siteName}",
"profile.learningGoal.learningGoal": "Obiettivo di apprendimento",
"profile.learningGoal.options.start_career": "Voglio iniziare il mio percorso",
"profile.learningGoal.options.advance_career": "Voglio avanzare nel mio percorso",
"profile.learningGoal.options.learn_something_new": "Voglio imparare qualcosa di nuovo",
"profile.learningGoal.options.something_else": "Qualcos'altro",
"profile.name.full.name": "Nome e Cognome",
"profile.name.details": "Questo è il nome visualizzato nel proprio account e nei propri certificati. ",
"profile.name.empty": "Aggiungi nome ",
"profile.preferredlanguage.empty": "Aggiungi lingua",
"profile.preferredlanguage.label": "Lingua principale ",
"profile.profileavatar.upload-button": "Carica foto",
"profile.profileavatar.remove.button": "Rimuovi",
"profile.image.alt.attribute": "avatar del profilo ",
"profile.profileavatar.change-button": "Cambia",
"profile.sociallinks.add": "Aggiungi {network}",
"profile.sociallinks.social.links": "Link social ",
"profile.notfound.message": "La pagina ricercata non è disponibile oppure è presente un errore nell'URL. Controllare l'URL e riprovare. ",
"profile.viewMyRecords": "Visualizza record personali ",
"profile.loading": "Caricamento del profilo... ",
"profile.username.description": "Le informazioni del tuo profilo sono visibili solo a te. Solo il tuo nome utente è visibile agli altri su {siteName}."
}

View File

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

View File

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

View File

@@ -1,57 +0,0 @@
{
"profile.page.title": "Profile | {siteName}",
"profile.age.details": "To share your profile with other {siteName} 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 {siteName}",
"profile.learningGoal.learningGoal": "Learning Goal",
"profile.learningGoal.options.start_career": "I want to start my career",
"profile.learningGoal.options.advance_career": "I want to advance my career",
"profile.learningGoal.options.learn_something_new": "I want to learn something new",
"profile.learningGoal.options.something_else": "Something else",
"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.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}."
}

View File

@@ -1,57 +0,0 @@
{
"profile.page.title": "Perfil | {siteName}",
"profile.age.details": "Para partilhar o seu perfil com outros estudantes da plataforma {siteName}, tem de confirmar que tem mais de 13 anos de idade.",
"profile.age.set.date": "Indique a sua data de nascimento",
"profile.datejoined.member.since": "Utilizador desde {year}",
"profile.bio.empty": "Adicionar uma breve biografia",
"profile.bio.about.me": "Sobre Mim",
"profile.certificate.organization.label": "De",
"profile.certificate.completion.date.label": "Concluído a {date}",
"profile.no.certificates": "Ainda não tem certificados.",
"profile.certificates.my.certificates": "Os Meus Certificados",
"profile.certificates.view.certificate": "Ver Certificado",
"profile.certificates.types.verified": "Certificado Validado",
"profile.certificates.types.professional": "Certificado Profissional",
"profile.certificates.types.unknown": "Certificado",
"profile.country.label": "Localização",
"profile.country.empty": "Adicionar localização",
"profile.education.empty": "Adicionar grau de escolaridade",
"profile.education.education": "Educação",
"profile.education.levels.p": "Doutoramento",
"profile.education.levels.m": "Mestrado ou Grau Profissional",
"profile.education.levels.b": "Licenciatura",
"profile.education.levels.a": "Pós-graduação",
"profile.education.levels.hs": "Secundário",
"profile.education.levels.jhs": "2ªciclo/3ºciclo",
"profile.education.levels.el": "Primária",
"profile.education.levels.none": "Sem estudos",
"profile.education.levels.o": "Outra formação",
"profile.editbutton.edit": "Editar",
"profile.formcontrols.who.can.see": "Quem pode ver isto:",
"profile.formcontrols.button.cancel": "Cancelar",
"profile.formcontrols.button.save": "Guardar",
"profile.formcontrols.button.saving": "A Guardar",
"profile.formcontrols.button.saved": "Guardado",
"profile.visibility.who.just.me": "Apenas eu",
"profile.visibility.who.everyone": "Toda a gente em {siteName}",
"profile.learningGoal.learningGoal": "Objectivo de aprendizagem",
"profile.learningGoal.options.start_career": "Quero começar a minha carreira",
"profile.learningGoal.options.advance_career": "Quero progredir na minha carreira",
"profile.learningGoal.options.learn_something_new": "Quero aprender algo novo",
"profile.learningGoal.options.something_else": "Outra coisa",
"profile.name.full.name": "Nome Completo",
"profile.name.details": "Este é o nome que aparece na sua conta e nos seus certificados.",
"profile.name.empty": "Adicionar nome",
"profile.preferredlanguage.empty": "Adicionar idioma",
"profile.preferredlanguage.label": "Língua Materna",
"profile.profileavatar.upload-button": "Carregar Fotografia",
"profile.profileavatar.remove.button": "Eliminar",
"profile.image.alt.attribute": "Icon de perfil",
"profile.profileavatar.change-button": "Alterar",
"profile.sociallinks.add": "Adicionar {network}",
"profile.sociallinks.social.links": "Links de Redes Sociais",
"profile.notfound.message": "A página que procura não está disponível ou há um erro no URL. Por favor, verifique o URL e tente novamente.",
"profile.viewMyRecords": "Ver os Meus Registos",
"profile.loading": "A carregar perfil...",
"profile.username.description": "As informações do seu perfil só são visíveis para si. Apenas o seu nome de utilizador é visível para outros em {siteName}."
}

View File

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

View File

@@ -1,57 +1 @@
{ {}
"profile.page.title": "Profile | {siteName}",
"profile.age.details": "To share your profile with other {siteName} 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 {siteName}",
"profile.learningGoal.learningGoal": "Learning Goal",
"profile.learningGoal.options.start_career": "I want to start my career",
"profile.learningGoal.options.advance_career": "I want to advance my career",
"profile.learningGoal.options.learn_something_new": "I want to learn something new",
"profile.learningGoal.options.something_else": "Something else",
"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.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}."
}

View File

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

View File

@@ -1,57 +1 @@
{ {}
"profile.page.title": "Profile | {siteName}",
"profile.age.details": "To share your profile with other {siteName} 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": "Мої сертифікати",
"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 {siteName}",
"profile.learningGoal.learningGoal": "Learning Goal",
"profile.learningGoal.options.start_career": "I want to start my career",
"profile.learningGoal.options.advance_career": "I want to advance my career",
"profile.learningGoal.options.learn_something_new": "I want to learn something new",
"profile.learningGoal.options.something_else": "Something else",
"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.username.description": "Your profile information is only visible to you. Only your username is visible to others on {siteName}."
}

View File

@@ -1,5 +1,5 @@
{ {
"profile.page.title": "Profile | {siteName}", "profile.age.headline": "Your profile cannot be shared.",
"profile.age.details": "To share your profile with other {siteName} learners, you must confirm that you are over the age of 13.", "profile.age.details": "To share your profile with other {siteName} learners, you must confirm that you are over the age of 13.",
"profile.age.set.date": "Set your date of birth", "profile.age.set.date": "Set your date of birth",
"profile.datejoined.member.since": "Member since {year}", "profile.datejoined.member.since": "Member since {year}",
@@ -34,11 +34,6 @@
"profile.formcontrols.button.saved": "Saved", "profile.formcontrols.button.saved": "Saved",
"profile.visibility.who.just.me": "Just me", "profile.visibility.who.just.me": "Just me",
"profile.visibility.who.everyone": "Everyone on {siteName}", "profile.visibility.who.everyone": "Everyone on {siteName}",
"profile.learningGoal.learningGoal": "Learning Goal",
"profile.learningGoal.options.start_career": "I want to start my career",
"profile.learningGoal.options.advance_career": "I want to advance my career",
"profile.learningGoal.options.learn_something_new": "I want to learn something new",
"profile.learningGoal.options.something_else": "Something else",
"profile.name.full.name": "Full Name", "profile.name.full.name": "Full Name",
"profile.name.details": "This is the name that appears in your account and on your certificates.", "profile.name.details": "This is the name that appears in your account and on your certificates.",
"profile.name.empty": "Add name", "profile.name.empty": "Add name",

View File

@@ -15,38 +15,29 @@ import {
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
import { useLocation } from 'react-router-dom'; import { Route, Switch } from 'react-router-dom';
import Header from '@edx/frontend-component-header'; import Header, { messages as headerMessages } from '@edx/frontend-component-header';
import Footer from '@edx/frontend-component-footer'; import Footer, { messages as footerMessages } from '@edx/frontend-component-footer';
import messages from './i18n'; import appMessages from './i18n';
import { ProfilePage, NotFoundPage } from './profile';
import configureStore from './data/configureStore'; import configureStore from './data/configureStore';
import './index.scss'; import './index.scss';
import Head from './head/Head';
import AppRoutes from './routes/AppRoutes';
const RenderFooter = () => {
const location = useLocation();
return location.pathname.includes('/plugin') ? null : <Footer />;
};
const RenderHeader = () => {
const location = useLocation();
return location.pathname.includes('/plugin') ? null : <Header />;
};
subscribe(APP_READY, () => { subscribe(APP_READY, () => {
ReactDOM.render( ReactDOM.render(
<AppProvider store={configureStore()}> <AppProvider store={configureStore()}>
<Head /> <Header />
<RenderHeader /> <main>
<main id="main"> <Switch>
<AppRoutes /> <Route path="/u/:username" component={ProfilePage} />
<Route path="/notfound" component={NotFoundPage} />
<Route path="*" component={NotFoundPage} />
</Switch>
</main> </main>
<RenderFooter /> <Footer />
</AppProvider>, </AppProvider>,
document.getElementById('root'), document.getElementById('root'),
); );
@@ -57,13 +48,19 @@ subscribe(APP_INIT_ERROR, (error) => {
}); });
initialize({ initialize({
messages, messages: [
appMessages,
headerMessages,
footerMessages,
],
requireAuthenticatedUser: true,
hydrateAuthenticatedUser: true, hydrateAuthenticatedUser: true,
handlers: { handlers: {
config: () => { config: () => {
mergeConfig({ mergeConfig({
ENABLE_LEARNER_RECORD_MFE: (process.env.ENABLE_LEARNER_RECORD_MFE || false),
LEARNER_RECORD_MFE_BASE_URL: process.env.LEARNER_RECORD_MFE_BASE_URL,
COLLECT_YEAR_OF_BIRTH: process.env.COLLECT_YEAR_OF_BIRTH, COLLECT_YEAR_OF_BIRTH: process.env.COLLECT_YEAR_OF_BIRTH,
ENABLE_SKILLS_BUILDER_PROFILE: process.env.ENABLE_SKILLS_BUILDER_PROFILE,
}, 'App loadConfig override handler'); }, 'App loadConfig override handler');
}, },
}, },

View File

@@ -1,77 +0,0 @@
{
"consumer": {
"name": "frontend-app-profile"
},
"interactions": [
{
"description": "A request for user's basic information",
"providerStates": [
{
"name": "Account and user's information does not exist"
}
],
"request": {
"method": "GET",
"path": "/api/user/v1/accounts/staff_not_found"
},
"response": {
"status": 404
}
},
{
"description": "A request for user's basic information",
"providerStates": [
{
"name": "I have a user's basic information"
}
],
"request": {
"method": "GET",
"path": "/api/user/v1/accounts/staff"
},
"response": {
"body": {
"bio": "This is my bio",
"country": "ME",
"dateJoined": "2017-06-07T00:44:23Z",
"email": "staff@example.com",
"isActive": true,
"name": "Lemon Seltzer",
"username": "staff",
"yearOfBirth": 1901
},
"headers": {
"Content-Type": "application/json"
},
"matchingRules": {
"body": {
"$": {
"combine": "AND",
"matchers": [
{
"match": "type"
}
]
}
}
},
"status": 200
}
}
],
"metadata": {
"pact-js": {
"version": "11.0.2"
},
"pactRust": {
"ffi": "0.4.0",
"models": "1.0.4"
},
"pactSpecification": {
"version": "3.0.0"
}
},
"provider": {
"name": "edx-platform"
}
}

View File

@@ -1,18 +1,21 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Alert } from '@edx/paragon'; import { StatusAlert } from '@edx/paragon';
import { FormattedMessage } from '@edx/frontend-platform/i18n'; import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { getConfig } from '@edx/frontend-platform'; import { getConfig } from '@edx/frontend-platform';
const AgeMessage = ({ accountSettingsUrl }) => ( function AgeMessage({ accountSettingsUrl }) {
<Alert return (
variant="info" <StatusAlert
dismissible={false} alertType="info"
show dialog={(
> <>
<Alert.Heading id="profile.age.headline"> <FormattedMessage
Your profile cannot be shared. id="profile.age.headline"
</Alert.Heading> defaultMessage="Your profile cannot be shared."
description="error message"
tagName="h6"
/>
<FormattedMessage <FormattedMessage
id="profile.age.details" id="profile.age.details"
defaultMessage="To share your profile with other {siteName} learners, you must confirm that you are over the age of 13." defaultMessage="To share your profile with other {siteName} learners, you must confirm that you are over the age of 13."
@@ -22,15 +25,20 @@ const AgeMessage = ({ accountSettingsUrl }) => (
siteName: getConfig().SITE_NAME, siteName: getConfig().SITE_NAME,
}} }}
/> />
<Alert.Link href={accountSettingsUrl}> <a href={accountSettingsUrl}>
<FormattedMessage <FormattedMessage
id="profile.age.set.date" id="profile.age.set.date"
defaultMessage="Set your date of birth" defaultMessage="Set your date of birth"
description="Label on a link to set birthday" description="Label on a link to set birthday"
/> />
</Alert.Link> </a>
</Alert> </>
)}
dismissible={false}
open
/>
); );
}
AgeMessage.propTypes = { AgeMessage.propTypes = {
accountSettingsUrl: PropTypes.string.isRequired, accountSettingsUrl: PropTypes.string.isRequired,

View File

@@ -1,5 +1,7 @@
import React from 'react'; import React from 'react';
const Banner = () => <div className="profile-page-bg-banner bg-primary d-none d-md-block p-relative" />; function Banner() {
return <div className="profile-page-bg-banner bg-primary d-none d-md-block p-relative" />;
}
export default Banner; export default Banner;

View File

@@ -2,10 +2,11 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { FormattedMessage, FormattedDate } from '@edx/frontend-platform/i18n'; import { FormattedMessage, FormattedDate } from '@edx/frontend-platform/i18n';
const DateJoined = ({ date }) => { function DateJoined({ date }) {
if (date == null) { if (date == null) {
return null; return null;
} }
return ( return (
<p className="mb-0"> <p className="mb-0">
<FormattedMessage <FormattedMessage
@@ -18,7 +19,7 @@ const DateJoined = ({ date }) => {
/> />
</p> </p>
); );
}; }
DateJoined.propTypes = { DateJoined.propTypes = {
date: PropTypes.string, date: PropTypes.string,

View File

@@ -1,7 +1,8 @@
import React from 'react'; import React from 'react';
import { FormattedMessage } from '@edx/frontend-platform/i18n'; import { FormattedMessage } from '@edx/frontend-platform/i18n';
const NotFoundPage = () => ( export default function NotFoundPage() {
return (
<div className="container-fluid d-flex py-5 justify-content-center align-items-start text-center"> <div className="container-fluid d-flex py-5 justify-content-center align-items-start text-center">
<p className="my-0 py-5 text-muted" style={{ maxWidth: '32em' }}> <p className="my-0 py-5 text-muted" style={{ maxWidth: '32em' }}>
<FormattedMessage <FormattedMessage
@@ -12,5 +13,4 @@ const NotFoundPage = () => (
</p> </p>
</div> </div>
); );
}
export default NotFoundPage;

View File

@@ -6,7 +6,7 @@ import { sendTrackingLogEvent } from '@edx/frontend-platform/analytics';
import { ensureConfig, getConfig } from '@edx/frontend-platform'; import { ensureConfig, getConfig } from '@edx/frontend-platform';
import { AppContext } from '@edx/frontend-platform/react'; import { AppContext } from '@edx/frontend-platform/react';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Alert, Hyperlink } from '@edx/paragon'; import { StatusAlert, Hyperlink } from '@edx/paragon';
// Actions // Actions
import { import {
@@ -33,7 +33,6 @@ import DateJoined from './DateJoined';
import UsernameDescription from './UsernameDescription'; import UsernameDescription from './UsernameDescription';
import PageLoading from './PageLoading'; import PageLoading from './PageLoading';
import Banner from './Banner'; import Banner from './Banner';
import LearningGoal from './forms/LearningGoal';
// Selectors // Selectors
import { profilePageSelector } from './data/selectors'; import { profilePageSelector } from './data/selectors';
@@ -41,18 +40,16 @@ import { profilePageSelector } from './data/selectors';
// i18n // i18n
import messages from './ProfilePage.messages'; import messages from './ProfilePage.messages';
import withParams from '../utils/hoc';
ensureConfig(['CREDENTIALS_BASE_URL', 'LMS_BASE_URL'], 'ProfilePage'); ensureConfig(['CREDENTIALS_BASE_URL', 'LMS_BASE_URL'], 'ProfilePage');
class ProfilePage extends React.Component { class ProfilePage extends React.Component {
constructor(props, context) { constructor(props, context) {
super(props, context); super(props, context);
const credentialsBaseUrl = context.config.CREDENTIALS_BASE_URL; const recordsUrl = this.getRecordsUrl(context);
this.state = { this.state = {
viewMyRecordsUrl: credentialsBaseUrl ? `${credentialsBaseUrl}/records` : null, viewMyRecordsUrl: recordsUrl,
accountSettingsUrl: `${context.config.LMS_BASE_URL}/account/settings`, accountSettingsUrl: `${context.config.LMS_BASE_URL}/account/settings`,
}; };
@@ -65,12 +62,37 @@ class ProfilePage extends React.Component {
} }
componentDidMount() { componentDidMount() {
this.props.fetchProfile(this.props.params.username); this.props.fetchProfile(this.props.match.params.username);
sendTrackingLogEvent('edx.profile.viewed', { sendTrackingLogEvent('edx.profile.viewed', {
username: this.props.params.username, username: this.props.match.params.username,
}); });
} }
getRecordsUrl(context) {
let recordsUrl = null;
if (getConfig().ENABLE_LEARNER_RECORD_MFE) {
recordsUrl = getConfig().LEARNER_RECORD_MFE_BASE_URL;
} else {
const credentialsBaseUrl = context.config.CREDENTIALS_BASE_URL;
recordsUrl = credentialsBaseUrl ? `${credentialsBaseUrl}/records` : null;
}
return recordsUrl;
}
isYOBDisabled() {
const { yearOfBirth } = this.props;
const currentYear = new Date().getFullYear();
const isAgeOrNotCompliant = !yearOfBirth || ((currentYear - yearOfBirth) < 13);
return isAgeOrNotCompliant && getConfig().COLLECT_YEAR_OF_BIRTH !== 'true';
}
isAuthenticatedUserProfile() {
return this.props.match.params.username === this.context.authenticatedUser.username;
}
handleSaveProfilePhoto(formData) { handleSaveProfilePhoto(formData) {
this.props.saveProfilePhoto(this.context.authenticatedUser.username, formData); this.props.saveProfilePhoto(this.context.authenticatedUser.username, formData);
} }
@@ -95,18 +117,6 @@ class ProfilePage extends React.Component {
this.props.updateDraft(name, value); this.props.updateDraft(name, value);
} }
isYOBDisabled() {
const { yearOfBirth } = this.props;
const currentYear = new Date().getFullYear();
const isAgeOrNotCompliant = !yearOfBirth || ((currentYear - yearOfBirth) < 13);
return isAgeOrNotCompliant && getConfig().COLLECT_YEAR_OF_BIRTH !== 'true';
}
isAuthenticatedUserProfile() {
return this.props.params.username === this.context.authenticatedUser.username;
}
// Inserted into the DOM in two places (for responsive layout) // Inserted into the DOM in two places (for responsive layout)
renderViewMyRecordsButton() { renderViewMyRecordsButton() {
if (!(this.state.viewMyRecordsUrl && this.isAuthenticatedUserProfile())) { if (!(this.state.viewMyRecordsUrl && this.isAuthenticatedUserProfile())) {
@@ -125,12 +135,14 @@ class ProfilePage extends React.Component {
const { dateJoined } = this.props; const { dateJoined } = this.props;
return ( return (
<>
<span data-hj-suppress> <span data-hj-suppress>
<h1 className="h2 mb-0 font-weight-bold text-truncate">{this.props.params.username}</h1> <h1 className="h2 mb-0 font-weight-bold">{this.props.match.params.username}</h1>
<DateJoined date={dateJoined} /> <DateJoined date={dateJoined} />
{this.isYOBDisabled() && <UsernameDescription />} {this.isYOBDisabled() && <UsernameDescription />}
<hr className="d-none d-md-block" /> <hr className="d-none d-md-block" />
</span> </span>
</>
); );
} }
@@ -144,9 +156,7 @@ class ProfilePage extends React.Component {
return ( return (
<div className="row"> <div className="row">
<div className="col-md-4 col-lg-3"> <div className="col-md-4 col-lg-3">
<Alert variant="danger" dismissible={false} show> <StatusAlert alertType="danger" dialog={photoUploadError.userMessage} dismissible={false} open />
{photoUploadError.userMessage}
</Alert>
</div> </div>
</div> </div>
); );
@@ -174,11 +184,8 @@ class ProfilePage extends React.Component {
socialLinks, socialLinks,
draftSocialLinksByPlatform, draftSocialLinksByPlatform,
visibilitySocialLinks, visibilitySocialLinks,
learningGoal,
visibilityLearningGoal,
languageProficiencies, languageProficiencies,
visibilityLanguageProficiencies, visibilityLanguageProficiencies,
courseCertificates,
visibilityCourseCertificates, visibilityCourseCertificates,
bio, bio,
visibilityBio, visibilityBio,
@@ -197,17 +204,6 @@ class ProfilePage extends React.Component {
changeHandler: this.handleChange, changeHandler: this.handleChange,
}; };
const isBlockVisible = (blockInfo) => this.isAuthenticatedUserProfile()
|| (!this.isAuthenticatedUserProfile() && Boolean(blockInfo));
const isLanguageBlockVisible = isBlockVisible(languageProficiencies.length);
const isEducationBlockVisible = isBlockVisible(levelOfEducation);
const isSocialLinksBLockVisible = isBlockVisible(socialLinks.some((link) => link.socialLink !== null));
const isBioBlockVisible = isBlockVisible(bio);
const isCertificatesBlockVisible = isBlockVisible(courseCertificates.length);
const isNameBlockVisible = isBlockVisible(name);
const isLocationBlockVisible = isBlockVisible(country);
return ( return (
<div className="container-fluid"> <div className="container-fluid">
<div className="row align-items-center pt-4 mb-4 pt-md-0 mb-md-0"> <div className="row align-items-center pt-4 mb-4 pt-md-0 mb-md-0">
@@ -224,8 +220,7 @@ class ProfilePage extends React.Component {
/> />
</div> </div>
</div> </div>
<div>PluginPOC</div> <div className="col pl-0">
<div className="col">
<div className="d-md-none"> <div className="d-md-none">
{this.renderHeadingLockup()} {this.renderHeadingLockup()}
</div> </div>
@@ -243,39 +238,30 @@ class ProfilePage extends React.Component {
<div className="d-md-none mb-4"> <div className="d-md-none mb-4">
{this.renderViewMyRecordsButton()} {this.renderViewMyRecordsButton()}
</div> </div>
{isNameBlockVisible && (
<Name <Name
name={name} name={name}
visibilityName={visibilityName} visibilityName={visibilityName}
formId="name" formId="name"
{...commonFormProps} {...commonFormProps}
/> />
)}
{isLocationBlockVisible && (
<Country <Country
country={country} country={country}
visibilityCountry={visibilityCountry} visibilityCountry={visibilityCountry}
formId="country" formId="country"
{...commonFormProps} {...commonFormProps}
/> />
)}
{isLanguageBlockVisible && (
<PreferredLanguage <PreferredLanguage
languageProficiencies={languageProficiencies} languageProficiencies={languageProficiencies}
visibilityLanguageProficiencies={visibilityLanguageProficiencies} visibilityLanguageProficiencies={visibilityLanguageProficiencies}
formId="languageProficiencies" formId="languageProficiencies"
{...commonFormProps} {...commonFormProps}
/> />
)}
{isEducationBlockVisible && (
<Education <Education
levelOfEducation={levelOfEducation} levelOfEducation={levelOfEducation}
visibilityLevelOfEducation={visibilityLevelOfEducation} visibilityLevelOfEducation={visibilityLevelOfEducation}
formId="levelOfEducation" formId="levelOfEducation"
{...commonFormProps} {...commonFormProps}
/> />
)}
{isSocialLinksBLockVisible && (
<SocialLinks <SocialLinks
socialLinks={socialLinks} socialLinks={socialLinks}
draftSocialLinksByPlatform={draftSocialLinksByPlatform} draftSocialLinksByPlatform={draftSocialLinksByPlatform}
@@ -283,33 +269,20 @@ class ProfilePage extends React.Component {
formId="socialLinks" formId="socialLinks"
{...commonFormProps} {...commonFormProps}
/> />
)}
</div> </div>
<div className="pt-md-3 col-md-8 col-lg-7 offset-lg-1"> <div className="pt-md-3 col-md-8 col-lg-7 offset-lg-1">
{!this.isYOBDisabled() && this.renderAgeMessage()} {!this.isYOBDisabled() && this.renderAgeMessage()}
{isBioBlockVisible && (
<Bio <Bio
bio={bio} bio={bio}
visibilityBio={visibilityBio} visibilityBio={visibilityBio}
formId="bio" formId="bio"
{...commonFormProps} {...commonFormProps}
/> />
)}
{getConfig().ENABLE_SKILLS_BUILDER_PROFILE && (
<LearningGoal
learningGoal={learningGoal}
visibilityLearningGoal={visibilityLearningGoal}
formId="learningGoal"
{...commonFormProps}
/>
)}
{isCertificatesBlockVisible && (
<Certificates <Certificates
visibilityCourseCertificates={visibilityCourseCertificates} visibilityCourseCertificates={visibilityCourseCertificates}
formId="certificates" formId="certificates"
{...commonFormProps} {...commonFormProps}
/> />
)}
</div> </div>
</div> </div>
</div> </div>
@@ -373,10 +346,6 @@ ProfilePage.propTypes = {
})), })),
visibilitySocialLinks: PropTypes.string.isRequired, visibilitySocialLinks: PropTypes.string.isRequired,
// Learning Goal form data
learningGoal: PropTypes.string,
visibilityLearningGoal: PropTypes.string.isRequired,
// Other data we need // Other data we need
profileImage: PropTypes.shape({ profileImage: PropTypes.shape({
src: PropTypes.string, src: PropTypes.string,
@@ -399,9 +368,11 @@ ProfilePage.propTypes = {
updateDraft: PropTypes.func.isRequired, updateDraft: PropTypes.func.isRequired,
// Router // Router
match: PropTypes.shape({
params: PropTypes.shape({ params: PropTypes.shape({
username: PropTypes.string.isRequired, username: PropTypes.string.isRequired,
}).isRequired, }).isRequired,
}).isRequired,
// i18n // i18n
intl: intlShape.isRequired, intl: intlShape.isRequired,
@@ -419,7 +390,6 @@ ProfilePage.defaultProps = {
socialLinks: [], socialLinks: [],
draftSocialLinksByPlatform: {}, draftSocialLinksByPlatform: {},
bio: null, bio: null,
learningGoal: null,
languageProficiencies: [], languageProficiencies: [],
courseCertificates: null, courseCertificates: null,
requiresParentalConsent: null, requiresParentalConsent: null,
@@ -437,4 +407,4 @@ export default connect(
closeForm, closeForm,
updateDraft, updateDraft,
}, },
)(injectIntl(withParams(ProfilePage))); )(injectIntl(ProfilePage));

View File

@@ -5,7 +5,6 @@ import { AppContext } from '@edx/frontend-platform/react';
import { configure as configureI18n, IntlProvider } from '@edx/frontend-platform/i18n'; import { configure as configureI18n, IntlProvider } from '@edx/frontend-platform/i18n';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import renderer from 'react-test-renderer'; import renderer from 'react-test-renderer';
import configureMockStore from 'redux-mock-store'; import configureMockStore from 'redux-mock-store';
@@ -16,10 +15,10 @@ import ProfilePage from './ProfilePage';
const mockStore = configureMockStore([thunk]); const mockStore = configureMockStore([thunk]);
const storeMocks = { const storeMocks = {
loadingApp: require('./__mocks__/loadingApp.mockStore'), loadingApp: require('./__mocks__/loadingApp.mockStore.js'),
viewOwnProfile: require('./__mocks__/viewOwnProfile.mockStore'), viewOwnProfile: require('./__mocks__/viewOwnProfile.mockStore.js'),
viewOtherProfile: require('./__mocks__/viewOtherProfile.mockStore'), viewOtherProfile: require('./__mocks__/viewOtherProfile.mockStore.js'),
savingEditedBio: require('./__mocks__/savingEditedBio.mockStore'), savingEditedBio: require('./__mocks__/savingEditedBio.mockStore.js'),
}; };
const requiredProfilePageProps = { const requiredProfilePageProps = {
fetchUserAccount: () => {}, fetchUserAccount: () => {},
@@ -29,7 +28,7 @@ const requiredProfilePageProps = {
deleteProfilePhoto: () => {}, deleteProfilePhoto: () => {},
openField: () => {}, openField: () => {},
closeField: () => {}, closeField: () => {},
params: { username: 'staff' }, match: { params: { username: 'staff' } },
}; };
// Mock language cookie // Mock language cookie
@@ -66,169 +65,82 @@ beforeEach(() => {
analytics.sendTrackingLogEvent.mockReset(); analytics.sendTrackingLogEvent.mockReset();
}); });
const ProfilePageWrapper = ({ describe('<ProfilePage />', () => {
contextValue, store, params, requiresParentalConsent, describe('Renders correctly in various states', () => {
}) => ( it('app loading', () => {
const component = (
<AppContext.Provider <AppContext.Provider
value={contextValue} value={{
authenticatedUser: { userId: null, username: null, administrator: false },
config: getConfig(),
}}
> >
<IntlProvider locale="en"> <IntlProvider locale="en">
<Provider store={store}> <Provider store={mockStore(storeMocks.loadingApp)}>
<ProfilePage {...requiredProfilePageProps} params={params} requiresParentalConsent={requiresParentalConsent} /> <ProfilePage {...requiredProfilePageProps} />
</Provider> </Provider>
</IntlProvider> </IntlProvider>
</AppContext.Provider> </AppContext.Provider>
); );
ProfilePageWrapper.defaultProps = {
params: { username: 'staff' },
requiresParentalConsent: null,
};
ProfilePageWrapper.propTypes = {
contextValue: PropTypes.shape({}).isRequired,
store: PropTypes.shape({}).isRequired,
params: PropTypes.shape({}),
requiresParentalConsent: PropTypes.bool,
};
describe('<ProfilePage />', () => {
describe('Renders correctly in various states', () => {
it('app loading', () => {
const contextValue = {
authenticatedUser: { userId: null, username: null, administrator: false },
config: getConfig(),
};
const component = <ProfilePageWrapper contextValue={contextValue} store={mockStore(storeMocks.loadingApp)} />;
const tree = renderer.create(component).toJSON(); const tree = renderer.create(component).toJSON();
expect(tree).toMatchSnapshot(); expect(tree).toMatchSnapshot();
}); });
it('viewing own profile', () => { it('viewing own profile', () => {
const contextValue = { const component = (
<AppContext.Provider
value={{
authenticatedUser: { userId: 123, username: 'staff', administrator: true }, authenticatedUser: { userId: 123, username: 'staff', administrator: true },
config: getConfig(), config: getConfig(),
}; }}
const component = <ProfilePageWrapper contextValue={contextValue} store={mockStore(storeMocks.viewOwnProfile)} />; >
<IntlProvider locale="en">
<Provider store={mockStore(storeMocks.viewOwnProfile)}>
<ProfilePage {...requiredProfilePageProps} />
</Provider>
</IntlProvider>
</AppContext.Provider>
);
const tree = renderer.create(component).toJSON(); const tree = renderer.create(component).toJSON();
expect(tree).toMatchSnapshot(); expect(tree).toMatchSnapshot();
}); });
it('viewing other profile with all fields', () => { it('viewing other profile', () => {
const contextValue = { const component = (
<AppContext.Provider
value={{
authenticatedUser: { userId: 123, username: 'staff', administrator: true }, authenticatedUser: { userId: 123, username: 'staff', administrator: true },
config: getConfig(), config: getConfig(),
}; }}
>
const component = ( <IntlProvider locale="en">
<ProfilePageWrapper <Provider store={mockStore(storeMocks.viewOtherProfile)}>
contextValue={contextValue} <ProfilePage
store={mockStore({ {...requiredProfilePageProps}
...storeMocks.viewOtherProfile,
profilePage: {
...storeMocks.viewOtherProfile.profilePage,
account: {
...storeMocks.viewOtherProfile.profilePage.account,
name: 'user',
country: 'EN',
bio: 'bio',
courseCertificates: ['course certificates'],
levelOfEducation: 'some level',
languageProficiencies: ['some lang'],
socialLinks: ['twitter'],
timeZone: 'time zone',
accountPrivacy: 'all_users',
},
},
})}
match={{ params: { username: 'verified' } }} // Override default match match={{ params: { username: 'verified' } }} // Override default match
/> />
</Provider>
</IntlProvider>
</AppContext.Provider>
); );
const tree = renderer.create(component).toJSON(); const tree = renderer.create(component).toJSON();
expect(tree).toMatchSnapshot(); expect(tree).toMatchSnapshot();
}); });
it('while saving an edited bio', () => { it('while saving an edited bio', () => {
const contextValue = { const component = (
<AppContext.Provider
value={{
authenticatedUser: { userId: 123, username: 'staff', administrator: true }, authenticatedUser: { userId: 123, username: 'staff', administrator: true },
config: getConfig(), config: getConfig(),
}; }}
const component = ( >
<ProfilePageWrapper <IntlProvider locale="en">
contextValue={contextValue} <Provider store={mockStore(storeMocks.savingEditedBio)}>
store={mockStore(storeMocks.savingEditedBio)} <ProfilePage {...requiredProfilePageProps} />
/> </Provider>
); </IntlProvider>
const tree = renderer.create(component).toJSON(); </AppContext.Provider>
expect(tree).toMatchSnapshot();
});
it('while saving an edited bio with error', () => {
const storeData = JSON.parse(JSON.stringify(storeMocks.savingEditedBio));
storeData.profilePage.errors.bio = { userMessage: 'bio error' };
const contextValue = {
authenticatedUser: { userId: 123, username: 'staff', administrator: true },
config: getConfig(),
};
const component = (
<ProfilePageWrapper
contextValue={contextValue}
store={mockStore(storeData)}
/>
);
const tree = renderer.create(component).toJSON();
expect(tree).toMatchSnapshot();
});
it('test country edit with error', () => {
const storeData = JSON.parse(JSON.stringify(storeMocks.savingEditedBio));
storeData.profilePage.errors.country = { userMessage: 'country error' };
storeData.profilePage.currentlyEditingField = 'country';
const contextValue = {
authenticatedUser: { userId: 123, username: 'staff', administrator: true },
config: getConfig(),
};
const component = (
<ProfilePageWrapper
contextValue={contextValue}
store={mockStore(storeData)}
/>
);
const tree = renderer.create(component).toJSON();
expect(tree).toMatchSnapshot();
});
it('test education edit with error', () => {
const storeData = JSON.parse(JSON.stringify(storeMocks.savingEditedBio));
storeData.profilePage.errors.levelOfEducation = { userMessage: 'education error' };
storeData.profilePage.currentlyEditingField = 'levelOfEducation';
const contextValue = {
authenticatedUser: { userId: 123, username: 'staff', administrator: true },
config: getConfig(),
};
const component = (
<ProfilePageWrapper
contextValue={contextValue}
store={mockStore(storeData)}
/>
);
const tree = renderer.create(component).toJSON();
expect(tree).toMatchSnapshot();
});
it('test preferreded language edit with error', () => {
const storeData = JSON.parse(JSON.stringify(storeMocks.savingEditedBio));
storeData.profilePage.errors.languageProficiencies = { userMessage: 'preferred language error' };
storeData.profilePage.currentlyEditingField = 'languageProficiencies';
const contextValue = {
authenticatedUser: { userId: 123, username: 'staff', administrator: true },
config: getConfig(),
};
const component = (
<ProfilePageWrapper
contextValue={contextValue}
store={mockStore(storeData)}
/>
); );
const tree = renderer.create(component).toJSON(); const tree = renderer.create(component).toJSON();
expect(tree).toMatchSnapshot(); expect(tree).toMatchSnapshot();
@@ -238,66 +150,43 @@ describe('<ProfilePage />', () => {
const config = getConfig(); const config = getConfig();
config.CREDENTIALS_BASE_URL = ''; config.CREDENTIALS_BASE_URL = '';
const contextValue = {
authenticatedUser: { userId: 123, username: 'staff', administrator: true },
config: getConfig(),
};
const component = ( const component = (
<ProfilePageWrapper <AppContext.Provider
contextValue={contextValue} value={{
store={mockStore(storeMocks.viewOwnProfile)} 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(); const tree = renderer.create(component).toJSON();
expect(tree).toMatchSnapshot(); expect(tree).toMatchSnapshot();
}); });
it('test age message alert', () => {
const storeData = JSON.parse(JSON.stringify(storeMocks.viewOwnProfile));
storeData.userAccount.requiresParentalConsent = true;
storeData.profilePage.account.requiresParentalConsent = true;
const contextValue = {
authenticatedUser: { userId: 123, username: 'staff', administrator: true },
config: { ...getConfig(), COLLECT_YEAR_OF_BIRTH: true },
};
const component = (
<ProfilePageWrapper
contextValue={contextValue}
store={mockStore(storeData)}
requiresParentalConsent
/>
);
const wrapper = mount(component);
wrapper.update();
expect(wrapper.find('.alert-info').hasClass('show')).toBe(true);
});
it('test photo error alert', () => {
const storeData = JSON.parse(JSON.stringify(storeMocks.viewOwnProfile));
storeData.profilePage.errors.photo = { userMessage: 'error' };
const contextValue = {
authenticatedUser: { userId: 123, username: 'staff', administrator: true },
config: { ...getConfig(), COLLECT_YEAR_OF_BIRTH: true },
};
const component = <ProfilePageWrapper contextValue={contextValue} store={mockStore(storeData)} />;
const wrapper = mount(component);
wrapper.update();
expect(wrapper.find('.alert-danger').hasClass('show')).toBe(true);
});
}); });
describe('handles analytics', () => { describe('handles analytics', () => {
it('calls sendTrackingLogEvent when mounting', () => { it('calls sendTrackingLogEvent when mounting', () => {
const contextValue = { const component = (
<AppContext.Provider
value={{
authenticatedUser: { userId: 123, username: 'staff', administrator: true }, authenticatedUser: { userId: 123, username: 'staff', administrator: true },
config: getConfig(), config: getConfig(),
}; }}
const component = ( >
<ProfilePageWrapper <IntlProvider locale="en">
contextValue={contextValue} <Provider store={mockStore(storeMocks.loadingApp)}>
store={mockStore(storeMocks.loadingApp)} <ProfilePage
params={{ username: 'test-username' }} {...requiredProfilePageProps}
match={{ params: { username: 'test-username' } }}
/> />
</Provider>
</IntlProvider>
</AppContext.Provider>
); );
const wrapper = mount(component); const wrapper = mount(component);
wrapper.update(); wrapper.update();

View File

@@ -1,219 +0,0 @@
/* eslint-disable react/prop-types */
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { ensureConfig } from '@edx/frontend-platform';
import { AppContext } from '@edx/frontend-platform/react';
import { injectIntl, intlShape, FormattedDate } from '@edx/frontend-platform/i18n';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faTwitter, faFacebook, faLinkedin } from '@fortawesome/free-brands-svg-icons';
import {
ActionRow, Avatar, Card, Hyperlink, Icon,
} from '@edx/paragon';
import { HistoryEdu, VerifiedUser } from '@edx/paragon/icons';
import get from 'lodash.get';
import PluginCountry from './forms/PluginCountry';
import { Plugin } from '../../plugins';
// Actions
import {
fetchProfile,
} from './data/actions';
// Components
import PageLoading from './PageLoading';
// Selectors
import { profilePageSelector } from './data/selectors';
// i18n
import messages from './ProfilePage.messages';
import eduMessages from './forms/Education.messages';
import withParams from '../utils/hoc';
ensureConfig(['CREDENTIALS_BASE_URL', 'LMS_BASE_URL'], 'ProfilePage');
// eslint-disable-next-line react/function-component-definition
function Fallback() {
return (
<div>this is broken as all get</div>
);
}
const platformDisplayInfo = {
facebook: {
icon: faFacebook,
name: '',
},
twitter: {
icon: faTwitter,
name: '',
},
linkedin: {
icon: faLinkedin,
name: '',
},
};
class ProfilePluginPage extends React.Component {
componentDidMount() {
this.props.fetchProfile(this.props.params.username);
}
renderContent() {
const {
profileImage,
country,
levelOfEducation,
socialLinks,
isLoadingProfile,
dateJoined,
name,
intl,
} = this.props;
if (isLoadingProfile) {
return <PageLoading srMessage={this.props.intl.formatMessage(messages['profile.loading'])} />;
}
return (
<Plugin fallbackComponent={<Fallback />}>
<Card className="mb-2">
<Card.Header
className="pb-5"
subtitle={(
<Hyperlink destination={`/u/${this.props.params.username}`} target="_parent">
View public profile
</Hyperlink>
)}
actions={
(
<ActionRow className="mt-3">
{socialLinks
.filter(({ socialLink }) => Boolean(socialLink))
.map(({ platform, socialLink }) => (
<StaticListItem
key={platform}
name={platformDisplayInfo[platform].name}
url={socialLink}
platform={platform}
/>
))}
</ActionRow>
)
}
/>
<Card.Section className="text-center" muted>
<Avatar
size="xl"
className="profile-plugin-avatar"
src={profileImage.src}
alt="Profile image"
/>
<p className="h2 mb-0 font-weight-bold">{name}</p>
<p className="h3 mb-0 font-weight-bold">{this.props.params.username}</p>
<PluginCountry
country={country}
/>
</Card.Section>
<Card.Footer className="p-0">
<Card.Section className="pgn-icons-cell-vertical">
<Icon src={VerifiedUser} />
<p>
since <FormattedDate value={new Date(dateJoined)} year="numeric" />
</p>
</Card.Section>
<Card.Section className="pgn-icons-cell-vertical">
<Icon src={HistoryEdu} />
<p>
{intl.formatMessage(get(
eduMessages,
`profile.education.levels.${levelOfEducation}`,
eduMessages['profile.education.levels.o'],
))}
</p>
</Card.Section>
</Card.Footer>
</Card>
</Plugin>
);
}
render() {
return (
<div className="profile-page overflow-hidden">
{this.renderContent()}
</div>
);
}
}
const SocialLink = ({ url, name, platform }) => (
<a href={url} className="font-weight-bold" target="_parent">
<FontAwesomeIcon className="mr-2" icon={platformDisplayInfo[platform].icon} />
{name}
</a>
);
const StaticListItem = ({ url, name, platform }) => (
<ul className="list-inline">
<SocialLink name={name} url={url} platform={platform} />
</ul>
);
ProfilePluginPage.contextType = AppContext;
ProfilePluginPage.propTypes = {
// Account data
dateJoined: PropTypes.string,
// Country form data
country: PropTypes.string,
// Education form data
levelOfEducation: PropTypes.string,
// Social links form data
socialLinks: PropTypes.arrayOf(PropTypes.shape({
platform: PropTypes.string,
socialLink: PropTypes.string,
})),
// Other data we need
profileImage: PropTypes.shape({
src: PropTypes.string,
isDefault: PropTypes.bool,
}),
isLoadingProfile: PropTypes.bool.isRequired,
// Actions
fetchProfile: PropTypes.func.isRequired,
// Router
params: PropTypes.shape({
username: PropTypes.string.isRequired,
}).isRequired,
// i18n
intl: intlShape.isRequired,
};
ProfilePluginPage.defaultProps = {
profileImage: {},
levelOfEducation: null,
country: null,
socialLinks: [],
dateJoined: null,
};
export default connect(
profilePageSelector,
{
fetchProfile,
},
)(injectIntl(withParams(ProfilePluginPage)));

View File

@@ -4,7 +4,8 @@ import { VisibilityOff } from '@edx/paragon/icons';
import { Icon } from '@edx/paragon'; import { Icon } from '@edx/paragon';
import { getConfig } from '@edx/frontend-platform'; import { getConfig } from '@edx/frontend-platform';
const UsernameDescription = () => ( function UsernameDescription() {
return (
<div className="d-flex align-items-center mt-3 mb-2rem"> <div className="d-flex align-items-center mt-3 mb-2rem">
<Icon src={VisibilityOff} className="icon-visibility-off" /> <Icon src={VisibilityOff} className="icon-visibility-off" />
<div className="username-description"> <div className="username-description">
@@ -19,5 +20,6 @@ const UsernameDescription = () => (
</div> </div>
</div> </div>
); );
}
export default UsernameDescription; export default UsernameDescription;

View File

@@ -12,8 +12,7 @@ module.exports = {
imageUrlMedium: null, imageUrlMedium: null,
imageUrlLarge: null imageUrlLarge: null
}, },
levelOfEducation: null, levelOfEducation: null
learningGoal: null
}, },
profilePage: { profilePage: {
errors: {}, errors: {},

View File

@@ -42,8 +42,7 @@ module.exports = {
secondaryEmail: null, secondaryEmail: null,
timeZone: null, timeZone: null,
gender: null, gender: null,
accountPrivacy: 'custom', accountPrivacy: 'custom'
learningGoal: null,
}, },
profilePage: { profilePage: {
errors: {}, errors: {},
@@ -92,8 +91,7 @@ module.exports = {
timeZone: null, timeZone: null,
levelOfEducation: 'el', levelOfEducation: 'el',
gender: null, gender: null,
accountPrivacy: 'custom', accountPrivacy: 'custom'
learningGoal: null,
}, },
preferences: { preferences: {
visibilityUserLocation: 'all_users', visibilityUserLocation: 'all_users',
@@ -106,8 +104,7 @@ module.exports = {
visibilityName: 'private', visibilityName: 'private',
visibilityLanguageProficiencies: 'all_users', visibilityLanguageProficiencies: 'all_users',
visibilityCountry: 'all_users', visibilityCountry: 'all_users',
accountPrivacy: 'custom', accountPrivacy: 'custom'
visibilityLearningGoal: 'private',
}, },
courseCertificates: [ courseCertificates: [
{ {

View File

@@ -42,8 +42,7 @@ module.exports = {
secondaryEmail: null, secondaryEmail: null,
timeZone: null, timeZone: null,
gender: null, gender: null,
accountPrivacy: 'custom', accountPrivacy: 'custom'
learningGoal: 'advance_career',
}, },
profilePage: { profilePage: {
errors: {}, errors: {},
@@ -84,8 +83,7 @@ module.exports = {
preferences: {}, preferences: {},
courseCertificates: [], courseCertificates: [],
drafts: {}, drafts: {},
isLoadingProfile: false, isLoadingProfile: false
learningGoal: 'advance_career',
}, },
router: { router: {
location: { location: {

View File

@@ -42,8 +42,7 @@ module.exports = {
secondaryEmail: null, secondaryEmail: null,
timeZone: null, timeZone: null,
gender: null, gender: null,
accountPrivacy: 'custom', accountPrivacy: 'custom'
learningGoal: 'advance_career'
}, },
profilePage: { profilePage: {
errors: {}, errors: {},
@@ -92,8 +91,7 @@ module.exports = {
timeZone: null, timeZone: null,
levelOfEducation: 'el', levelOfEducation: 'el',
gender: null, gender: null,
accountPrivacy: 'custom', accountPrivacy: 'custom'
learningGoal: 'advance_career'
}, },
preferences: { preferences: {
visibilityUserLocation: 'all_users', visibilityUserLocation: 'all_users',
@@ -106,8 +104,7 @@ module.exports = {
visibilityName: 'private', visibilityName: 'private',
visibilityLanguageProficiencies: 'all_users', visibilityLanguageProficiencies: 'all_users',
visibilityCountry: 'all_users', visibilityCountry: 'all_users',
accountPrivacy: 'custom', accountPrivacy: 'custom'
visibilityLearningGoal: 'private',
}, },
courseCertificates: [ courseCertificates: [
{ {

View File

@@ -31,5628 +31,7 @@ exports[`<ProfilePage /> Renders correctly in various states app loading 1`] = `
</div> </div>
`; `;
exports[`<ProfilePage /> Renders correctly in various states test country edit with error 1`] = ` exports[`<ProfilePage /> Renders correctly in various states viewing other profile 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="pgn__dropdown pgn__dropdown-light dropdown"
data-testid="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>
PluginPOC
</div>
<div
className="col"
>
<div
className="d-md-none"
>
<span
data-hj-suppress={true}
>
<h1
className="h2 mb-0 font-weight-bold text-truncate"
>
staff
</h1>
<p
className="mb-0"
>
Member since
2017
</p>
<hr
className="d-none d-md-block"
/>
</span>
</div>
<div
className="d-none d-md-block float-right"
>
<a
className="pgn__hyperlink default-link standalone-link btn btn-primary"
href="http://localhost:18150/records"
onClick={[Function]}
rel="noopener noreferrer"
target="_blank"
>
View My Records
<span
className="pgn__hyperlink__external-icon"
title="Opens in a new tab"
>
<span
className="pgn__icon"
style={
Object {
"height": "1em",
"width": "1em",
}
}
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
fill="currentColor"
/>
</svg>
<span
className="sr-only"
>
in a new tab
</span>
</span>
</span>
</a>
</div>
</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 text-truncate"
>
staff
</h1>
<p
className="mb-0"
>
Member since
2017
</p>
<hr
className="d-none d-md-block"
/>
</span>
</div>
<div
className="d-md-none mb-4"
>
<a
className="pgn__hyperlink default-link standalone-link btn btn-primary"
href="http://localhost:18150/records"
onClick={[Function]}
rel="noopener noreferrer"
target="_blank"
>
View My Records
<span
className="pgn__hyperlink__external-icon"
title="Opens in a new tab"
>
<span
className="pgn__icon"
style={
Object {
"height": "1em",
"width": "1em",
}
}
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
fill="currentColor"
/>
</svg>
<span
className="sr-only"
>
in a new tab
</span>
</span>
</span>
</a>
</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}
>
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
aria-labelledby="country-label"
role="dialog"
>
<form
onSubmit={[Function]}
>
<div
className="pgn__form-group"
>
<label
className="edit-section-header"
htmlFor="country"
>
Location
</label>
<select
className="form-control"
data-hj-suppress={true}
id="country"
name="country"
onChange={[Function]}
type="select"
value="ME"
>
<option
value=""
>
 
</option>
<option
value="AF"
>
Afghanistan
</option>
<option
value="AL"
>
Albania
</option>
<option
value="DZ"
>
Algeria
</option>
<option
value="AS"
>
American Samoa
</option>
<option
value="AD"
>
Andorra
</option>
<option
value="AO"
>
Angola
</option>
<option
value="AI"
>
Anguilla
</option>
<option
value="AQ"
>
Antarctica
</option>
<option
value="AG"
>
Antigua and Barbuda
</option>
<option
value="AR"
>
Argentina
</option>
<option
value="AM"
>
Armenia
</option>
<option
value="AW"
>
Aruba
</option>
<option
value="AU"
>
Australia
</option>
<option
value="AT"
>
Austria
</option>
<option
value="AZ"
>
Azerbaijan
</option>
<option
value="BS"
>
Bahamas
</option>
<option
value="BH"
>
Bahrain
</option>
<option
value="BD"
>
Bangladesh
</option>
<option
value="BB"
>
Barbados
</option>
<option
value="BY"
>
Belarus
</option>
<option
value="BE"
>
Belgium
</option>
<option
value="BZ"
>
Belize
</option>
<option
value="BJ"
>
Benin
</option>
<option
value="BM"
>
Bermuda
</option>
<option
value="BT"
>
Bhutan
</option>
<option
value="BO"
>
Bolivia
</option>
<option
value="BA"
>
Bosnia and Herzegovina
</option>
<option
value="BW"
>
Botswana
</option>
<option
value="BV"
>
Bouvet Island
</option>
<option
value="BR"
>
Brazil
</option>
<option
value="IO"
>
British Indian Ocean Territory
</option>
<option
value="BN"
>
Brunei Darussalam
</option>
<option
value="BG"
>
Bulgaria
</option>
<option
value="BF"
>
Burkina Faso
</option>
<option
value="BI"
>
Burundi
</option>
<option
value="KH"
>
Cambodia
</option>
<option
value="CM"
>
Cameroon
</option>
<option
value="CA"
>
Canada
</option>
<option
value="CV"
>
Cape Verde
</option>
<option
value="KY"
>
Cayman Islands
</option>
<option
value="CF"
>
Central African Republic
</option>
<option
value="TD"
>
Chad
</option>
<option
value="CL"
>
Chile
</option>
<option
value="CN"
>
China
</option>
<option
value="CX"
>
Christmas Island
</option>
<option
value="CC"
>
Cocos (Keeling) Islands
</option>
<option
value="CO"
>
Colombia
</option>
<option
value="KM"
>
Comoros
</option>
<option
value="CG"
>
Congo
</option>
<option
value="CD"
>
Congo, the Democratic Republic of the
</option>
<option
value="CK"
>
Cook Islands
</option>
<option
value="CR"
>
Costa Rica
</option>
<option
value="CI"
>
Cote D'Ivoire
</option>
<option
value="HR"
>
Croatia
</option>
<option
value="CU"
>
Cuba
</option>
<option
value="CY"
>
Cyprus
</option>
<option
value="CZ"
>
Czech Republic
</option>
<option
value="DK"
>
Denmark
</option>
<option
value="DJ"
>
Djibouti
</option>
<option
value="DM"
>
Dominica
</option>
<option
value="DO"
>
Dominican Republic
</option>
<option
value="EC"
>
Ecuador
</option>
<option
value="EG"
>
Egypt
</option>
<option
value="SV"
>
El Salvador
</option>
<option
value="GQ"
>
Equatorial Guinea
</option>
<option
value="ER"
>
Eritrea
</option>
<option
value="EE"
>
Estonia
</option>
<option
value="ET"
>
Ethiopia
</option>
<option
value="FK"
>
Falkland Islands (Malvinas)
</option>
<option
value="FO"
>
Faroe Islands
</option>
<option
value="FJ"
>
Fiji
</option>
<option
value="FI"
>
Finland
</option>
<option
value="FR"
>
France
</option>
<option
value="GF"
>
French Guiana
</option>
<option
value="PF"
>
French Polynesia
</option>
<option
value="TF"
>
French Southern Territories
</option>
<option
value="GA"
>
Gabon
</option>
<option
value="GM"
>
Gambia
</option>
<option
value="GE"
>
Georgia
</option>
<option
value="DE"
>
Germany
</option>
<option
value="GH"
>
Ghana
</option>
<option
value="GI"
>
Gibraltar
</option>
<option
value="GR"
>
Greece
</option>
<option
value="GL"
>
Greenland
</option>
<option
value="GD"
>
Grenada
</option>
<option
value="GP"
>
Guadeloupe
</option>
<option
value="GU"
>
Guam
</option>
<option
value="GT"
>
Guatemala
</option>
<option
value="GN"
>
Guinea
</option>
<option
value="GW"
>
Guinea-Bissau
</option>
<option
value="GY"
>
Guyana
</option>
<option
value="HT"
>
Haiti
</option>
<option
value="HM"
>
Heard Island and Mcdonald Islands
</option>
<option
value="VA"
>
Holy See (Vatican City State)
</option>
<option
value="HN"
>
Honduras
</option>
<option
value="HK"
>
Hong Kong
</option>
<option
value="HU"
>
Hungary
</option>
<option
value="IS"
>
Iceland
</option>
<option
value="IN"
>
India
</option>
<option
value="ID"
>
Indonesia
</option>
<option
value="IR"
>
Iran, Islamic Republic of
</option>
<option
value="IQ"
>
Iraq
</option>
<option
value="IE"
>
Ireland
</option>
<option
value="IL"
>
Israel
</option>
<option
value="IT"
>
Italy
</option>
<option
value="JM"
>
Jamaica
</option>
<option
value="JP"
>
Japan
</option>
<option
value="JO"
>
Jordan
</option>
<option
value="KZ"
>
Kazakhstan
</option>
<option
value="KE"
>
Kenya
</option>
<option
value="KI"
>
Kiribati
</option>
<option
value="KP"
>
North Korea
</option>
<option
value="KR"
>
South Korea
</option>
<option
value="KW"
>
Kuwait
</option>
<option
value="KG"
>
Kyrgyzstan
</option>
<option
value="LA"
>
Lao People's Democratic Republic
</option>
<option
value="LV"
>
Latvia
</option>
<option
value="LB"
>
Lebanon
</option>
<option
value="LS"
>
Lesotho
</option>
<option
value="LR"
>
Liberia
</option>
<option
value="LY"
>
Libya
</option>
<option
value="LI"
>
Liechtenstein
</option>
<option
value="LT"
>
Lithuania
</option>
<option
value="LU"
>
Luxembourg
</option>
<option
value="MO"
>
Macao
</option>
<option
value="MG"
>
Madagascar
</option>
<option
value="MW"
>
Malawi
</option>
<option
value="MY"
>
Malaysia
</option>
<option
value="MV"
>
Maldives
</option>
<option
value="ML"
>
Mali
</option>
<option
value="MT"
>
Malta
</option>
<option
value="MH"
>
Marshall Islands
</option>
<option
value="MQ"
>
Martinique
</option>
<option
value="MR"
>
Mauritania
</option>
<option
value="MU"
>
Mauritius
</option>
<option
value="YT"
>
Mayotte
</option>
<option
value="MX"
>
Mexico
</option>
<option
value="FM"
>
Micronesia, Federated States of
</option>
<option
value="MD"
>
Moldova, Republic of
</option>
<option
value="MC"
>
Monaco
</option>
<option
value="MN"
>
Mongolia
</option>
<option
value="MS"
>
Montserrat
</option>
<option
value="MA"
>
Morocco
</option>
<option
value="MZ"
>
Mozambique
</option>
<option
value="MM"
>
Myanmar
</option>
<option
value="NA"
>
Namibia
</option>
<option
value="NR"
>
Nauru
</option>
<option
value="NP"
>
Nepal
</option>
<option
value="NL"
>
Netherlands
</option>
<option
value="NC"
>
New Caledonia
</option>
<option
value="NZ"
>
New Zealand
</option>
<option
value="NI"
>
Nicaragua
</option>
<option
value="NE"
>
Niger
</option>
<option
value="NG"
>
Nigeria
</option>
<option
value="NU"
>
Niue
</option>
<option
value="NF"
>
Norfolk Island
</option>
<option
value="MK"
>
North Macedonia, Republic of
</option>
<option
value="MP"
>
Northern Mariana Islands
</option>
<option
value="NO"
>
Norway
</option>
<option
value="OM"
>
Oman
</option>
<option
value="PK"
>
Pakistan
</option>
<option
value="PW"
>
Palau
</option>
<option
value="PS"
>
Palestinian Territory, Occupied
</option>
<option
value="PA"
>
Panama
</option>
<option
value="PG"
>
Papua New Guinea
</option>
<option
value="PY"
>
Paraguay
</option>
<option
value="PE"
>
Peru
</option>
<option
value="PH"
>
Philippines
</option>
<option
value="PN"
>
Pitcairn
</option>
<option
value="PL"
>
Poland
</option>
<option
value="PT"
>
Portugal
</option>
<option
value="PR"
>
Puerto Rico
</option>
<option
value="QA"
>
Qatar
</option>
<option
value="RE"
>
Reunion
</option>
<option
value="RO"
>
Romania
</option>
<option
value="RU"
>
Russian Federation
</option>
<option
value="RW"
>
Rwanda
</option>
<option
value="SH"
>
Saint Helena
</option>
<option
value="KN"
>
Saint Kitts and Nevis
</option>
<option
value="LC"
>
Saint Lucia
</option>
<option
value="PM"
>
Saint Pierre and Miquelon
</option>
<option
value="VC"
>
Saint Vincent and the Grenadines
</option>
<option
value="WS"
>
Samoa
</option>
<option
value="SM"
>
San Marino
</option>
<option
value="ST"
>
Sao Tome and Principe
</option>
<option
value="SA"
>
Saudi Arabia
</option>
<option
value="SN"
>
Senegal
</option>
<option
value="SC"
>
Seychelles
</option>
<option
value="SL"
>
Sierra Leone
</option>
<option
value="SG"
>
Singapore
</option>
<option
value="SK"
>
Slovakia
</option>
<option
value="SI"
>
Slovenia
</option>
<option
value="SB"
>
Solomon Islands
</option>
<option
value="SO"
>
Somalia
</option>
<option
value="ZA"
>
South Africa
</option>
<option
value="GS"
>
South Georgia and the South Sandwich Islands
</option>
<option
value="ES"
>
Spain
</option>
<option
value="LK"
>
Sri Lanka
</option>
<option
value="SD"
>
Sudan
</option>
<option
value="SR"
>
Suriname
</option>
<option
value="SJ"
>
Svalbard and Jan Mayen
</option>
<option
value="SZ"
>
Swaziland
</option>
<option
value="SE"
>
Sweden
</option>
<option
value="CH"
>
Switzerland
</option>
<option
value="SY"
>
Syrian Arab Republic
</option>
<option
value="TW"
>
Taiwan
</option>
<option
value="TJ"
>
Tajikistan
</option>
<option
value="TZ"
>
Tanzania, United Republic of
</option>
<option
value="TH"
>
Thailand
</option>
<option
value="TL"
>
Timor-Leste
</option>
<option
value="TG"
>
Togo
</option>
<option
value="TK"
>
Tokelau
</option>
<option
value="TO"
>
Tonga
</option>
<option
value="TT"
>
Trinidad and Tobago
</option>
<option
value="TN"
>
Tunisia
</option>
<option
value="TR"
>
Turkey
</option>
<option
value="TM"
>
Turkmenistan
</option>
<option
value="TC"
>
Turks and Caicos Islands
</option>
<option
value="TV"
>
Tuvalu
</option>
<option
value="UG"
>
Uganda
</option>
<option
value="UA"
>
Ukraine
</option>
<option
value="AE"
>
United Arab Emirates
</option>
<option
value="GB"
>
United Kingdom
</option>
<option
value="US"
>
United States of America
</option>
<option
value="UM"
>
United States Minor Outlying Islands
</option>
<option
value="UY"
>
Uruguay
</option>
<option
value="UZ"
>
Uzbekistan
</option>
<option
value="VU"
>
Vanuatu
</option>
<option
value="VE"
>
Venezuela
</option>
<option
value="VN"
>
Viet Nam
</option>
<option
value="VG"
>
Virgin Islands, British
</option>
<option
value="VI"
>
Virgin Islands, U.S.
</option>
<option
value="WF"
>
Wallis and Futuna
</option>
<option
value="EH"
>
Western Sahara
</option>
<option
value="YE"
>
Yemen
</option>
<option
value="ZM"
>
Zambia
</option>
<option
value="ZW"
>
Zimbabwe
</option>
<option
value="AX"
>
Åland Islands
</option>
<option
value="BQ"
>
Bonaire, Sint Eustatius and Saba
</option>
<option
value="CW"
>
Curaçao
</option>
<option
value="GG"
>
Guernsey
</option>
<option
value="IM"
>
Isle of Man
</option>
<option
value="JE"
>
Jersey
</option>
<option
value="ME"
>
Montenegro
</option>
<option
value="BL"
>
Saint Barthélemy
</option>
<option
value="MF"
>
Saint Martin (French part)
</option>
<option
value="RS"
>
Serbia
</option>
<option
value="SX"
>
Sint Maarten (Dutch part)
</option>
<option
value="SS"
>
South Sudan
</option>
<option
value="XK"
>
Kosovo
</option>
</select>
<div
className="pgn__form-control-description pgn__form-text pgn__form-text-invalid"
id="country-2"
>
<div>
country error
</div>
</div>
</div>
<div
className="d-flex flex-row-reverse flex-wrap justify-content-end align-items-center"
>
<div
className="form-group d-flex flex-wrap"
>
<label
className="col-form-label"
htmlFor="visibilityCountry"
>
Who can see this:
</label>
<span
className="d-flex align-items-center"
>
<span
className="d-inline-block ml-1 mr-2"
style={
Object {
"width": "1.5rem",
}
}
>
<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>
</span>
<select
className="d-inline-block form-control"
id="visibilityCountry"
name="visibilityCountry"
onChange={[Function]}
type="select"
value="all_users"
>
<option
value="private"
>
Just me
</option>
<option
value="all_users"
>
Everyone on localhost
</option>
</select>
</span>
</div>
<div
className="form-group flex-shrink-0 flex-grow-1"
>
<button
aria-disabled={false}
aria-live="assertive"
className="pgn__stateful-btn pgn__stateful-btn-state-pending btn btn-primary"
disabled={false}
onClick={[Function]}
type="submit"
>
<span
className="d-flex align-items-center justify-content-center"
>
<span
className="pgn__stateful-btn-icon"
>
<span
className="pgn__icon icon-spin"
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M22 12A10 10 0 1 1 6.122 3.91l1.176 1.618A8 8 0 1 0 20 12h2Z"
fill="currentColor"
/>
</svg>
</span>
</span>
<span>
Saving
</span>
</span>
</button>
<button
className="btn btn-link"
disabled={false}
onClick={[Function]}
type="button"
>
Cancel
</button>
</div>
</div>
</form>
</div>
</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 localhost
</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 localhost
</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-16 mr-2"
data-icon="facebook"
data-prefix="fab"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M504 256C504 119 393 8 256 8S8 119 8 256c0 123.78 90.69 226.38 209.25 245V327.69h-63V256h63v-54.64c0-62.15 37-96.48 93.67-96.48 27.14 0 55.52 4.84 55.52 4.84v61h-31.28c-30.8 0-40.41 19.12-40.41 38.73V256h68.78l-11 71.69h-57.78V501C413.31 482.38 504 379.78 504 256z"
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 localhost
</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 localhost
</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"
>
From
</p>
<p
className="h6 mb-4"
>
edX
</p>
<div
className="flex-grow-1"
/>
<p
className="small mb-2"
>
Completed on
3/4/2019
</p>
<div>
<a
className="pgn__hyperlink default-link standalone-link btn btn-outline-primary"
href="http://www.example.com/"
onClick={[Function]}
rel="noopener noreferrer"
target="_blank"
>
View Certificate
<span
className="pgn__hyperlink__external-icon"
title="Opens in a new tab"
>
<span
className="pgn__icon"
style={
Object {
"height": "1em",
"width": "1em",
}
}
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
fill="currentColor"
/>
</svg>
<span
className="sr-only"
>
in a new tab
</span>
</span>
</span>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`<ProfilePage /> Renders correctly in various states test education edit with error 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="pgn__dropdown pgn__dropdown-light dropdown"
data-testid="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>
PluginPOC
</div>
<div
className="col"
>
<div
className="d-md-none"
>
<span
data-hj-suppress={true}
>
<h1
className="h2 mb-0 font-weight-bold text-truncate"
>
staff
</h1>
<p
className="mb-0"
>
Member since
2017
</p>
<hr
className="d-none d-md-block"
/>
</span>
</div>
<div
className="d-none d-md-block float-right"
>
<a
className="pgn__hyperlink default-link standalone-link btn btn-primary"
href="http://localhost:18150/records"
onClick={[Function]}
rel="noopener noreferrer"
target="_blank"
>
View My Records
<span
className="pgn__hyperlink__external-icon"
title="Opens in a new tab"
>
<span
className="pgn__icon"
style={
Object {
"height": "1em",
"width": "1em",
}
}
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
fill="currentColor"
/>
</svg>
<span
className="sr-only"
>
in a new tab
</span>
</span>
</span>
</a>
</div>
</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 text-truncate"
>
staff
</h1>
<p
className="mb-0"
>
Member since
2017
</p>
<hr
className="d-none d-md-block"
/>
</span>
</div>
<div
className="d-md-none mb-4"
>
<a
className="pgn__hyperlink default-link standalone-link btn btn-primary"
href="http://localhost:18150/records"
onClick={[Function]}
rel="noopener noreferrer"
target="_blank"
>
View My Records
<span
className="pgn__hyperlink__external-icon"
title="Opens in a new tab"
>
<span
className="pgn__icon"
style={
Object {
"height": "1em",
"width": "1em",
}
}
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
fill="currentColor"
/>
</svg>
<span
className="sr-only"
>
in a new tab
</span>
</span>
</span>
</a>
</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}
>
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 localhost
</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 localhost
</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
aria-labelledby="levelOfEducation-label"
role="dialog"
>
<form
onSubmit={[Function]}
>
<div
className="pgn__form-group"
>
<label
className="edit-section-header"
htmlFor="levelOfEducation"
>
Education
</label>
<select
className="form-control"
data-hj-suppress={true}
id="levelOfEducation"
name="levelOfEducation"
onChange={[Function]}
value="el"
>
<option
value=""
>
 
</option>
<option
value="p"
>
Doctorate
</option>
<option
value="m"
>
Master's or professional degree
</option>
<option
value="b"
>
Bachelor's Degree
</option>
<option
value="a"
>
Associate's degree
</option>
<option
value="hs"
>
Secondary/high school
</option>
<option
value="jhs"
>
Junior secondary/junior high/middle school
</option>
<option
value="el"
>
Elementary/primary school
</option>
<option
value="none"
>
No formal education
</option>
<option
value="other"
>
Other education
</option>
</select>
<div
className="pgn__form-control-description pgn__form-text pgn__form-text-invalid"
id="levelOfEducation-3"
>
<div>
education error
</div>
</div>
</div>
<div
className="d-flex flex-row-reverse flex-wrap justify-content-end align-items-center"
>
<div
className="form-group d-flex flex-wrap"
>
<label
className="col-form-label"
htmlFor="visibilityLevelOfEducation"
>
Who can see this:
</label>
<span
className="d-flex align-items-center"
>
<span
className="d-inline-block ml-1 mr-2"
style={
Object {
"width": "1.5rem",
}
}
>
<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>
</span>
<select
className="d-inline-block form-control"
id="visibilityLevelOfEducation"
name="visibilityLevelOfEducation"
onChange={[Function]}
type="select"
value="private"
>
<option
value="private"
>
Just me
</option>
<option
value="all_users"
>
Everyone on localhost
</option>
</select>
</span>
</div>
<div
className="form-group flex-shrink-0 flex-grow-1"
>
<button
aria-disabled={false}
aria-live="assertive"
className="pgn__stateful-btn pgn__stateful-btn-state-pending btn btn-primary"
disabled={false}
onClick={[Function]}
type="submit"
>
<span
className="d-flex align-items-center justify-content-center"
>
<span
className="pgn__stateful-btn-icon"
>
<span
className="pgn__icon icon-spin"
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M22 12A10 10 0 1 1 6.122 3.91l1.176 1.618A8 8 0 1 0 20 12h2Z"
fill="currentColor"
/>
</svg>
</span>
</span>
<span>
Saving
</span>
</span>
</button>
<button
className="btn btn-link"
disabled={false}
onClick={[Function]}
type="button"
>
Cancel
</button>
</div>
</div>
</form>
</div>
</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 localhost
</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-16 mr-2"
data-icon="facebook"
data-prefix="fab"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M504 256C504 119 393 8 256 8S8 119 8 256c0 123.78 90.69 226.38 209.25 245V327.69h-63V256h63v-54.64c0-62.15 37-96.48 93.67-96.48 27.14 0 55.52 4.84 55.52 4.84v61h-31.28c-30.8 0-40.41 19.12-40.41 38.73V256h68.78l-11 71.69h-57.78V501C413.31 482.38 504 379.78 504 256z"
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 localhost
</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 localhost
</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"
>
From
</p>
<p
className="h6 mb-4"
>
edX
</p>
<div
className="flex-grow-1"
/>
<p
className="small mb-2"
>
Completed on
3/4/2019
</p>
<div>
<a
className="pgn__hyperlink default-link standalone-link btn btn-outline-primary"
href="http://www.example.com/"
onClick={[Function]}
rel="noopener noreferrer"
target="_blank"
>
View Certificate
<span
className="pgn__hyperlink__external-icon"
title="Opens in a new tab"
>
<span
className="pgn__icon"
style={
Object {
"height": "1em",
"width": "1em",
}
}
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
fill="currentColor"
/>
</svg>
<span
className="sr-only"
>
in a new tab
</span>
</span>
</span>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`<ProfilePage /> Renders correctly in various states test preferreded language edit with error 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="pgn__dropdown pgn__dropdown-light dropdown"
data-testid="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>
PluginPOC
</div>
<div
className="col"
>
<div
className="d-md-none"
>
<span
data-hj-suppress={true}
>
<h1
className="h2 mb-0 font-weight-bold text-truncate"
>
staff
</h1>
<p
className="mb-0"
>
Member since
2017
</p>
<hr
className="d-none d-md-block"
/>
</span>
</div>
<div
className="d-none d-md-block float-right"
>
<a
className="pgn__hyperlink default-link standalone-link btn btn-primary"
href="http://localhost:18150/records"
onClick={[Function]}
rel="noopener noreferrer"
target="_blank"
>
View My Records
<span
className="pgn__hyperlink__external-icon"
title="Opens in a new tab"
>
<span
className="pgn__icon"
style={
Object {
"height": "1em",
"width": "1em",
}
}
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
fill="currentColor"
/>
</svg>
<span
className="sr-only"
>
in a new tab
</span>
</span>
</span>
</a>
</div>
</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 text-truncate"
>
staff
</h1>
<p
className="mb-0"
>
Member since
2017
</p>
<hr
className="d-none d-md-block"
/>
</span>
</div>
<div
className="d-md-none mb-4"
>
<a
className="pgn__hyperlink default-link standalone-link btn btn-primary"
href="http://localhost:18150/records"
onClick={[Function]}
rel="noopener noreferrer"
target="_blank"
>
View My Records
<span
className="pgn__hyperlink__external-icon"
title="Opens in a new tab"
>
<span
className="pgn__icon"
style={
Object {
"height": "1em",
"width": "1em",
}
}
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
fill="currentColor"
/>
</svg>
<span
className="sr-only"
>
in a new tab
</span>
</span>
</span>
</a>
</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}
>
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 localhost
</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
aria-labelledby="languageProficiencies-label"
role="dialog"
>
<form
onSubmit={[Function]}
>
<div
className="pgn__form-group"
>
<label
className="edit-section-header"
htmlFor="languageProficiencies"
>
Primary Language Spoken
</label>
<select
className="form-control"
data-hj-suppress={true}
id="languageProficiencies"
name="languageProficiencies"
onChange={[Function]}
value="yo"
>
<option
value=""
>
 
</option>
<option
value="aa"
>
Afar
</option>
<option
value="ab"
>
Abkhazian
</option>
<option
value="ae"
>
Avestan
</option>
<option
value="af"
>
Afrikaans
</option>
<option
value="ak"
>
Akan
</option>
<option
value="am"
>
Amharic
</option>
<option
value="an"
>
Aragonese
</option>
<option
value="ar"
>
Arabic
</option>
<option
value="as"
>
Assamese
</option>
<option
value="av"
>
Avaric
</option>
<option
value="ay"
>
Aymara
</option>
<option
value="az"
>
Azerbaijani
</option>
<option
value="ba"
>
Bashkir
</option>
<option
value="be"
>
Belarusian
</option>
<option
value="bg"
>
Bulgarian
</option>
<option
value="bh"
>
Bihari languages
</option>
<option
value="bi"
>
Bislama
</option>
<option
value="bm"
>
Bambara
</option>
<option
value="bn"
>
Bengali
</option>
<option
value="bo"
>
Tibetan
</option>
<option
value="br"
>
Breton
</option>
<option
value="bs"
>
Bosnian
</option>
<option
value="ca"
>
Catalan
</option>
<option
value="ce"
>
Chechen
</option>
<option
value="ch"
>
Chamorro
</option>
<option
value="co"
>
Corsican
</option>
<option
value="cr"
>
Cree
</option>
<option
value="cs"
>
Czech
</option>
<option
value="cu"
>
Church Slavic
</option>
<option
value="cv"
>
Chuvash
</option>
<option
value="cy"
>
Welsh
</option>
<option
value="da"
>
Danish
</option>
<option
value="de"
>
German
</option>
<option
value="dv"
>
Maldivian
</option>
<option
value="dz"
>
Dzongkha
</option>
<option
value="ee"
>
Ewe
</option>
<option
value="el"
>
Greek (modern)
</option>
<option
value="en"
>
English
</option>
<option
value="eo"
>
Esperanto
</option>
<option
value="es"
>
Spanish
</option>
<option
value="et"
>
Estonian
</option>
<option
value="eu"
>
Basque
</option>
<option
value="fa"
>
Persian
</option>
<option
value="ff"
>
Fulah
</option>
<option
value="fi"
>
Finnish
</option>
<option
value="fj"
>
Fijian
</option>
<option
value="fo"
>
Faroese
</option>
<option
value="fr"
>
French
</option>
<option
value="fy"
>
Western Frisian
</option>
<option
value="ga"
>
Irish
</option>
<option
value="gd"
>
Gaelic
</option>
<option
value="gl"
>
Galician
</option>
<option
value="gn"
>
Guaraní
</option>
<option
value="gu"
>
Gujarati
</option>
<option
value="gv"
>
Manx
</option>
<option
value="ha"
>
Hausa
</option>
<option
value="he"
>
Hebrew (modern)
</option>
<option
value="hi"
>
Hindi
</option>
<option
value="ho"
>
Hiri Motu
</option>
<option
value="hr"
>
Croatian
</option>
<option
value="ht"
>
Haitian Creole
</option>
<option
value="hu"
>
Hungarian
</option>
<option
value="hy"
>
Armenian
</option>
<option
value="hz"
>
Herero
</option>
<option
value="ia"
>
Interlingua
</option>
<option
value="id"
>
Indonesian
</option>
<option
value="ie"
>
Interlingue
</option>
<option
value="ig"
>
Igbo
</option>
<option
value="ii"
>
Nuosu
</option>
<option
value="ik"
>
Inupiaq
</option>
<option
value="io"
>
Ido
</option>
<option
value="is"
>
Icelandic
</option>
<option
value="it"
>
Italian
</option>
<option
value="iu"
>
Inuktitut
</option>
<option
value="ja"
>
Japanese
</option>
<option
value="jv"
>
Javanese
</option>
<option
value="ka"
>
Georgian
</option>
<option
value="kg"
>
Kongo
</option>
<option
value="ki"
>
Kikuyu
</option>
<option
value="kj"
>
Kwanyama
</option>
<option
value="kk"
>
Kazakh
</option>
<option
value="kl"
>
Greenlandic
</option>
<option
value="km"
>
Central Khmer
</option>
<option
value="kn"
>
Kannada
</option>
<option
value="ko"
>
Korean
</option>
<option
value="kr"
>
Kanuri
</option>
<option
value="ks"
>
Kashmiri
</option>
<option
value="ku"
>
Kurdish
</option>
<option
value="kv"
>
Komi
</option>
<option
value="kw"
>
Cornish
</option>
<option
value="ky"
>
Kyrgyz
</option>
<option
value="la"
>
Latin
</option>
<option
value="lb"
>
Luxembourgish
</option>
<option
value="lg"
>
Ganda
</option>
<option
value="li"
>
Limburgish
</option>
<option
value="ln"
>
Lingala
</option>
<option
value="lo"
>
Lao
</option>
<option
value="lt"
>
Lithuanian
</option>
<option
value="lu"
>
Luba-Katanga
</option>
<option
value="lv"
>
Latvian
</option>
<option
value="mg"
>
Malagasy
</option>
<option
value="mh"
>
Marshallese
</option>
<option
value="mi"
>
Maori
</option>
<option
value="mk"
>
Macedonian
</option>
<option
value="ml"
>
Malayalam
</option>
<option
value="mn"
>
Mongolian
</option>
<option
value="mr"
>
Marathi
</option>
<option
value="ms"
>
Malay
</option>
<option
value="mt"
>
Maltese
</option>
<option
value="my"
>
Burmese
</option>
<option
value="na"
>
Nauru
</option>
<option
value="nb"
>
Bokmål
</option>
<option
value="nd"
>
North Ndebele
</option>
<option
value="ne"
>
Nepali
</option>
<option
value="ng"
>
Ndonga
</option>
<option
value="nl"
>
Dutch
</option>
<option
value="nn"
>
Norwegian Nynorsk
</option>
<option
value="no"
>
Norwegian
</option>
<option
value="nr"
>
South Ndebele
</option>
<option
value="nv"
>
Navajo
</option>
<option
value="ny"
>
Nyanja
</option>
<option
value="oc"
>
Occitan
</option>
<option
value="oj"
>
Ojibwa
</option>
<option
value="om"
>
Oromo
</option>
<option
value="or"
>
Oriya
</option>
<option
value="os"
>
Ossetian
</option>
<option
value="pa"
>
Punjabi
</option>
<option
value="pi"
>
Pali
</option>
<option
value="pl"
>
Polish
</option>
<option
value="ps"
>
Pashto
</option>
<option
value="pt"
>
Portuguese
</option>
<option
value="qu"
>
Quechua
</option>
<option
value="rm"
>
Romansh
</option>
<option
value="rn"
>
Rundi
</option>
<option
value="ro"
>
Romanian
</option>
<option
value="ru"
>
Russian
</option>
<option
value="rw"
>
Kinyarwanda
</option>
<option
value="sa"
>
Sanskrit
</option>
<option
value="sc"
>
Sardinian
</option>
<option
value="sd"
>
Sindhi
</option>
<option
value="se"
>
Northern Sami
</option>
<option
value="sg"
>
Sango
</option>
<option
value="si"
>
Sinhalese
</option>
<option
value="sk"
>
Slovak
</option>
<option
value="sl"
>
Slovenian
</option>
<option
value="sm"
>
Samoan
</option>
<option
value="sn"
>
Shona
</option>
<option
value="so"
>
Somali
</option>
<option
value="sq"
>
Albanian
</option>
<option
value="sr"
>
Serbian
</option>
<option
value="ss"
>
Swati
</option>
<option
value="st"
>
Southern Sotho
</option>
<option
value="su"
>
Sundanese
</option>
<option
value="sv"
>
Swedish
</option>
<option
value="sw"
>
Swahili
</option>
<option
value="ta"
>
Tamil
</option>
<option
value="te"
>
Telugu
</option>
<option
value="tg"
>
Tajik
</option>
<option
value="th"
>
Thai
</option>
<option
value="ti"
>
Tigrinya
</option>
<option
value="tk"
>
Turkmen
</option>
<option
value="tl"
>
Tagalog
</option>
<option
value="tn"
>
Tswana
</option>
<option
value="to"
>
Tonga
</option>
<option
value="tr"
>
Turkish
</option>
<option
value="ts"
>
Tsonga
</option>
<option
value="tt"
>
Tatar
</option>
<option
value="tw"
>
Twi
</option>
<option
value="ty"
>
Tahitian
</option>
<option
value="ug"
>
Uyghur
</option>
<option
value="uk"
>
Ukrainian
</option>
<option
value="ur"
>
Urdu
</option>
<option
value="uz"
>
Uzbek
</option>
<option
value="ve"
>
Venda
</option>
<option
value="vi"
>
Vietnamese
</option>
<option
value="vo"
>
Volapük
</option>
<option
value="wa"
>
Walloon
</option>
<option
value="wo"
>
Wolof
</option>
<option
value="xh"
>
Xhosa
</option>
<option
value="yi"
>
Yiddish
</option>
<option
value="yo"
>
Yoruba
</option>
<option
value="za"
>
Zhuang
</option>
<option
value="zh"
>
Chinese
</option>
<option
value="zu"
>
Zulu
</option>
</select>
<div
className="pgn__form-control-description pgn__form-text pgn__form-text-invalid"
id="languageProficiencies-4"
>
<div>
preferred language error
</div>
</div>
</div>
<div
className="d-flex flex-row-reverse flex-wrap justify-content-end align-items-center"
>
<div
className="form-group d-flex flex-wrap"
>
<label
className="col-form-label"
htmlFor="visibilityLanguageProficiencies"
>
Who can see this:
</label>
<span
className="d-flex align-items-center"
>
<span
className="d-inline-block ml-1 mr-2"
style={
Object {
"width": "1.5rem",
}
}
>
<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>
</span>
<select
className="d-inline-block form-control"
id="visibilityLanguageProficiencies"
name="visibilityLanguageProficiencies"
onChange={[Function]}
type="select"
value="all_users"
>
<option
value="private"
>
Just me
</option>
<option
value="all_users"
>
Everyone on localhost
</option>
</select>
</span>
</div>
<div
className="form-group flex-shrink-0 flex-grow-1"
>
<button
aria-disabled={false}
aria-live="assertive"
className="pgn__stateful-btn pgn__stateful-btn-state-pending btn btn-primary"
disabled={false}
onClick={[Function]}
type="submit"
>
<span
className="d-flex align-items-center justify-content-center"
>
<span
className="pgn__stateful-btn-icon"
>
<span
className="pgn__icon icon-spin"
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M22 12A10 10 0 1 1 6.122 3.91l1.176 1.618A8 8 0 1 0 20 12h2Z"
fill="currentColor"
/>
</svg>
</span>
</span>
<span>
Saving
</span>
</span>
</button>
<button
className="btn btn-link"
disabled={false}
onClick={[Function]}
type="button"
>
Cancel
</button>
</div>
</div>
</form>
</div>
</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 localhost
</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-16 mr-2"
data-icon="facebook"
data-prefix="fab"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M504 256C504 119 393 8 256 8S8 119 8 256c0 123.78 90.69 226.38 209.25 245V327.69h-63V256h63v-54.64c0-62.15 37-96.48 93.67-96.48 27.14 0 55.52 4.84 55.52 4.84v61h-31.28c-30.8 0-40.41 19.12-40.41 38.73V256h68.78l-11 71.69h-57.78V501C413.31 482.38 504 379.78 504 256z"
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 localhost
</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 localhost
</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"
>
From
</p>
<p
className="h6 mb-4"
>
edX
</p>
<div
className="flex-grow-1"
/>
<p
className="small mb-2"
>
Completed on
3/4/2019
</p>
<div>
<a
className="pgn__hyperlink default-link standalone-link btn btn-outline-primary"
href="http://www.example.com/"
onClick={[Function]}
rel="noopener noreferrer"
target="_blank"
>
View Certificate
<span
className="pgn__hyperlink__external-icon"
title="Opens in a new tab"
>
<span
className="pgn__icon"
style={
Object {
"height": "1em",
"width": "1em",
}
}
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
fill="currentColor"
/>
</svg>
<span
className="sr-only"
>
in a new tab
</span>
</span>
</span>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`<ProfilePage /> Renders correctly in various states viewing other profile with all fields 1`] = `
<div <div
className="profile-page" className="profile-page"
> >
@@ -5701,11 +80,8 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
</div> </div>
</div> </div>
</div> </div>
<div>
PluginPOC
</div>
<div <div
className="col" className="col pl-0"
> >
<div <div
className="d-md-none" className="d-md-none"
@@ -5714,15 +90,19 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
data-hj-suppress={true} data-hj-suppress={true}
> >
<h1 <h1
className="h2 mb-0 font-weight-bold text-truncate" className="h2 mb-0 font-weight-bold"
> >
staff verified
</h1> </h1>
<p <p
className="mb-0" className="mb-0"
> >
<span>
Member since Member since
<span>
2017 2017
</span>
</span>
</p> </p>
<div <div
className="d-flex align-items-center mt-3 mb-2rem" className="d-flex align-items-center mt-3 mb-2rem"
@@ -5741,7 +121,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<path <path
d="M12 6.5c2.76 0 5 2.24 5 5 0 .51-.1 1-.24 1.46l3.06 3.06c1.39-1.23 2.49-2.77 3.18-4.53C21.27 7.11 17 4 12 4c-1.27 0-2.49.2-3.64.57l2.17 2.17c.47-.14.96-.24 1.47-.24ZM3.42 2.45 2.01 3.87l2.68 2.68A11.738 11.738 0 0 0 1 11.5C2.73 15.89 7 19 12 19c1.52 0 2.97-.3 4.31-.82l3.43 3.43 1.41-1.41L3.42 2.45ZM12 16.5c-2.76 0-5-2.24-5-5 0-.77.18-1.5.49-2.14l1.57 1.57c-.03.18-.06.37-.06.57 0 1.66 1.34 3 3 3 .2 0 .38-.03.57-.07L14.14 16c-.65.32-1.37.5-2.14.5Zm2.97-5.33a2.97 2.97 0 0 0-2.64-2.64l2.64 2.64Z" d="M12 6.5c2.76 0 5 2.24 5 5 0 .51-.1 1-.24 1.46l3.06 3.06c1.39-1.23 2.49-2.77 3.18-4.53C21.27 7.11 17 4 12 4c-1.27 0-2.49.2-3.64.57l2.17 2.17c.47-.14.96-.24 1.47-.24zM3.42 2.45L2.01 3.87l2.68 2.68A11.738 11.738 0 001 11.5C2.73 15.89 7 19 12 19c1.52 0 2.97-.3 4.31-.82l3.43 3.43 1.41-1.41L3.42 2.45zM12 16.5c-2.76 0-5-2.24-5-5 0-.77.18-1.5.49-2.14l1.57 1.57c-.03.18-.06.37-.06.57 0 1.66 1.34 3 3 3 .2 0 .38-.03.57-.07L14.14 16c-.65.32-1.37.5-2.14.5zm2.97-5.33a2.97 2.97 0 00-2.64-2.64l2.64 2.64z"
fill="currentColor" fill="currentColor"
/> />
</svg> </svg>
@@ -5749,7 +129,9 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
<div <div
className="username-description" className="username-description"
> >
<span>
Your profile information is only visible to you. Only your username is visible to others on localhost. Your profile information is only visible to you. Only your username is visible to others on localhost.
</span>
</div> </div>
</div> </div>
<hr <hr
@@ -5759,52 +141,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
</div> </div>
<div <div
className="d-none d-md-block float-right" className="d-none d-md-block float-right"
>
<a
className="pgn__hyperlink default-link standalone-link btn btn-primary"
href="http://localhost:18150/records"
onClick={[Function]}
rel="noopener noreferrer"
target="_blank"
>
View My Records
<span
className="pgn__hyperlink__external-icon"
title="Opens in a new tab"
>
<span
className="pgn__icon"
style={
Object {
"height": "1em",
"width": "1em",
}
}
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
fill="currentColor"
/> />
</svg>
<span
className="sr-only"
>
in a new tab
</span>
</span>
</span>
</a>
</div>
</div> </div>
</div> </div>
<div <div
@@ -5820,15 +157,19 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
data-hj-suppress={true} data-hj-suppress={true}
> >
<h1 <h1
className="h2 mb-0 font-weight-bold text-truncate" className="h2 mb-0 font-weight-bold"
> >
staff verified
</h1> </h1>
<p <p
className="mb-0" className="mb-0"
> >
<span>
Member since Member since
<span>
2017 2017
</span>
</span>
</p> </p>
<div <div
className="d-flex align-items-center mt-3 mb-2rem" className="d-flex align-items-center mt-3 mb-2rem"
@@ -5847,7 +188,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<path <path
d="M12 6.5c2.76 0 5 2.24 5 5 0 .51-.1 1-.24 1.46l3.06 3.06c1.39-1.23 2.49-2.77 3.18-4.53C21.27 7.11 17 4 12 4c-1.27 0-2.49.2-3.64.57l2.17 2.17c.47-.14.96-.24 1.47-.24ZM3.42 2.45 2.01 3.87l2.68 2.68A11.738 11.738 0 0 0 1 11.5C2.73 15.89 7 19 12 19c1.52 0 2.97-.3 4.31-.82l3.43 3.43 1.41-1.41L3.42 2.45ZM12 16.5c-2.76 0-5-2.24-5-5 0-.77.18-1.5.49-2.14l1.57 1.57c-.03.18-.06.37-.06.57 0 1.66 1.34 3 3 3 .2 0 .38-.03.57-.07L14.14 16c-.65.32-1.37.5-2.14.5Zm2.97-5.33a2.97 2.97 0 0 0-2.64-2.64l2.64 2.64Z" d="M12 6.5c2.76 0 5 2.24 5 5 0 .51-.1 1-.24 1.46l3.06 3.06c1.39-1.23 2.49-2.77 3.18-4.53C21.27 7.11 17 4 12 4c-1.27 0-2.49.2-3.64.57l2.17 2.17c.47-.14.96-.24 1.47-.24zM3.42 2.45L2.01 3.87l2.68 2.68A11.738 11.738 0 001 11.5C2.73 15.89 7 19 12 19c1.52 0 2.97-.3 4.31-.82l3.43 3.43 1.41-1.41L3.42 2.45zM12 16.5c-2.76 0-5-2.24-5-5 0-.77.18-1.5.49-2.14l1.57 1.57c-.03.18-.06.37-.06.57 0 1.66 1.34 3 3 3 .2 0 .38-.03.57-.07L14.14 16c-.65.32-1.37.5-2.14.5zm2.97-5.33a2.97 2.97 0 00-2.64-2.64l2.64 2.64z"
fill="currentColor" fill="currentColor"
/> />
</svg> </svg>
@@ -5855,7 +196,9 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
<div <div
className="username-description" className="username-description"
> >
<span>
Your profile information is only visible to you. Only your username is visible to others on localhost. Your profile information is only visible to you. Only your username is visible to others on localhost.
</span>
</div> </div>
</div> </div>
<hr <hr
@@ -5865,52 +208,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
</div> </div>
<div <div
className="d-md-none mb-4" className="d-md-none mb-4"
>
<a
className="pgn__hyperlink default-link standalone-link btn btn-primary"
href="http://localhost:18150/records"
onClick={[Function]}
rel="noopener noreferrer"
target="_blank"
>
View My Records
<span
className="pgn__hyperlink__external-icon"
title="Opens in a new tab"
>
<span
className="pgn__icon"
style={
Object {
"height": "1em",
"width": "1em",
}
}
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
fill="currentColor"
/> />
</svg>
<span
className="sr-only"
>
in a new tab
</span>
</span>
</span>
</a>
</div>
<div <div
className="pgn-transition-replace-group position-relative mb-5" className="pgn-transition-replace-group position-relative mb-5"
style={ style={
@@ -5918,63 +216,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
"height": null, "height": null,
} }
} }
>
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
className="editable-item-header mb-2"
>
<h2
className="edit-section-header"
id={null}
>
Full Name
</h2>
</div>
<p
className="h5"
data-hj-suppress={true}
>
user
</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}
>
Location
</h2>
</div>
<p
className="h5"
data-hj-suppress={true}
/> />
</div>
</div>
<div <div
className="pgn-transition-replace-group position-relative mb-5" className="pgn-transition-replace-group position-relative mb-5"
style={ style={
@@ -5982,30 +224,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
"height": null, "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
</h2>
</div>
<p
className="h5"
data-hj-suppress={true}
/> />
</div>
</div>
<div <div
className="pgn-transition-replace-group position-relative mb-5" className="pgn-transition-replace-group position-relative mb-5"
style={ style={
@@ -6013,62 +232,23 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
"height": null, "height": null,
} }
} }
>
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
className="editable-item-header mb-2"
>
<h2
className="edit-section-header"
id={null}
>
Education
</h2>
</div>
<p
className="h5"
data-hj-suppress={true}
>
Other education
</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
</h2>
</div>
<ul
className="list-unstyled"
/> />
</div> <div
</div> className="pgn-transition-replace-group position-relative mb-5"
style={
Object {
"height": null,
}
}
/>
<div
className="pgn-transition-replace-group position-relative mb-5"
style={
Object {
"height": null,
}
}
/>
</div> </div>
<div <div
className="pt-md-3 col-md-8 col-lg-7 offset-lg-1" className="pt-md-3 col-md-8 col-lg-7 offset-lg-1"
@@ -6080,32 +260,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
"height": null, "height": null,
} }
} }
> />
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
className="editable-item-header mb-2"
>
<h2
className="edit-section-header"
id={null}
>
About Me
</h2>
</div>
<p
className="lead"
data-hj-suppress={true}
>
bio
</p>
</div>
</div>
<div <div
className="pgn-transition-replace-group position-relative mb-4" className="pgn-transition-replace-group position-relative mb-4"
style={ style={
@@ -6113,27 +268,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing other profi
"height": null, "height": null,
} }
} }
> />
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
className="editable-item-header mb-2"
>
<h2
className="edit-section-header"
id={null}
>
My Certificates
</h2>
</div>
You don't have any certificates yet.
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -6169,8 +304,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
className="profile-avatar-menu-container" className="profile-avatar-menu-container"
> >
<div <div
className="pgn__dropdown pgn__dropdown-light dropdown" className="dropdown"
data-testid="dropdown"
> >
<button <button
aria-expanded={false} aria-expanded={false}
@@ -6212,11 +346,8 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
</div> </div>
</div> </div>
</div> </div>
<div>
PluginPOC
</div>
<div <div
className="col" className="col pl-0"
> >
<div <div
className="d-md-none" className="d-md-none"
@@ -6225,15 +356,19 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
data-hj-suppress={true} data-hj-suppress={true}
> >
<h1 <h1
className="h2 mb-0 font-weight-bold text-truncate" className="h2 mb-0 font-weight-bold"
> >
staff staff
</h1> </h1>
<p <p
className="mb-0" className="mb-0"
> >
<span>
Member since Member since
<span>
2017 2017
</span>
</span>
</p> </p>
<hr <hr
className="d-none d-md-block" className="d-none d-md-block"
@@ -6275,7 +410,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<path <path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z" d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
fill="currentColor" fill="currentColor"
/> />
</svg> </svg>
@@ -6303,15 +438,19 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
data-hj-suppress={true} data-hj-suppress={true}
> >
<h1 <h1
className="h2 mb-0 font-weight-bold text-truncate" className="h2 mb-0 font-weight-bold"
> >
staff staff
</h1> </h1>
<p <p
className="mb-0" className="mb-0"
> >
<span>
Member since Member since
<span>
2017 2017
</span>
</span>
</p> </p>
<hr <hr
className="d-none d-md-block" className="d-none d-md-block"
@@ -6353,7 +492,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<path <path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z" d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
fill="currentColor" fill="currentColor"
/> />
</svg> </svg>
@@ -7119,7 +1258,9 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
<p <p
className="small mb-0" className="small mb-0"
> >
<span>
From From
</span>
</p> </p>
<p <p
className="h6 mb-4" className="h6 mb-4"
@@ -7132,8 +1273,12 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
<p <p
className="small mb-2" className="small mb-2"
> >
<span>
Completed on Completed on
<span>
3/4/2019 3/4/2019
</span>
</span>
</p> </p>
<div> <div>
<a <a
@@ -7168,7 +1313,7 @@ exports[`<ProfilePage /> Renders correctly in various states viewing own profile
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<path <path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z" d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
fill="currentColor" fill="currentColor"
/> />
</svg> </svg>
@@ -7222,8 +1367,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
className="profile-avatar-menu-container" className="profile-avatar-menu-container"
> >
<div <div
className="pgn__dropdown pgn__dropdown-light dropdown" className="dropdown"
data-testid="dropdown"
> >
<button <button
aria-expanded={false} aria-expanded={false}
@@ -7265,11 +1409,8 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
</div> </div>
</div> </div>
</div> </div>
<div>
PluginPOC
</div>
<div <div
className="col" className="col pl-0"
> >
<div <div
className="d-md-none" className="d-md-none"
@@ -7278,15 +1419,19 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
data-hj-suppress={true} data-hj-suppress={true}
> >
<h1 <h1
className="h2 mb-0 font-weight-bold text-truncate" className="h2 mb-0 font-weight-bold"
> >
staff staff
</h1> </h1>
<p <p
className="mb-0" className="mb-0"
> >
<span>
Member since Member since
<span>
2017 2017
</span>
</span>
</p> </p>
<hr <hr
className="d-none d-md-block" className="d-none d-md-block"
@@ -7328,7 +1473,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<path <path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z" d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
fill="currentColor" fill="currentColor"
/> />
</svg> </svg>
@@ -7356,15 +1501,19 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
data-hj-suppress={true} data-hj-suppress={true}
> >
<h1 <h1
className="h2 mb-0 font-weight-bold text-truncate" className="h2 mb-0 font-weight-bold"
> >
staff staff
</h1> </h1>
<p <p
className="mb-0" className="mb-0"
> >
<span>
Member since Member since
<span>
2017 2017
</span>
</span>
</p> </p>
<hr <hr
className="d-none d-md-block" className="d-none d-md-block"
@@ -7406,7 +1555,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<path <path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z" d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
fill="currentColor" fill="currentColor"
/> />
</svg> </svg>
@@ -7986,7 +2135,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
onSubmit={[Function]} onSubmit={[Function]}
> >
<div <div
className="pgn__form-group" className="form-group"
> >
<label <label
className="edit-section-header" className="edit-section-header"
@@ -7995,6 +2144,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
About Me About Me
</label> </label>
<textarea <textarea
aria-describedby=""
className="form-control" className="form-control"
id="bio" id="bio"
name="bio" name="bio"
@@ -8044,7 +2194,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
</svg> </svg>
</span> </span>
<select <select
className="d-inline-block form-control" className="d-inline-block w-auto form-control"
id="visibilityBio" id="visibilityBio"
name="visibilityBio" name="visibilityBio"
onChange={[Function]} onChange={[Function]}
@@ -8095,7 +2245,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<path <path
d="M22 12A10 10 0 1 1 6.122 3.91l1.176 1.618A8 8 0 1 0 20 12h2Z" d="M22 12A10 10 0 116.122 3.91l1.176 1.618A8 8 0 1020 12h2z"
fill="currentColor" fill="currentColor"
/> />
</svg> </svg>
@@ -8239,1135 +2389,9 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
<p <p
className="small mb-0" className="small mb-0"
> >
From
</p>
<p
className="h6 mb-4"
>
edX
</p>
<div
className="flex-grow-1"
/>
<p
className="small mb-2"
>
Completed on
3/4/2019
</p>
<div>
<a
className="pgn__hyperlink default-link standalone-link btn btn-outline-primary"
href="http://www.example.com/"
onClick={[Function]}
rel="noopener noreferrer"
target="_blank"
>
View Certificate
<span
className="pgn__hyperlink__external-icon"
title="Opens in a new tab"
>
<span
className="pgn__icon"
style={
Object {
"height": "1em",
"width": "1em",
}
}
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
fill="currentColor"
/>
</svg>
<span
className="sr-only"
>
in a new tab
</span>
</span>
</span>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
`;
exports[`<ProfilePage /> Renders correctly in various states while saving an edited bio with error 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="pgn__dropdown pgn__dropdown-light dropdown"
data-testid="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>
PluginPOC
</div>
<div
className="col"
>
<div
className="d-md-none"
>
<span
data-hj-suppress={true}
>
<h1
className="h2 mb-0 font-weight-bold text-truncate"
>
staff
</h1>
<p
className="mb-0"
>
Member since
2017
</p>
<hr
className="d-none d-md-block"
/>
</span>
</div>
<div
className="d-none d-md-block float-right"
>
<a
className="pgn__hyperlink default-link standalone-link btn btn-primary"
href="http://localhost:18150/records"
onClick={[Function]}
rel="noopener noreferrer"
target="_blank"
>
View My Records
<span
className="pgn__hyperlink__external-icon"
title="Opens in a new tab"
>
<span
className="pgn__icon"
style={
Object {
"height": "1em",
"width": "1em",
}
}
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
fill="currentColor"
/>
</svg>
<span
className="sr-only"
>
in a new tab
</span>
</span>
</span>
</a>
</div>
</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 text-truncate"
>
staff
</h1>
<p
className="mb-0"
>
Member since
2017
</p>
<hr
className="d-none d-md-block"
/>
</span>
</div>
<div
className="d-md-none mb-4"
>
<a
className="pgn__hyperlink default-link standalone-link btn btn-primary"
href="http://localhost:18150/records"
onClick={[Function]}
rel="noopener noreferrer"
target="_blank"
>
View My Records
<span
className="pgn__hyperlink__external-icon"
title="Opens in a new tab"
>
<span
className="pgn__icon"
style={
Object {
"height": "1em",
"width": "1em",
}
}
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z"
fill="currentColor"
/>
</svg>
<span
className="sr-only"
>
in a new tab
</span>
</span>
</span>
</a>
</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}
>
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 localhost
</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 localhost
</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 localhost
</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-16 mr-2"
data-icon="facebook"
data-prefix="fab"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M504 256C504 119 393 8 256 8S8 119 8 256c0 123.78 90.69 226.38 209.25 245V327.69h-63V256h63v-54.64c0-62.15 37-96.48 93.67-96.48 27.14 0 55.52 4.84 55.52 4.84v61h-31.28c-30.8 0-40.41 19.12-40.41 38.73V256h68.78l-11 71.69h-57.78V501C413.31 482.38 504 379.78 504 256z"
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
aria-labelledby="bio-label"
role="dialog"
>
<form
onSubmit={[Function]}
>
<div
className="pgn__form-group"
>
<label
className="edit-section-header"
htmlFor="bio"
>
About Me
</label>
<textarea
className="form-control"
id="bio"
name="bio"
onChange={[Function]}
value="This is my bio"
/>
<div
className="pgn__form-control-description pgn__form-text pgn__form-text-invalid"
id="bio-1"
>
<div>
bio error
</div>
</div>
</div>
<div
className="d-flex flex-row-reverse flex-wrap justify-content-end align-items-center"
>
<div
className="form-group d-flex flex-wrap"
>
<label
className="col-form-label"
htmlFor="visibilityBio"
>
Who can see this:
</label>
<span
className="d-flex align-items-center"
>
<span
className="d-inline-block ml-1 mr-2"
style={
Object {
"width": "1.5rem",
}
}
>
<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>
</span>
<select
className="d-inline-block form-control"
id="visibilityBio"
name="visibilityBio"
onChange={[Function]}
type="select"
value="all_users"
>
<option
value="private"
>
Just me
</option>
<option
value="all_users"
>
Everyone on localhost
</option>
</select>
</span>
</div>
<div
className="form-group flex-shrink-0 flex-grow-1"
>
<button
aria-disabled={false}
aria-live="assertive"
className="pgn__stateful-btn pgn__stateful-btn-state-pending btn btn-primary"
disabled={false}
onClick={[Function]}
type="submit"
>
<span
className="d-flex align-items-center justify-content-center"
>
<span
className="pgn__stateful-btn-icon"
>
<span
className="pgn__icon icon-spin"
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M22 12A10 10 0 1 1 6.122 3.91l1.176 1.618A8 8 0 1 0 20 12h2Z"
fill="currentColor"
/>
</svg>
</span>
</span>
<span> <span>
Saving
</span>
</span>
</button>
<button
className="btn btn-link"
disabled={false}
onClick={[Function]}
type="button"
>
Cancel
</button>
</div>
</div>
</form>
</div>
</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 localhost
</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"
>
From From
</span>
</p> </p>
<p <p
className="h6 mb-4" className="h6 mb-4"
@@ -9380,8 +2404,12 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
<p <p
className="small mb-2" className="small mb-2"
> >
<span>
Completed on Completed on
<span>
3/4/2019 3/4/2019
</span>
</span>
</p> </p>
<div> <div>
<a <a
@@ -9416,7 +2444,7 @@ exports[`<ProfilePage /> Renders correctly in various states while saving an edi
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<path <path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z" d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
fill="currentColor" fill="currentColor"
/> />
</svg> </svg>
@@ -9470,8 +2498,7 @@ exports[`<ProfilePage /> Renders correctly in various states without credentials
className="profile-avatar-menu-container" className="profile-avatar-menu-container"
> >
<div <div
className="pgn__dropdown pgn__dropdown-light dropdown" className="dropdown"
data-testid="dropdown"
> >
<button <button
aria-expanded={false} aria-expanded={false}
@@ -9513,11 +2540,8 @@ exports[`<ProfilePage /> Renders correctly in various states without credentials
</div> </div>
</div> </div>
</div> </div>
<div>
PluginPOC
</div>
<div <div
className="col" className="col pl-0"
> >
<div <div
className="d-md-none" className="d-md-none"
@@ -9526,15 +2550,19 @@ exports[`<ProfilePage /> Renders correctly in various states without credentials
data-hj-suppress={true} data-hj-suppress={true}
> >
<h1 <h1
className="h2 mb-0 font-weight-bold text-truncate" className="h2 mb-0 font-weight-bold"
> >
staff staff
</h1> </h1>
<p <p
className="mb-0" className="mb-0"
> >
<span>
Member since Member since
<span>
2017 2017
</span>
</span>
</p> </p>
<hr <hr
className="d-none d-md-block" className="d-none d-md-block"
@@ -9559,15 +2587,19 @@ exports[`<ProfilePage /> Renders correctly in various states without credentials
data-hj-suppress={true} data-hj-suppress={true}
> >
<h1 <h1
className="h2 mb-0 font-weight-bold text-truncate" className="h2 mb-0 font-weight-bold"
> >
staff staff
</h1> </h1>
<p <p
className="mb-0" className="mb-0"
> >
<span>
Member since Member since
<span>
2017 2017
</span>
</span>
</p> </p>
<hr <hr
className="d-none d-md-block" className="d-none d-md-block"
@@ -10330,7 +3362,9 @@ exports[`<ProfilePage /> Renders correctly in various states without credentials
<p <p
className="small mb-0" className="small mb-0"
> >
<span>
From From
</span>
</p> </p>
<p <p
className="h6 mb-4" className="h6 mb-4"
@@ -10343,8 +3377,12 @@ exports[`<ProfilePage /> Renders correctly in various states without credentials
<p <p
className="small mb-2" className="small mb-2"
> >
<span>
Completed on Completed on
<span>
3/4/2019 3/4/2019
</span>
</span>
</p> </p>
<div> <div>
<a <a
@@ -10379,7 +3417,7 @@ exports[`<ProfilePage /> Renders correctly in various states without credentials
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
> >
<path <path
d="M19 19H5V5h7V3H3v18h18v-9h-2v7ZM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7Z" d="M19 19H5V5h7V3H3v18h18v-9h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"
fill="currentColor" fill="currentColor"
/> />
</svg> </svg>

View File

@@ -7,7 +7,7 @@ const EDUCATION_LEVELS = [
'jhs', 'jhs',
'el', 'el',
'none', 'none',
'other', 'o',
]; ];
const SOCIAL = { const SOCIAL = {

View File

@@ -1,7 +0,0 @@
const mockData = {
learningGoal: 'advance_career',
editMode: 'static',
visibilityLearningGoal: 'private',
};
export default mockData;

View File

@@ -1,80 +0,0 @@
// This test file simply creates a contract that defines
// expectations and correct responses from the Pact stub server.
import path from 'path';
import { PactV3, MatchersV3 } from '@pact-foundation/pact';
import { initializeMockApp, getConfig, setConfig } from '@edx/frontend-platform';
import { getAccount } from './services';
const expectedUserInfo200 = {
username: 'staff',
email: 'staff@example.com',
bio: 'This is my bio',
name: 'Lemon Seltzer',
country: 'ME',
dateJoined: '2017-06-07T00:44:23Z',
isActive: true,
yearOfBirth: 1901,
};
const provider = new PactV3({
log: path.resolve(process.cwd(), 'src/pact-logs/pact.log'),
dir: path.resolve(process.cwd(), 'src/pacts'),
consumer: 'frontend-app-profile',
provider: 'edx-platform',
});
describe('getAccount for one username', () => {
beforeAll(async () => {
initializeMockApp();
});
it('returns a HTTP 200 and user information', async () => {
const username200 = 'staff';
await provider.addInteraction({
states: [{ description: "I have a user's basic information" }],
uponReceiving: "A request for user's basic information",
withRequest: {
method: 'GET',
path: `/api/user/v1/accounts/${username200}`,
headers: {},
},
willRespondWith: {
status: 200,
headers: {},
body: MatchersV3.like(expectedUserInfo200),
},
});
return provider.executeTest(async (mockserver) => {
setConfig({
...getConfig(),
LMS_BASE_URL: mockserver.url,
});
const response = await getAccount(username200);
expect(response).toEqual(expectedUserInfo200);
});
});
it('Account does not exist', async () => {
const username404 = 'staff_not_found';
await provider.addInteraction({
states: [{ description: "Account and user's information does not exist" }],
uponReceiving: "A request for user's basic information",
withRequest: {
method: 'GET',
path: `/api/user/v1/accounts/${username404}`,
},
willRespondWith: {
status: 404,
},
});
await provider.executeTest(async (mockserver) => {
setConfig({
...getConfig(),
LMS_BASE_URL: mockserver.url,
});
await expect(getAccount(username404).then((response) => response.data)).rejects.toThrow('Request failed with status code 404');
});
});
});

View File

@@ -24,7 +24,7 @@ export const initialState = {
isAuthenticatedUserProfile: false, isAuthenticatedUserProfile: false,
}; };
const profilePage = (state = initialState, action = {}) => { const profilePage = (state = initialState, action) => {
switch (action.type) { switch (action.type) {
case FETCH_PROFILE.BEGIN: case FETCH_PROFILE.BEGIN:
return { return {

View File

@@ -66,25 +66,6 @@ export function* handleFetchProfile(action) {
} else { } else {
[account, courseCertificates] = result; [account, courseCertificates] = result;
} }
// Set initial visibility values for account
// Set account_privacy as custom is necessary so that when viewing another user's profile,
// their full name is displayed and change visibility forms are worked correctly
if (isAuthenticatedUserProfile && result[0].accountPrivacy === 'all_users') {
yield call(ProfileApiService.patchPreferences, action.payload.username, {
account_privacy: 'custom',
'visibility.name': 'all_users',
'visibility.bio': 'all_users',
'visibility.course_certificates': 'all_users',
'visibility.country': 'all_users',
'visibility.date_joined': 'all_users',
'visibility.level_of_education': 'all_users',
'visibility.language_proficiencies': 'all_users',
'visibility.social_links': 'all_users',
'visibility.time_zone': 'all_users',
});
}
yield put(fetchProfileSuccess( yield put(fetchProfileSuccess(
account, account,
preferences, preferences,

View File

@@ -35,12 +35,9 @@ export const editableFormModeSelector = createSelector(
// or is being hidden from us (for other users' profiles) // or is being hidden from us (for other users' profiles)
let propExists = account[formId] != null && account[formId].length > 0; let propExists = account[formId] != null && account[formId].length > 0;
propExists = formId === 'certificates' ? certificates.length > 0 : propExists; // overwrite for certificates propExists = formId === 'certificates' ? certificates.length > 0 : propExists; // overwrite for certificates
// If this isn't the current user's profile // If this isn't the current user's profile or if
if (!isAuthenticatedUserProfile) {
return 'static';
}
// the current user has no age set / under 13 ... // the current user has no age set / under 13 ...
if (account.requiresParentalConsent) { if (!isAuthenticatedUserProfile || account.requiresParentalConsent) {
// then there are only two options: static or nothing. // then there are only two options: static or nothing.
// We use 'null' as a return value because the consumers of // We use 'null' as a return value because the consumers of
// getMode render nothing at all on a mode of null. // getMode render nothing at all on a mode of null.
@@ -231,13 +228,13 @@ export const visibilitiesSelector = createSelector(
switch (accountPrivacy) { switch (accountPrivacy) {
case 'custom': case 'custom':
return { return {
visibilityBio: preferences.visibilityBio || 'all_users', visibilityBio: preferences.visibilityBio || 'private',
visibilityCourseCertificates: preferences.visibilityCourseCertificates || 'all_users', visibilityCourseCertificates: preferences.visibilityCourseCertificates || 'private',
visibilityCountry: preferences.visibilityCountry || 'all_users', visibilityCountry: preferences.visibilityCountry || 'private',
visibilityLevelOfEducation: preferences.visibilityLevelOfEducation || 'all_users', visibilityLevelOfEducation: preferences.visibilityLevelOfEducation || 'private',
visibilityLanguageProficiencies: preferences.visibilityLanguageProficiencies || 'all_users', visibilityLanguageProficiencies: preferences.visibilityLanguageProficiencies || 'private',
visibilityName: preferences.visibilityName || 'all_users', visibilityName: preferences.visibilityName || 'private',
visibilitySocialLinks: preferences.visibilitySocialLinks || 'all_users', visibilitySocialLinks: preferences.visibilitySocialLinks || 'private',
}; };
case 'private': case 'private':
return { return {

View File

@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Form } from '@edx/paragon'; import { ValidationFormGroup } from '@edx/paragon';
import messages from './Bio.messages'; import messages from './Bio.messages';
@@ -56,9 +56,10 @@ class Bio extends React.Component {
editing: ( editing: (
<div role="dialog" aria-labelledby={`${formId}-label`}> <div role="dialog" aria-labelledby={`${formId}-label`}>
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
<Form.Group <ValidationFormGroup
controlId={formId} for={formId}
isInvalid={error !== null} invalid={error !== null}
invalidMessage={error}
> >
<label className="edit-section-header" htmlFor={formId}> <label className="edit-section-header" htmlFor={formId}>
{intl.formatMessage(messages['profile.bio.about.me'])} {intl.formatMessage(messages['profile.bio.about.me'])}
@@ -70,12 +71,7 @@ class Bio extends React.Component {
value={bio} value={bio}
onChange={this.handleChange} onChange={this.handleChange}
/> />
{error !== null && ( </ValidationFormGroup>
<Form.Control.Feedback hasIcon={false}>
{error}
</Form.Control.Feedback>
)}
</Form.Group>
<FormControls <FormControls
visibilityId="visibilityBio" visibilityId="visibilityBio"
saveState={saveState} saveState={saveState}

View File

@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Form } from '@edx/paragon'; import { ValidationFormGroup } from '@edx/paragon';
import messages from './Country.messages'; import messages from './Country.messages';
@@ -67,9 +67,10 @@ class Country extends React.Component {
editing: ( editing: (
<div role="dialog" aria-labelledby={`${formId}-label`}> <div role="dialog" aria-labelledby={`${formId}-label`}>
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
<Form.Group <ValidationFormGroup
controlId={formId} for={formId}
isInvalid={error !== null} invalid={error !== null}
invalidMessage={error}
> >
<label className="edit-section-header" htmlFor={formId}> <label className="edit-section-header" htmlFor={formId}>
{intl.formatMessage(messages['profile.country.label'])} {intl.formatMessage(messages['profile.country.label'])}
@@ -88,12 +89,7 @@ class Country extends React.Component {
<option key={code} value={code}>{name}</option> <option key={code} value={code}>{name}</option>
))} ))}
</select> </select>
{error !== null && ( </ValidationFormGroup>
<Form.Control.Feedback hasIcon={false}>
{error}
</Form.Control.Feedback>
)}
</Form.Group>
<FormControls <FormControls
visibilityId="visibilityCountry" visibilityId="visibilityCountry"
saveState={saveState} saveState={saveState}

View File

@@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import get from 'lodash.get'; import get from 'lodash.get';
import { Form } from '@edx/paragon'; import { ValidationFormGroup } from '@edx/paragon';
import messages from './Education.messages'; import messages from './Education.messages';
@@ -63,9 +63,10 @@ class Education extends React.Component {
editing: ( editing: (
<div role="dialog" aria-labelledby={`${formId}-label`}> <div role="dialog" aria-labelledby={`${formId}-label`}>
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
<Form.Group <ValidationFormGroup
controlId={formId} for={formId}
isInvalid={error !== null} invalid={error !== null}
invalidMessage={error}
> >
<label className="edit-section-header" htmlFor={formId}> <label className="edit-section-header" htmlFor={formId}>
{intl.formatMessage(messages['profile.education.education'])} {intl.formatMessage(messages['profile.education.education'])}
@@ -89,12 +90,7 @@ class Education extends React.Component {
</option> </option>
))} ))}
</select> </select>
{error !== null && ( </ValidationFormGroup>
<Form.Control.Feedback hasIcon={false}>
{error}
</Form.Control.Feedback>
)}
</Form.Group>
<FormControls <FormControls
visibilityId="visibilityLevelOfEducation" visibilityId="visibilityLevelOfEducation"
saveState={saveState} saveState={saveState}

View File

@@ -1,92 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import get from 'lodash.get';
// Mock Data
import mockData from '../data/mock_data';
import messages from './LearningGoal.messages';
// Components
import EditableItemHeader from './elements/EditableItemHeader';
import SwitchContent from './elements/SwitchContent';
// Selectors
import { editableFormSelector } from '../data/selectors';
const LearningGoal = (props) => {
let { learningGoal, editMode, visibilityLearningGoal } = props;
const { intl } = props;
if (!learningGoal) {
learningGoal = mockData.learningGoal;
}
if (!editMode || editMode === 'empty') { // editMode defaults to 'empty', not sure why yet
editMode = mockData.editMode;
}
if (!visibilityLearningGoal) {
visibilityLearningGoal = mockData.visibilityLearningGoal;
}
return (
<SwitchContent
className="mb-5"
expression={editMode}
cases={{
editable: (
<>
<EditableItemHeader
content={intl.formatMessage(messages['profile.learningGoal.learningGoal'])}
showVisibility={visibilityLearningGoal !== null}
visibility={visibilityLearningGoal}
/>
<p data-hj-suppress className="lead">
{intl.formatMessage(get(
messages,
`profile.learningGoal.options.${learningGoal}`,
messages['profile.learningGoal.options.something_else'],
))}
</p>
</>
),
static: (
<>
<EditableItemHeader content={intl.formatMessage(messages['profile.learningGoal.learningGoal'])} />
<p data-hj-suppress className="lead">
{intl.formatMessage(get(
messages,
`profile.learningGoal.options.${learningGoal}`,
messages['profile.learningGoal.options.something_else'],
))}
</p>
</>
),
}}
/>
);
};
LearningGoal.propTypes = {
// From Selector
learningGoal: PropTypes.oneOf(['advance_career', 'start_career', 'learn_something_new', 'something_else']),
visibilityLearningGoal: PropTypes.oneOf(['private', 'all_users']),
editMode: PropTypes.oneOf(['editable', 'static']),
// i18n
intl: intlShape.isRequired,
};
LearningGoal.defaultProps = {
editMode: 'static',
learningGoal: null,
visibilityLearningGoal: 'private',
};
export default connect(
editableFormSelector,
{},
)(injectIntl(LearningGoal));

View File

@@ -1,31 +0,0 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
'profile.learningGoal.learningGoal': {
id: 'profile.learningGoal.learningGoal',
defaultMessage: 'Learning Goal',
description: 'A section of a user profile that displays their current learning goal.',
},
'profile.learningGoal.options.start_career': {
id: 'profile.learningGoal.options.start_career',
defaultMessage: 'I want to start my career',
description: 'Selected by user if their goal is to start their career.',
},
'profile.learningGoal.options.advance_career': {
id: 'profile.learningGoal.options.advance_career',
defaultMessage: 'I want to advance my career',
description: 'Selected by user if their goal is to advance their career.',
},
'profile.learningGoal.options.learn_something_new': {
id: 'profile.learningGoal.options.learn_something_new',
defaultMessage: 'I want to learn something new',
description: 'Selected by user if their goal is to learn something new.',
},
'profile.learningGoal.options.something_else': {
id: 'profile.learningGoal.options.something_else',
defaultMessage: 'Something else',
description: 'Selected by user if their goal is not described by the other choices.',
},
});
export default messages;

View File

@@ -1,122 +0,0 @@
import PropTypes from 'prop-types';
import React, { useMemo } from 'react';
import { Provider } from 'react-redux';
import renderer from 'react-test-renderer';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { configure as configureI18n, IntlProvider } from '@edx/frontend-platform/i18n';
import { getConfig } from '@edx/frontend-platform';
import { AppContext } from '@edx/frontend-platform/react';
import messages from '../../i18n';
import viewOwnProfileMockStore from '../__mocks__/viewOwnProfile.mockStore';
import savingEditedBioMockStore from '../__mocks__/savingEditedBio.mockStore';
import LearningGoal from './LearningGoal';
const mockStore = configureMockStore([thunk]);
// props to be passed down to LearningGoal component
const requiredLearningGoalProps = {
formId: 'learningGoal',
learningGoal: 'advance_career',
drafts: {},
visibilityLearningGoal: 'private',
editMode: 'static',
saveState: null,
error: null,
openHandler: jest.fn(),
};
configureI18n({
loggingService: { logError: jest.fn() },
config: {
ENVIRONMENT: 'production',
LANGUAGE_PREFERENCE_COOKIE_NAME: 'yum',
},
messages,
});
const LearningGoalWrapper = (props) => {
const contextValue = useMemo(() => ({
authenticatedUser: { userId: null, username: null, administrator: false },
config: getConfig(),
}), []);
return (
<AppContext.Provider
value={contextValue}
>
<IntlProvider locale="en">
<Provider store={props.store}>
<LearningGoal {...props} />
</Provider>
</IntlProvider>
</AppContext.Provider>
);
};
LearningGoalWrapper.defaultProps = {
store: mockStore(viewOwnProfileMockStore),
};
LearningGoalWrapper.propTypes = {
store: PropTypes.shape({}),
};
const LearningGoalWrapperWithStore = ({ store }) => {
const contextValue = useMemo(() => ({
authenticatedUser: { userId: null, username: null, administrator: false },
config: getConfig(),
}), []);
return (
<AppContext.Provider
value={contextValue}
>
<IntlProvider locale="en">
<Provider store={mockStore(store)}>
<LearningGoal {...requiredLearningGoalProps} formId="learningGoal" />
</Provider>
</IntlProvider>
</AppContext.Provider>
);
};
LearningGoalWrapperWithStore.defaultProps = {
store: mockStore(savingEditedBioMockStore),
};
LearningGoalWrapperWithStore.propTypes = {
store: PropTypes.shape({}),
};
describe('<LearningGoal />', () => {
describe('renders the current learning goal', () => {
it('renders "I want to advance my career"', () => {
const learningGoalRenderer = renderer.create(
<LearningGoalWrapper
{...requiredLearningGoalProps}
formId="learningGoal"
/>,
);
const learningGoalInstance = learningGoalRenderer.root;
expect(learningGoalInstance.findByProps({ className: 'lead' }).children).toEqual(['I want to advance my career']);
});
it('renders "Something else"', () => {
requiredLearningGoalProps.learningGoal = 'something_else';
const learningGoalRenderer = renderer.create(
<LearningGoalWrapper
{...requiredLearningGoalProps}
formId="learningGoal"
/>,
);
const learningGoalInstance = learningGoalRenderer.root;
expect(learningGoalInstance.findByProps({ className: 'lead' }).children).toEqual(['Something else']);
});
});
});

View File

@@ -1,40 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { injectIntl } from '@edx/frontend-platform/i18n';
import { Icon } from '@edx/paragon';
import { LocationOn } from '@edx/paragon/icons';
// Selectors
import { countrySelector } from '../data/selectors';
// eslint-disable-next-line react/prefer-stateless-function
class PluginCountry extends React.Component {
render() {
const {
country,
countryMessages,
} = this.props;
return (
<div className="pgn-icons-cell-horizontal">
<Icon src={LocationOn} />
<p className="h5 mt-1 ml-1">{countryMessages[country]}</p>
</div>
);
}
}
PluginCountry.propTypes = {
country: PropTypes.string,
countryMessages: PropTypes.objectOf(PropTypes.string).isRequired,
};
PluginCountry.defaultProps = {
country: null,
};
export default connect(
countrySelector,
{},
)(injectIntl(PluginCountry));

View File

@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Form } from '@edx/paragon'; import { ValidationFormGroup } from '@edx/paragon';
import messages from './PreferredLanguage.messages'; import messages from './PreferredLanguage.messages';
@@ -77,9 +77,10 @@ class PreferredLanguage extends React.Component {
editing: ( editing: (
<div role="dialog" aria-labelledby={`${formId}-label`}> <div role="dialog" aria-labelledby={`${formId}-label`}>
<form onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
<Form.Group <ValidationFormGroup
controlId={formId} for={formId}
isInvalid={error !== null} invalid={error !== null}
invalidMessage={error}
> >
<label className="edit-section-header" htmlFor={formId}> <label className="edit-section-header" htmlFor={formId}>
{intl.formatMessage(messages['profile.preferredlanguage.label'])} {intl.formatMessage(messages['profile.preferredlanguage.label'])}
@@ -97,12 +98,7 @@ class PreferredLanguage extends React.Component {
<option key={code} value={code}>{name}</option> <option key={code} value={code}>{name}</option>
))} ))}
</select> </select>
{error !== null && ( </ValidationFormGroup>
<Form.Control.Feedback hasIcon={false}>
{error}
</Form.Control.Feedback>
)}
</Form.Group>
<FormControls <FormControls
visibilityId="visibilityLanguageProficiencies" visibilityId="visibilityLanguageProficiencies"
saveState={saveState} saveState={saveState}

View File

@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Alert } from '@edx/paragon'; import { StatusAlert } from '@edx/paragon';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faTwitter, faFacebook, faLinkedin } from '@fortawesome/free-brands-svg-icons'; import { faTwitter, faFacebook, faLinkedin } from '@fortawesome/free-brands-svg-icons';
@@ -65,19 +65,6 @@ class SocialLinks extends React.Component {
} }
} }
handleSubmit(e) {
e.preventDefault();
this.props.submitHandler(this.props.formId);
}
handleClose() {
this.props.closeHandler(this.props.formId);
}
handleOpen() {
this.props.openHandler(this.props.formId);
}
mergeWithDrafts(newSocialLink) { mergeWithDrafts(newSocialLink) {
const knownPlatforms = ['twitter', 'facebook', 'linkedin']; const knownPlatforms = ['twitter', 'facebook', 'linkedin'];
const updated = []; const updated = [];
@@ -91,6 +78,19 @@ class SocialLinks extends React.Component {
return updated; return updated;
} }
handleSubmit(e) {
e.preventDefault();
this.props.submitHandler(this.props.formId);
}
handleClose() {
this.props.closeHandler(this.props.formId);
}
handleOpen() {
this.props.openHandler(this.props.formId);
}
render() { render() {
const { const {
socialLinks, visibilitySocialLinks, editMode, saveState, error, intl, socialLinks, visibilitySocialLinks, editMode, saveState, error, intl,
@@ -158,19 +158,14 @@ class SocialLinks extends React.Component {
), ),
editing: ( editing: (
<div role="dialog" aria-labelledby="social-links-label"> <div role="dialog" aria-labelledby="social-links-label">
<form aria-labelledby="editing-form" onSubmit={this.handleSubmit}> <form onSubmit={this.handleSubmit}>
<EditableItemHeader <EditableItemHeader
headingId="social-links-label" headingId="social-links-label"
content={intl.formatMessage(messages['profile.sociallinks.social.links'])} content={intl.formatMessage(messages['profile.sociallinks.social.links'])}
/> />
{/* TODO: Replace this alert with per-field errors. Needs API update. */} {/* TODO: Replace this alert with per-field errors. Needs API update. */}
<div id="social-error-feedback"> <div id="social-error-feedback">
{error !== null {error !== null ? <StatusAlert alertType="danger" dialog={error} dismissible={false} open /> : null}
? (
<Alert variant="danger" dismissible={false} show>
{error}
</Alert>
) : null}
</div> </div>
<ul className="list-unstyled"> <ul className="list-unstyled">
{socialLinks.map(({ platform, socialLink }) => ( {socialLinks.map(({ platform, socialLink }) => (
@@ -244,12 +239,14 @@ export default connect(
{}, {},
)(injectIntl(SocialLinks)); )(injectIntl(SocialLinks));
const SocialLink = ({ url, name, platform }) => ( function SocialLink({ url, name, platform }) {
return (
<a href={url} className="font-weight-bold"> <a href={url} className="font-weight-bold">
<FontAwesomeIcon className="mr-2" icon={platformDisplayInfo[platform].icon} /> <FontAwesomeIcon className="mr-2" icon={platformDisplayInfo[platform].icon} />
{name} {name}
</a> </a>
); );
}
SocialLink.propTypes = { SocialLink.propTypes = {
url: PropTypes.string.isRequired, url: PropTypes.string.isRequired,
@@ -257,9 +254,9 @@ SocialLink.propTypes = {
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,
}; };
const EditableListItem = ({ function EditableListItem({
url, platform, onClickEmptyContent, name, url, platform, onClickEmptyContent, name,
}) => { }) {
const linkDisplay = url ? ( const linkDisplay = url ? (
<SocialLink name={name} url={url} platform={platform} /> <SocialLink name={name} url={url} platform={platform} />
) : ( ) : (
@@ -267,7 +264,7 @@ const EditableListItem = ({
); );
return <li className="form-group">{linkDisplay}</li>; return <li className="form-group">{linkDisplay}</li>;
}; }
EditableListItem.propTypes = { EditableListItem.propTypes = {
url: PropTypes.string, url: PropTypes.string,
@@ -280,9 +277,10 @@ EditableListItem.defaultProps = {
onClickEmptyContent: null, onClickEmptyContent: null,
}; };
const EditingListItem = ({ function EditingListItem({
platform, name, value, onChange, error, platform, name, value, onChange, error,
}) => ( }) {
return (
<li className="form-group"> <li className="form-group">
<label htmlFor={`social-${platform}`}>{name}</label> <label htmlFor={`social-${platform}`}>{name}</label>
<input <input
@@ -296,6 +294,7 @@ const EditingListItem = ({
/> />
</li> </li>
); );
}
EditingListItem.propTypes = { EditingListItem.propTypes = {
platform: PropTypes.string.isRequired, platform: PropTypes.string.isRequired,
@@ -310,7 +309,8 @@ EditingListItem.defaultProps = {
error: null, error: null,
}; };
const EmptyListItem = ({ onClick, name }) => ( function EmptyListItem({ onClick, name }) {
return (
<li className="mb-4"> <li className="mb-4">
<EmptyContent onClick={onClick}> <EmptyContent onClick={onClick}>
<FormattedMessage <FormattedMessage
@@ -324,17 +324,20 @@ const EmptyListItem = ({ onClick, name }) => (
</EmptyContent> </EmptyContent>
</li> </li>
); );
}
EmptyListItem.propTypes = { EmptyListItem.propTypes = {
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired, onClick: PropTypes.func.isRequired,
}; };
const StaticListItem = ({ name, url, platform }) => ( function StaticListItem({ name, url, platform }) {
return (
<li className="mb-2"> <li className="mb-2">
<SocialLink name={name} url={url} platform={platform} /> <SocialLink name={name} url={url} platform={platform} />
</li> </li>
); );
}
StaticListItem.propTypes = { StaticListItem.propTypes = {
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,

View File

@@ -1,169 +0,0 @@
import { mount } from 'enzyme';
import PropTypes from 'prop-types';
import React, { useMemo } from 'react';
import { Provider } from 'react-redux';
import renderer from 'react-test-renderer';
import configureMockStore from 'redux-mock-store';
import thunk from 'redux-thunk';
import { configure as configureI18n, IntlProvider } from '@edx/frontend-platform/i18n';
import { getConfig } from '@edx/frontend-platform';
import { AppContext } from '@edx/frontend-platform/react';
import SocialLinks from './SocialLinks';
import * as savingEditedBio from '../__mocks__/savingEditedBio.mockStore';
import messages from '../../i18n';
const mockStore = configureMockStore([thunk]);
const defaultProps = {
formId: 'socialLinks',
socialLinks: [
{
platform: 'facebook',
socialLink: 'https://www.facebook.com/aloha',
},
{
platform: 'twitter',
socialLink: 'https://www.twitter.com/ALOHA',
},
],
drafts: {},
visibilitySocialLinks: 'private',
editMode: 'static',
saveState: null,
error: null,
changeHandler: jest.fn(),
submitHandler: jest.fn(),
closeHandler: jest.fn(),
openHandler: jest.fn(),
};
configureI18n({
loggingService: { logError: jest.fn() },
config: {
ENVIRONMENT: 'production',
LANGUAGE_PREFERENCE_COOKIE_NAME: 'yum',
},
messages,
});
const SocialLinksWrapper = (props) => {
const contextValue = useMemo(() => ({
authenticatedUser: { userId: null, username: null, administrator: false },
config: getConfig(),
}), []);
return (
<AppContext.Provider
value={contextValue}
>
<IntlProvider locale="en">
<Provider store={props.store}>
<SocialLinks {...props} />
</Provider>
</IntlProvider>
</AppContext.Provider>
);
};
SocialLinksWrapper.defaultProps = {
store: mockStore(savingEditedBio),
};
SocialLinksWrapper.propTypes = {
store: PropTypes.shape({}),
};
const SocialLinksWrapperWithStore = ({ store }) => {
const contextValue = useMemo(() => ({
authenticatedUser: { userId: null, username: null, administrator: false },
config: getConfig(),
}), []);
return (
<AppContext.Provider
value={contextValue}
>
<IntlProvider locale="en">
<Provider store={mockStore(store)}>
<SocialLinks {...defaultProps} formId="bio" />
</Provider>
</IntlProvider>
</AppContext.Provider>
);
};
SocialLinksWrapperWithStore.defaultProps = {
store: mockStore(savingEditedBio),
};
SocialLinksWrapperWithStore.propTypes = {
store: PropTypes.shape({}),
};
describe('<SocialLinks />', () => {
['certificates', 'bio', 'goals', 'socialLinks'].forEach(editMode => (
it(`calls social links with edit mode ${editMode}`, () => {
const component = <SocialLinksWrapper {...defaultProps} formId={editMode} />;
const tree = renderer.create(component).toJSON();
expect(tree).toMatchSnapshot();
})
));
it('calls social links with editing', () => {
const changeHandler = jest.fn();
const submitHandler = jest.fn();
const closeHandler = jest.fn();
const component = (
<SocialLinksWrapper
{...defaultProps}
formId="bio"
changeHandler={changeHandler}
submitHandler={submitHandler}
closeHandler={closeHandler}
/>
);
const wrapper = mount(component);
const socialLink = wrapper.find(SocialLinks);
const { platform } = defaultProps.socialLinks[0];
const inputField = socialLink.find(`#social-${platform}`);
inputField.simulate('change', { target: { value: 'test', name: platform } });
expect(changeHandler).toHaveBeenCalledTimes(1);
expect(socialLink.find('#visibilitySocialLinks select').props().value).toBe('private');
const event = { target: { value: 'all_users', name: 'visibilitySocialLinks' } };
socialLink.find('#visibilitySocialLinks select').simulate('change', event);
expect(changeHandler).toHaveBeenCalledTimes(2);
socialLink.find('[aria-labelledby="editing-form"]').simulate('submit');
expect(submitHandler).toHaveBeenCalledTimes(1);
socialLink.find('[aria-labelledby="editing-form"]').find('Button .btn-link').simulate('click');
expect(closeHandler).toHaveBeenCalledTimes(1);
});
it('calls social links with static', () => {
const openHandler = jest.fn();
const component = (
<SocialLinksWrapper
{...defaultProps}
formId="goals"
openHandler={openHandler}
/>
);
const wrapper = mount(component);
const socialLink = wrapper.find(SocialLinks);
socialLink.find('EmptyContent button').first().simulate('click');
expect(openHandler).toHaveBeenCalledTimes(1);
});
it('calls social links with error', () => {
const newStore = JSON.parse(JSON.stringify(savingEditedBio));
newStore.profilePage.errors.bio = { userMessage: 'error' };
const component = <SocialLinksWrapperWithStore store={newStore} />;
const wrapper = mount(component);
const socialLink = wrapper.find(SocialLinks);
expect(socialLink.find('.alert-danger').exists()).toBe(true);
});
});

View File

@@ -1,586 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<SocialLinks /> calls social links with edit mode bio 1`] = `
<div
className="pgn-transition-replace-group position-relative mb-5"
style={
Object {
"height": null,
}
}
>
<div
style={
Object {
"padding": ".1px 0",
}
}
>
<div
aria-labelledby="social-links-label"
role="dialog"
>
<form
aria-labelledby="editing-form"
onSubmit={[Function]}
>
<div
className="editable-item-header mb-2"
>
<h2
className="edit-section-header"
id="social-links-label"
>
Social Links
</h2>
</div>
<div
id="social-error-feedback"
/>
<ul
className="list-unstyled"
>
<li
className="form-group"
>
<label
htmlFor="social-facebook"
>
Facebook
</label>
<input
aria-describedby="social-error-feedback"
className="form-control"
id="social-facebook"
name="facebook"
onChange={[Function]}
type="text"
value="https://www.facebook.com/aloha"
/>
</li>
<li
className="form-group"
>
<label
htmlFor="social-twitter"
>
Twitter
</label>
<input
aria-describedby="social-error-feedback"
className="form-control"
id="social-twitter"
name="twitter"
onChange={[Function]}
type="text"
value="https://www.twitter.com/ALOHA"
/>
</li>
</ul>
<div
className="d-flex flex-row-reverse flex-wrap justify-content-end align-items-center"
>
<div
className="form-group d-flex flex-wrap"
>
<label
className="col-form-label"
htmlFor="visibilitySocialLinks"
>
Who can see this:
</label>
<span
className="d-flex align-items-center"
>
<span
className="d-inline-block ml-1 mr-2"
style={
Object {
"width": "1.5rem",
}
}
>
<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>
</span>
<select
className="d-inline-block form-control"
id="visibilitySocialLinks"
name="visibilitySocialLinks"
onChange={[Function]}
type="select"
value="private"
>
<option
value="private"
>
Just me
</option>
<option
value="all_users"
>
Everyone on localhost
</option>
</select>
</span>
</div>
<div
className="form-group flex-shrink-0 flex-grow-1"
>
<button
aria-disabled={false}
aria-live="assertive"
className="pgn__stateful-btn pgn__stateful-btn-state-pending btn btn-primary"
disabled={false}
onClick={[Function]}
type="submit"
>
<span
className="d-flex align-items-center justify-content-center"
>
<span
className="pgn__stateful-btn-icon"
>
<span
className="pgn__icon icon-spin"
>
<svg
aria-hidden={true}
fill="none"
focusable={false}
height={24}
role="img"
viewBox="0 0 24 24"
width={24}
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M22 12A10 10 0 1 1 6.122 3.91l1.176 1.618A8 8 0 1 0 20 12h2Z"
fill="currentColor"
/>
</svg>
</span>
</span>
<span>
Saving
</span>
</span>
</button>
<button
className="btn btn-link"
disabled={false}
onClick={[Function]}
type="button"
>
Cancel
</button>
</div>
</div>
</form>
</div>
</div>
</div>
`;
exports[`<SocialLinks /> calls social links with edit mode certificates 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}
>
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-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>
<ul
className="list-unstyled"
>
<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-16 mr-2"
data-icon="facebook"
data-prefix="fab"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M504 256C504 119 393 8 256 8S8 119 8 256c0 123.78 90.69 226.38 209.25 245V327.69h-63V256h63v-54.64c0-62.15 37-96.48 93.67-96.48 27.14 0 55.52 4.84 55.52 4.84v61h-31.28c-30.8 0-40.41 19.12-40.41 38.73V256h68.78l-11 71.69h-57.78V501C413.31 482.38 504 379.78 504 256z"
fill="currentColor"
style={Object {}}
/>
</svg>
Facebook
</a>
</li>
<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>
</ul>
</div>
</div>
`;
exports[`<SocialLinks /> calls social links with edit mode goals 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}
>
Social Links
</h2>
</div>
<ul
className="list-unstyled"
>
<li
className="mb-4"
>
<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 Facebook
</button>
</div>
</li>
<li
className="mb-4"
>
<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 Twitter
</button>
</div>
</li>
</ul>
</div>
</div>
`;
exports[`<SocialLinks /> calls social links with edit mode socialLinks 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}
>
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-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>
<ul
className="list-unstyled"
>
<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-16 mr-2"
data-icon="facebook"
data-prefix="fab"
focusable="false"
role="img"
style={Object {}}
viewBox="0 0 512 512"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M504 256C504 119 393 8 256 8S8 119 8 256c0 123.78 90.69 226.38 209.25 245V327.69h-63V256h63v-54.64c0-62.15 37-96.48 93.67-96.48 27.14 0 55.52 4.84 55.52 4.84v61h-31.28c-30.8 0-40.41 19.12-40.41 38.73V256h68.78l-11 71.69h-57.78V501C413.31 482.38 504 379.78 504 256z"
fill="currentColor"
style={Object {}}
/>
</svg>
Facebook
</a>
</li>
<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>
</ul>
</div>
</div>
`;

View File

@@ -7,9 +7,10 @@ import { Button } from '@edx/paragon';
import messages from './EditButton.messages'; import messages from './EditButton.messages';
const EditButton = ({ function EditButton({
onClick, className, style, intl, onClick, className, style, intl,
}) => ( }) {
return (
<Button <Button
variant="link" variant="link"
size="sm" size="sm"
@@ -21,6 +22,7 @@ const EditButton = ({
{intl.formatMessage(messages['profile.editbutton.edit'])} {intl.formatMessage(messages['profile.editbutton.edit'])}
</Button> </Button>
); );
}
export default injectIntl(EditButton); export default injectIntl(EditButton);

View File

@@ -4,14 +4,16 @@ import PropTypes from 'prop-types';
import EditButton from './EditButton'; import EditButton from './EditButton';
import { Visibility } from './Visibility'; import { Visibility } from './Visibility';
const EditableItemHeader = ({ function EditableItemHeader({
content, content,
showVisibility, showVisibility,
visibility, visibility,
showEditButton, showEditButton,
onClickEdit, onClickEdit,
headingId, headingId,
}) => ( }) {
return (
<>
<div className="editable-item-header mb-2"> <div className="editable-item-header mb-2">
<h2 className="edit-section-header" id={headingId}> <h2 className="edit-section-header" id={headingId}>
{content} {content}
@@ -19,7 +21,9 @@ const EditableItemHeader = ({
</h2> </h2>
{showVisibility ? <p className="mb-0"><Visibility to={visibility} /></p> : null} {showVisibility ? <p className="mb-0"><Visibility to={visibility} /></p> : null}
</div> </div>
</>
); );
}
export default EditableItemHeader; export default EditableItemHeader;

View File

@@ -3,7 +3,8 @@ import PropTypes from 'prop-types';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faPlus } from '@fortawesome/free-solid-svg-icons'; import { faPlus } from '@fortawesome/free-solid-svg-icons';
const EmptyContent = ({ children, onClick, showPlusIcon }) => ( function EmptyContent({ children, onClick, showPlusIcon }) {
return (
<div> <div>
{onClick ? ( {onClick ? (
<button <button
@@ -19,6 +20,7 @@ const EmptyContent = ({ children, onClick, showPlusIcon }) => (
) : children} ) : children}
</div> </div>
); );
}
export default EmptyContent; export default EmptyContent;

View File

@@ -7,9 +7,9 @@ import messages from './FormControls.messages';
import { VisibilitySelect } from './Visibility'; import { VisibilitySelect } from './Visibility';
const FormControls = ({ function FormControls({
cancelHandler, changeHandler, visibility, visibilityId, saveState, intl, cancelHandler, changeHandler, visibility, visibilityId, saveState, intl,
}) => { }) {
// Eliminate error/failed state for save button // Eliminate error/failed state for save button
const buttonState = saveState === 'error' ? null : saveState; const buttonState = saveState === 'error' ? null : saveState;
@@ -57,7 +57,7 @@ const FormControls = ({
</div> </div>
</div> </div>
); );
}; }
export default injectIntl(FormControls); export default injectIntl(FormControls);

View File

@@ -22,7 +22,7 @@ const onChildExit = (htmlNode) => {
} }
}; };
const SwitchContent = ({ expression, cases, className }) => { function SwitchContent({ expression, cases, className }) {
const getContent = (caseKey) => { const getContent = (caseKey) => {
if (cases[caseKey]) { if (cases[caseKey]) {
if (typeof cases[caseKey] === 'string') { if (typeof cases[caseKey] === 'string') {
@@ -48,7 +48,7 @@ const SwitchContent = ({ expression, cases, className }) => {
{getContent(expression)} {getContent(expression)}
</TransitionReplace> </TransitionReplace>
); );
}; }
SwitchContent.propTypes = { SwitchContent.propTypes = {
expression: PropTypes.string, expression: PropTypes.string,

View File

@@ -7,7 +7,7 @@ import { faEyeSlash, faEye } from '@fortawesome/free-regular-svg-icons';
import messages from './Visibility.messages'; import messages from './Visibility.messages';
const Visibility = ({ to, intl }) => { function Visibility({ to, intl }) {
const icon = to === 'private' ? faEyeSlash : faEye; const icon = to === 'private' ? faEyeSlash : faEye;
const label = to === 'private' const label = to === 'private'
? intl.formatMessage(messages['profile.visibility.who.just.me']) ? intl.formatMessage(messages['profile.visibility.who.just.me'])
@@ -18,7 +18,7 @@ const Visibility = ({ to, intl }) => {
<FontAwesomeIcon icon={icon} /> {label} <FontAwesomeIcon icon={icon} /> {label}
</span> </span>
); );
}; }
Visibility.propTypes = { Visibility.propTypes = {
to: PropTypes.oneOf(['private', 'all_users']), to: PropTypes.oneOf(['private', 'all_users']),
@@ -30,7 +30,7 @@ Visibility.defaultProps = {
to: 'private', to: 'private',
}; };
const VisibilitySelect = ({ intl, className, ...props }) => { function VisibilitySelect({ intl, className, ...props }) {
const { value } = props; const { value } = props;
const icon = value === 'private' ? faEyeSlash : faEye; const icon = value === 'private' ? faEyeSlash : faEye;
@@ -39,7 +39,7 @@ const VisibilitySelect = ({ intl, className, ...props }) => {
<span className="d-inline-block ml-1 mr-2" style={{ width: '1.5rem' }}> <span className="d-inline-block ml-1 mr-2" style={{ width: '1.5rem' }}>
<FontAwesomeIcon icon={icon} /> <FontAwesomeIcon icon={icon} />
</span> </span>
<select className="d-inline-block form-control" {...props}> <select className="d-inline-block w-auto form-control" {...props}>
<option key="private" value="private"> <option key="private" value="private">
{intl.formatMessage(messages['profile.visibility.who.just.me'])} {intl.formatMessage(messages['profile.visibility.who.just.me'])}
</option> </option>
@@ -49,7 +49,7 @@ const VisibilitySelect = ({ intl, className, ...props }) => {
</select> </select>
</span> </span>
); );
}; }
VisibilitySelect.propTypes = { VisibilitySelect.propTypes = {
id: PropTypes.string, id: PropTypes.string,

View File

@@ -3,4 +3,3 @@ export { default as saga } from './data/sagas';
export { default as ProfilePage } from './ProfilePage'; export { default as ProfilePage } from './ProfilePage';
export { default as NotFoundPage } from './NotFoundPage'; export { default as NotFoundPage } from './NotFoundPage';
export { default as messages } from './ProfilePage.messages'; export { default as messages } from './ProfilePage.messages';
export { default as ProfilePluginPage } from './ProfilePluginPage';

View File

@@ -162,28 +162,4 @@
position: relative; position: relative;
} }
} }
.pgn-icons-cell-vertical {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
margin: 1px;
}
.pgn-icons-cell-horizontal {
display: flex;
flex-direction: row;
justify-content: center;
margin: 1px;
}
.profile-plugin-avatar {
@include media-breakpoint-up(xs) {
max-width: 12rem;
margin-right: 0;
margin-top: -4rem;
margin-bottom: 1rem;
}
}
} }

View File

@@ -1,18 +0,0 @@
import React from 'react';
import {
AuthenticatedPageRoute,
PageWrap,
} from '@edx/frontend-platform/react';
import { Routes, Route } from 'react-router-dom';
import { ProfilePage, NotFoundPage, ProfilePluginPage } from '../profile';
const AppRoutes = () => (
<Routes>
<Route path="/u/:username" element={<AuthenticatedPageRoute><ProfilePage /></AuthenticatedPageRoute>} />
<Route path="/u/:username/plugin" element={<AuthenticatedPageRoute><ProfilePluginPage /></AuthenticatedPageRoute>} />
<Route path="/notfound" element={<PageWrap><NotFoundPage /></PageWrap>} />
<Route path="*" element={<PageWrap><NotFoundPage /></PageWrap>} />
</Routes>
);
export default AppRoutes;

View File

@@ -1,80 +0,0 @@
import React from 'react';
import { AppContext } from '@edx/frontend-platform/react';
import { getConfig } from '@edx/frontend-platform';
import { MemoryRouter as Router } from 'react-router-dom';
import { render, screen } from '@testing-library/react';
import { getLoginRedirectUrl } from '@edx/frontend-platform/auth';
import AppRoutes from './AppRoutes';
jest.mock('@edx/frontend-platform/analytics');
jest.mock('@edx/frontend-platform/auth', () => ({
getLoginRedirectUrl: jest.fn(),
}));
jest.mock('../profile', () => ({
ProfilePage: () => (<div>Profile page</div>),
NotFoundPage: () => (<div>Not found page</div>),
ProfilePluginPage: () => (<div>Plugin page</div>),
}));
const RoutesWithProvider = (context, path) => (
<AppContext.Provider value={context}>
<Router initialEntries={[`${path}`]}>
<AppRoutes />
</Router>
</AppContext.Provider>
);
const unauthenticatedUser = {
authenticatedUser: null,
config: getConfig(),
};
describe('routes', () => {
test('Profile page should redirect for unauthenticated users', () => {
render(
RoutesWithProvider(unauthenticatedUser, '/u/edx'),
);
expect(getLoginRedirectUrl).toHaveBeenCalled();
});
test('Profile page should be accessible for authenticated users', () => {
render(
RoutesWithProvider(
{
authenticatedUser: {
username: 'edx',
email: 'edx@example.com',
},
config: getConfig(),
},
'/u/edx',
),
);
expect(screen.getByText('Profile page')).toBeTruthy();
});
test('Profile Plugin page should be accessible for authenticated users', () => {
render(
RoutesWithProvider(
{
authenticatedUser: {
username: 'edx',
email: 'edx@example.com',
},
config: getConfig(),
},
'/u/edx/plugin',
),
);
expect(screen.getByText('Plugin page')).toBeTruthy();
});
test('should show NotFound page for a bad route', () => {
render(
RoutesWithProvider(unauthenticatedUser, '/nonMatchingRoute'),
);
expect(screen.getByText('Not found page')).toBeTruthy();
});
});

View File

@@ -2,6 +2,6 @@ import 'core-js/stable';
import 'regenerator-runtime/runtime'; import 'regenerator-runtime/runtime';
import Enzyme from 'enzyme'; import Enzyme from 'enzyme';
import Adapter from '@wojtekmaj/enzyme-adapter-react-17'; import Adapter from 'enzyme-adapter-react-16';
Enzyme.configure({ adapter: new Adapter() }); Enzyme.configure({ adapter: new Adapter() });

View File

@@ -1,10 +0,0 @@
import React from 'react';
import { useParams } from 'react-router-dom';
const withParams = (WrappedComponent) => {
const WithParamsComponent = (props) => <WrappedComponent params={useParams()} {...props} />;
return WithParamsComponent;
};
export default withParams;