Compare commits
33 Commits
djoy/bump-
...
astankiewi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ba5456b03f | ||
|
|
4d8151a1d0 | ||
|
|
c39fd332b6 | ||
|
|
04d515f554 | ||
|
|
f425e9b94f | ||
|
|
7ec147fe6f | ||
|
|
7a8ae85b72 | ||
|
|
0f8f5a1e9a | ||
|
|
757a9ac033 | ||
|
|
9efc8d1290 | ||
|
|
5f53270148 | ||
|
|
9716495951 | ||
|
|
f9b29948e7 | ||
|
|
bededb3912 | ||
|
|
bb6390f9ae | ||
|
|
d2300d2dfd | ||
|
|
a501407907 | ||
|
|
88e63cd390 | ||
|
|
eaebe6980b | ||
|
|
d6efba63ca | ||
|
|
257e425fd9 | ||
|
|
02e3364874 | ||
|
|
9ae74708fb | ||
|
|
e946e377c6 | ||
|
|
5dbe649b2c | ||
|
|
2792902975 | ||
|
|
6f643070ea | ||
|
|
ce946f56b2 | ||
|
|
de1e67f68d | ||
|
|
79001bccd8 | ||
|
|
963884cc4c | ||
|
|
e43c1bcc9e | ||
|
|
32cc2c7835 |
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@@ -1 +0,0 @@
|
||||
* @edx/community-engineering
|
||||
19
.github/workflows/add-depr-ticket-to-depr-board.yml
vendored
Normal file
19
.github/workflows/add-depr-ticket-to-depr-board.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
# Run the workflow that adds new tickets that are either:
|
||||
# - labelled "DEPR"
|
||||
# - title starts with "[DEPR]"
|
||||
# - body starts with "Proposal Date" (this is the first template field)
|
||||
# to the org-wide DEPR project board
|
||||
|
||||
name: Add newly created DEPR issues to the DEPR project board
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
routeissue:
|
||||
uses: openedx/.github/.github/workflows/add-depr-ticket-to-depr-board.yml@master
|
||||
secrets:
|
||||
GITHUB_APP_ID: ${{ secrets.GRAPHQL_AUTH_APP_ID }}
|
||||
GITHUB_APP_PRIVATE_KEY: ${{ secrets.GRAPHQL_AUTH_APP_PEM }}
|
||||
SLACK_BOT_TOKEN: ${{ secrets.SLACK_ISSUE_BOT_TOKEN }}
|
||||
13
.github/workflows/ci.yml
vendored
13
.github/workflows/ci.yml
vendored
@@ -1,4 +1,3 @@
|
||||
---
|
||||
name: ci
|
||||
on:
|
||||
push:
|
||||
@@ -6,23 +5,23 @@ on:
|
||||
- master
|
||||
pull_request:
|
||||
jobs:
|
||||
build:
|
||||
tests:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node-version:
|
||||
- 12
|
||||
node: [16]
|
||||
npm: [8.5.0]
|
||||
npm-test:
|
||||
- i18n_extract
|
||||
- is-es5
|
||||
- lint
|
||||
- is-es6
|
||||
- test
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: npm install -g npm@6
|
||||
node-version: ${{ matrix.node }}
|
||||
- run: npm install -g npm@${{ matrix.npm }}
|
||||
- run: make requirements
|
||||
- run: make test NPM_TESTS=build
|
||||
- run: make test NPM_TESTS=${{ matrix.npm-test }}
|
||||
|
||||
14
.github/workflows/lockfileversion-check.yml
vendored
Normal file
14
.github/workflows/lockfileversion-check.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
#check package-lock file version
|
||||
|
||||
name: Lockfile Version check
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
version-check:
|
||||
uses: edx/.github/.github/workflows/lockfileversion-check.yml@master
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
|
||||
[edx-platform.frontend-app-account]
|
||||
[o:open-edx:p:edx-platform:r:frontend-app-account]
|
||||
file_filter = src/i18n/messages/<lang>.json
|
||||
source_file = src/i18n/transifex_input.json
|
||||
source_lang = en
|
||||
type = KEYVALUEJSON
|
||||
type = KEYVALUEJSON
|
||||
|
||||
|
||||
11
Makefile
11
Makefile
@@ -1,3 +1,4 @@
|
||||
export TRANSIFEX_RESOURCE = frontend-app-account
|
||||
transifex_resource = frontend-app-account
|
||||
transifex_langs = "ar,fr,es_419,zh_CN"
|
||||
|
||||
@@ -10,7 +11,7 @@ tx_url2 = https://www.transifex.com/api/2/project/edx-platform/resource/$(transi
|
||||
# This directory must match .babelrc .
|
||||
transifex_temp = ./temp/babel-plugin-react-intl
|
||||
|
||||
NPM_TESTS=build i18n_extract lint test is-es5
|
||||
NPM_TESTS=build i18n_extract lint test is-es6
|
||||
|
||||
.PHONY: test
|
||||
test: $(addprefix test.npm.,$(NPM_TESTS)) ## validate ci suite
|
||||
@@ -45,15 +46,15 @@ push_translations:
|
||||
# Pushing strings to Transifex...
|
||||
tx push -s
|
||||
# Fetching hashes from Transifex...
|
||||
./node_modules/reactifex/bash_scripts/get_hashed_strings.sh $(tx_url1)
|
||||
./node_modules/@edx/reactifex/bash_scripts/get_hashed_strings_v3.sh
|
||||
# Writing out comments to file...
|
||||
$(transifex_utils) $(transifex_temp) --comments
|
||||
$(transifex_utils) $(transifex_temp) --comments --v3-scripts-path
|
||||
# Pushing comments to Transifex...
|
||||
./node_modules/reactifex/bash_scripts/put_comments.sh $(tx_url2)
|
||||
./node_modules/@edx/reactifex/bash_scripts/put_comments_v3.sh
|
||||
|
||||
# Pulls translations from Transifex.
|
||||
pull_translations:
|
||||
tx pull -f --mode reviewed --language=$(transifex_langs)
|
||||
tx pull -f --mode reviewed --languages=$(transifex_langs)
|
||||
|
||||
# This target is used by Travis.
|
||||
validate-no-uncommitted-package-lock-changes:
|
||||
|
||||
31108
package-lock.json
generated
31108
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
21
package.json
21
package.json
@@ -11,8 +11,8 @@
|
||||
"scripts": {
|
||||
"build": "fedx-scripts webpack",
|
||||
"i18n_extract": "BABEL_ENV=i18n fedx-scripts babel src --quiet > /dev/null",
|
||||
"is-es5": "es-check es5 ./dist/*.js",
|
||||
"lint": "fedx-scripts eslint --ext .js --ext .jsx .",
|
||||
"is-es6": "es-check es6 ./dist/*.js",
|
||||
"snapshot": "fedx-scripts jest --updateSnapshot",
|
||||
"start": "fedx-scripts webpack-dev-server --progress",
|
||||
"test": "fedx-scripts jest --coverage --passWithNoTests"
|
||||
@@ -25,15 +25,14 @@
|
||||
"access": "public"
|
||||
},
|
||||
"browserslist": [
|
||||
"last 2 versions",
|
||||
"ie 11"
|
||||
"extends @edx/browserslist-config"
|
||||
],
|
||||
"dependencies": {
|
||||
"@edx/brand": "npm:@edx/brand-openedx@1.1.0",
|
||||
"@edx/frontend-component-footer": "10.1.6",
|
||||
"@edx/frontend-component-header": "2.3.0",
|
||||
"@edx/frontend-platform": "1.14.1",
|
||||
"@edx/paragon": "16.1.0",
|
||||
"@edx/frontend-component-footer": "10.2.0",
|
||||
"@edx/frontend-component-header": "2.4.5",
|
||||
"@edx/frontend-platform": "1.15.1",
|
||||
"@edx/paragon": "19.20.0",
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.36",
|
||||
"@fortawesome/free-brands-svg-icons": "5.15.4",
|
||||
"@fortawesome/free-regular-svg-icons": "5.15.4",
|
||||
@@ -61,7 +60,7 @@
|
||||
"lodash.pickby": "4.6.0",
|
||||
"memoize-one": "5.2.1",
|
||||
"prop-types": "15.7.2",
|
||||
"qs": "6.10.1",
|
||||
"qs": "6.10.3",
|
||||
"react": "16.14.0",
|
||||
"react-dom": "16.14.0",
|
||||
"react-redux": "7.2.6",
|
||||
@@ -80,9 +79,11 @@
|
||||
"universal-cookie": "4.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@edx/frontend-build": "8.1.6",
|
||||
"@edx/browserslist-config": "1.0.0",
|
||||
"@edx/frontend-build": "9.1.1",
|
||||
"@edx/reactifex": "^1.0.3",
|
||||
"@testing-library/jest-dom": "5.15.1",
|
||||
"@testing-library/react": "12.1.2",
|
||||
"@testing-library/react": "12.1.4",
|
||||
"codecov": "3.8.3",
|
||||
"enzyme": "3.11.0",
|
||||
"enzyme-adapter-react-16": "1.15.6",
|
||||
|
||||
@@ -259,25 +259,27 @@ class AccountSettingsPage extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
renderFullNameHelpText = (status) => {
|
||||
if (
|
||||
!this.props.verifiedNameHistory
|
||||
) {
|
||||
renderFullNameHelpText = (status, proctoredExamId) => {
|
||||
if (!this.props.verifiedNameHistory) {
|
||||
return this.props.intl.formatMessage(messages['account.settings.field.full.name.help.text']);
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case 'submitted':
|
||||
if (this.props.committedValues.useVerifiedNameForCerts) {
|
||||
return this.props.intl.formatMessage(messages['account.settings.field.full.name.help.text.submitted']);
|
||||
}
|
||||
return this.props.intl.formatMessage(messages['account.settings.field.full.name.help.text.submitted.certificate']);
|
||||
default:
|
||||
if (this.props.committedValues.useVerifiedNameForCerts) {
|
||||
return this.props.intl.formatMessage(messages['account.settings.field.full.name.help.text.non.certificate']);
|
||||
}
|
||||
return this.props.intl.formatMessage(messages['account.settings.field.full.name.help.text.certificate']);
|
||||
let messageString = 'account.settings.field.full.name.help.text';
|
||||
|
||||
if (status === 'submitted') {
|
||||
messageString += '.submitted';
|
||||
if (proctoredExamId) {
|
||||
messageString += '.proctored';
|
||||
}
|
||||
} else {
|
||||
messageString += '.default';
|
||||
}
|
||||
|
||||
if (!this.props.committedValues.useVerifiedNameForCerts) {
|
||||
messageString += '.certificate';
|
||||
}
|
||||
|
||||
return this.props.intl.formatMessage(messages[messageString]);
|
||||
}
|
||||
|
||||
renderVerifiedNameSuccessMessage = (verifiedName, created) => {
|
||||
@@ -350,6 +352,7 @@ class AccountSettingsPage extends React.Component {
|
||||
status,
|
||||
profile_name: profileName,
|
||||
verified_name: verifiedName,
|
||||
proctored_exam_attempt_id: proctoredExamId,
|
||||
} = verifiedNameRecord;
|
||||
let willCertNameChange = false;
|
||||
|
||||
@@ -368,6 +371,10 @@ class AccountSettingsPage extends React.Component {
|
||||
willCertNameChange = true;
|
||||
}
|
||||
|
||||
if (proctoredExamId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (status) {
|
||||
case 'approved':
|
||||
return this.renderVerifiedNameSuccessMessage(verifiedName, created);
|
||||
@@ -391,21 +398,29 @@ class AccountSettingsPage extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
renderVerifiedNameHelpText = (status) => {
|
||||
switch (status) {
|
||||
case 'approved':
|
||||
if (this.props.committedValues.useVerifiedNameForCerts) {
|
||||
return this.props.intl.formatMessage(messages['account.settings.field.name.verified.help.text.certificate']);
|
||||
}
|
||||
return this.props.intl.formatMessage(messages['account.settings.field.name.verified.help.text.verified']);
|
||||
case 'submitted':
|
||||
if (this.props.committedValues.useVerifiedNameForCerts) {
|
||||
return this.props.intl.formatMessage(messages['account.settings.field.name.verified.help.text.submitted.certificate']);
|
||||
}
|
||||
return this.props.intl.formatMessage(messages['account.settings.field.name.verified.help.text.submitted']);
|
||||
default:
|
||||
return null;
|
||||
renderVerifiedNameHelpText = (status, proctoredExamId) => {
|
||||
let messageStr = 'account.settings.field.name.verified.help.text';
|
||||
|
||||
// add additional string based on status
|
||||
if (status === 'approved') {
|
||||
messageStr += '.verified';
|
||||
} else if (status === 'submitted') {
|
||||
messageStr += '.submitted';
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
|
||||
// add additional string if verified name came from a proctored exam attempt
|
||||
if (proctoredExamId) {
|
||||
messageStr += '.proctored';
|
||||
}
|
||||
|
||||
// add additional string based on certificate name use
|
||||
if (this.props.committedValues.useVerifiedNameForCerts) {
|
||||
messageStr += '.certificate';
|
||||
}
|
||||
|
||||
return this.props.intl.formatMessage(messages[messageStr]);
|
||||
}
|
||||
|
||||
renderEmptyStaticFieldMessage() {
|
||||
@@ -481,13 +496,13 @@ class AccountSettingsPage extends React.Component {
|
||||
const hasLinkedTPA = findIndex(this.props.tpaProviders, provider => provider.connected) >= 0;
|
||||
return (
|
||||
<>
|
||||
<div className="account-section" id="basic-information" ref={this.navLinkRefs['#basic-information']}>
|
||||
<div className="account-section pt-3 mb-5" id="basic-information" ref={this.navLinkRefs['#basic-information']}>
|
||||
{
|
||||
this.props.mostRecentVerifiedName
|
||||
&& this.renderVerifiedNameMessage(this.props.mostRecentVerifiedName)
|
||||
}
|
||||
|
||||
<h2 className="section-heading">
|
||||
<h2 className="section-heading h4 mb-3">
|
||||
{this.props.intl.formatMessage(messages['account.settings.section.account.information'])}
|
||||
</h2>
|
||||
<p>{this.props.intl.formatMessage(messages['account.settings.section.account.information.description'])}</p>
|
||||
@@ -524,7 +539,7 @@ class AccountSettingsPage extends React.Component {
|
||||
}
|
||||
helpText={
|
||||
verifiedName
|
||||
? this.renderFullNameHelpText(verifiedName.status)
|
||||
? this.renderFullNameHelpText(verifiedName.status, verifiedName.proctored_exam_attempt_id)
|
||||
: this.props.intl.formatMessage(messages['account.settings.field.full.name.help.text'])
|
||||
}
|
||||
isEditable={
|
||||
@@ -554,7 +569,7 @@ class AccountSettingsPage extends React.Component {
|
||||
</div>
|
||||
)
|
||||
}
|
||||
helpText={this.renderVerifiedNameHelpText(verifiedName.status)}
|
||||
helpText={this.renderVerifiedNameHelpText(verifiedName.status, verifiedName.proctored_exam_attempt_id)}
|
||||
isEditable={this.isEditable('verifiedName')}
|
||||
isGrayedOut={!this.isEditable('verifiedName')}
|
||||
onChange={this.handleEditableFieldChange}
|
||||
@@ -626,8 +641,8 @@ class AccountSettingsPage extends React.Component {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="account-section" id="profile-information" ref={this.navLinkRefs['#profile-information']}>
|
||||
<h2 className="section-heading">
|
||||
<div className="account-section pt-3 mb-5" id="profile-information" ref={this.navLinkRefs['#profile-information']}>
|
||||
<h2 className="section-heading h4 mb-3">
|
||||
{this.props.intl.formatMessage(messages['account.settings.section.profile.information'])}
|
||||
</h2>
|
||||
|
||||
@@ -669,8 +684,8 @@ class AccountSettingsPage extends React.Component {
|
||||
)}
|
||||
</div>
|
||||
{getConfig().ENABLE_DEMOGRAPHICS_COLLECTION && this.renderDemographicsSection()}
|
||||
<div className="account-section" id="social-media">
|
||||
<h2 className="section-heading">
|
||||
<div className="account-section pt-3 mb-5" id="social-media">
|
||||
<h2 className="section-heading h4 mb-3">
|
||||
{this.props.intl.formatMessage(messages['account.settings.section.social.media'])}
|
||||
</h2>
|
||||
<p>
|
||||
@@ -706,8 +721,8 @@ class AccountSettingsPage extends React.Component {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="account-section" id="site-preferences" ref={this.navLinkRefs['#site-preferences']}>
|
||||
<h2 className="section-heading">
|
||||
<div className="account-section pt-3 mb-5" id="site-preferences" ref={this.navLinkRefs['#site-preferences']}>
|
||||
<h2 className="section-heading h4 mb-3">
|
||||
{this.props.intl.formatMessage(messages['account.settings.section.site.preferences'])}
|
||||
</h2>
|
||||
|
||||
@@ -737,8 +752,8 @@ class AccountSettingsPage extends React.Component {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="account-section" id="linked-accounts" ref={this.navLinkRefs['#linked-accounts']}>
|
||||
<h2 className="section-heading">{this.props.intl.formatMessage(messages['account.settings.section.linked.accounts'])}</h2>
|
||||
<div className="account-section pt-3 mb-5" id="linked-accounts" ref={this.navLinkRefs['#linked-accounts']}>
|
||||
<h2 className="section-heading h4 mb-3">{this.props.intl.formatMessage(messages['account.settings.section.linked.accounts'])}</h2>
|
||||
<p>
|
||||
{this.props.intl.formatMessage(
|
||||
messages['account.settings.section.linked.accounts.description'],
|
||||
@@ -748,7 +763,7 @@ class AccountSettingsPage extends React.Component {
|
||||
<ThirdPartyAuth />
|
||||
</div>
|
||||
|
||||
<div className="account-section" id="delete-account" ref={this.navLinkRefs['#delete-account']}>
|
||||
<div className="account-section pt-3 mb-5" id="delete-account" ref={this.navLinkRefs['#delete-account']}>
|
||||
<DeleteAccount
|
||||
isVerifiedAccount={this.props.isActive}
|
||||
hasLinkedTPA={hasLinkedTPA}
|
||||
@@ -888,15 +903,18 @@ AccountSettingsPage.propTypes = {
|
||||
verifiedName: PropTypes.shape({
|
||||
verified_name: PropTypes.string,
|
||||
status: PropTypes.string,
|
||||
proctored_exam_attempt_id: PropTypes.number,
|
||||
}),
|
||||
mostRecentVerifiedName: PropTypes.shape({
|
||||
verified_name: PropTypes.string,
|
||||
status: PropTypes.string,
|
||||
proctored_exam_attempt_id: PropTypes.number,
|
||||
}),
|
||||
verifiedNameHistory: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
verified_name: PropTypes.string,
|
||||
status: PropTypes.string,
|
||||
proctored_exam_attempt_id: PropTypes.number,
|
||||
}),
|
||||
),
|
||||
};
|
||||
|
||||
@@ -91,13 +91,13 @@ const messages = defineMessages({
|
||||
defaultMessage: 'The name that is used for ID verification and that appears on your certificates.',
|
||||
description: 'Help text for the account settings name field.',
|
||||
},
|
||||
'account.settings.field.full.name.help.text.non.certificate': {
|
||||
id: 'account.settings.field.full.name.help.text.non.certificate',
|
||||
'account.settings.field.full.name.help.text.default': {
|
||||
id: 'account.settings.field.full.name.help.text.default',
|
||||
defaultMessage: 'The name that appears on your public profile.',
|
||||
description: 'Help text for the account settings name field.',
|
||||
},
|
||||
'account.settings.field.full.name.help.text.certificate': {
|
||||
id: 'account.settings.field.full.name.help.text.certificate',
|
||||
'account.settings.field.full.name.help.text.default.certificate': {
|
||||
id: 'account.settings.field.full.name.help.text.default.certificate',
|
||||
defaultMessage: 'This name is selected to appear on your certificates and public-facing records.',
|
||||
description: 'Help text for the account settings name field.',
|
||||
},
|
||||
@@ -108,27 +108,47 @@ const messages = defineMessages({
|
||||
},
|
||||
'account.settings.field.name.verified.help.text.verified': {
|
||||
id: 'account.settings.field.name.verified.help.text.verified',
|
||||
defaultMessage: 'This name has been verified by government ID.',
|
||||
defaultMessage: 'This name has been verified by photo ID.',
|
||||
description: 'Help text for the account settings verified name field when the name is verified.',
|
||||
},
|
||||
'account.settings.field.name.verified.help.text.certificate': {
|
||||
id: 'account.settings.field.name.verified.help.text.certificate',
|
||||
defaultMessage: 'This name has been verified by government ID and selected to appear on your certificates and public-facing records.',
|
||||
'account.settings.field.name.verified.help.text.verified.proctored': {
|
||||
id: 'account.settings.field.name.verified.help.text.verified.proctored',
|
||||
defaultMessage: 'This name has been verified by proctoring.',
|
||||
description: 'Help text for the account settings verified name field when the name is verified through proctoring.',
|
||||
},
|
||||
'account.settings.field.name.verified.help.text.verified.certificate': {
|
||||
id: 'account.settings.field.name.verified.help.text.verified.certificate',
|
||||
defaultMessage: 'This name has been verified by photo ID, and is selected to appear on your certificates and public-facing records.',
|
||||
description: 'Help text for the account settings verified name field when the name is selected for certificates.',
|
||||
},
|
||||
'account.settings.field.name.verified.help.text.verified.proctored.certificate': {
|
||||
id: 'account.settings.field.name.verified.help.text.verified.proctored.certificate',
|
||||
defaultMessage: 'This name has been verified by proctoring, and is selected to appear on your certificates and public-facing records.',
|
||||
description: 'Help text for the account settings verified name field when the name is selected for certificates, and the name is verified through proctoring.',
|
||||
},
|
||||
'account.settings.field.name.verified.help.text.submitted': {
|
||||
id: 'account.settings.field.name.verified.help.text.submitted',
|
||||
defaultMessage: 'Verification has been submitted. This usually takes 48 hours or less. Verified name cannot be changed at this time.',
|
||||
description: 'Help text for the account settings verified name field when a verified name has been submitted.',
|
||||
},
|
||||
'account.settings.field.name.verified.help.text.submitted.proctored': {
|
||||
id: 'account.settings.field.name.verified.help.text.submitted.proctored',
|
||||
defaultMessage: 'Your proctored exam has been submitted. Verified name cannot be changed at this time. Please check back in 2-5 days.',
|
||||
description: 'Help text for the account settings verified name field when a verified name has been submitted through proctoring.',
|
||||
},
|
||||
'account.settings.field.name.verified.help.text.submitted.certificate': {
|
||||
id: 'account.settings.field.name.verified.help.text.submitted.certificate',
|
||||
defaultMessage: 'When identity verification is successful, this name will appear on your certificates and public-facing records. Verified name cannot be changed at this time.',
|
||||
description: 'Help text for the account settings verified name field when a verified name has been submitted and will appear on certificates.',
|
||||
},
|
||||
'account.settings.field.name.verified.help.text.submitted.proctored.certificate': {
|
||||
id: 'account.settings.field.name.verified.help.text.submitted.proctored.certificate',
|
||||
defaultMessage: 'Once your proctored exam passes review, this name will appear on your certificate and public-facing records. Verified Name cannot be changed at this time.',
|
||||
description: 'Help text for the account settings verified name field when a verified name has been submitted through proctoring and will appear on certificates.',
|
||||
},
|
||||
'account.settings.field.name.verified.verification.alert': {
|
||||
id: 'account.settings.field.name.verified.verification.help',
|
||||
defaultMessage: 'Enter your name as it appears on your government-issued ID.',
|
||||
defaultMessage: 'Enter your name as it appears on your unexpired student, work, or government-issued identification card.',
|
||||
description: 'Form label instructing the user to enter the name on their ID.',
|
||||
},
|
||||
'account.settings.field.full.name.help.text.submitted': {
|
||||
@@ -136,11 +156,21 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Verification has been submitted. This usually takes 48 hours or less. Full name cannot be changed at this time.',
|
||||
description: 'Help text for the account settings full name field when a verified name has been submitted.',
|
||||
},
|
||||
'account.settings.field.full.name.help.text.submitted.proctored': {
|
||||
id: 'account.settings.field.full.name.help.text.submitted.proctored',
|
||||
defaultMessage: 'Your proctored exam has been submitted. Full name cannot be changed at this time. Please check back in 2-5 days.',
|
||||
description: 'Help text for the account settings full name field when a verified name has been submitted through proctoring.',
|
||||
},
|
||||
'account.settings.field.full.name.help.text.submitted.certificate': {
|
||||
id: 'account.settings.field.full.name.help.text.submitted.certificate',
|
||||
defaultMessage: 'When identity verification is successful, this name will appear on your certificates and public-facing records. Full name cannot be changed at this time.',
|
||||
description: 'Help text for the account settings full name field when a full name has been submitted and will appear on certificates.',
|
||||
},
|
||||
'account.settings.field.full.name.help.text.submitted.proctored.certificate': {
|
||||
id: 'account.settings.field.full.name.help.text.submitted.proctored.certificate',
|
||||
defaultMessage: 'Once your proctored exam passes review, this name will appear on your certificates and public-facing records. Full name cannot be changed at this time.',
|
||||
description: 'Help text for the account settings full name field when a full name has been submitted and will appear on certificates.',
|
||||
},
|
||||
'account.settings.field.name.verified.success.message': {
|
||||
id: 'account.settings.field.name.verified.success.message',
|
||||
defaultMessage: 'Your identity verification request has successfully completed. You now have the option of selecting which name you prefer to appear on your certificates and public-records.',
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
import React from 'react';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { breakpoints, useWindowSize } from '@edx/paragon';
|
||||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import React from 'react';
|
||||
import { NavHashLink } from 'react-router-hash-link';
|
||||
import Scrollspy from 'react-scrollspy';
|
||||
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import PropTypes from 'prop-types';
|
||||
import messages from './AccountSettingsPage.messages';
|
||||
|
||||
function JumpNav({ intl, displayDemographicsLink }) {
|
||||
function JumpNav({
|
||||
intl,
|
||||
displayDemographicsLink,
|
||||
}) {
|
||||
const stickToTop = useWindowSize().width > breakpoints.small.minWidth;
|
||||
return (
|
||||
<div className="jump-nav">
|
||||
<div className={classNames('jump-nav', { 'jump-nav-sm position-sticky pt-3': stickToTop })}>
|
||||
<Scrollspy
|
||||
items={[
|
||||
'basic-information',
|
||||
|
||||
@@ -8,7 +8,7 @@ export default function NotFoundPage() {
|
||||
<FormattedMessage
|
||||
id="error.notfound.message"
|
||||
defaultMessage="The page you're looking for is unavailable or there's an error in the URL. Please check the URL and try again."
|
||||
description="error message when a page does not exist"
|
||||
description="Error message when a page does not exist"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -14,12 +14,11 @@
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.jump-nav-sm {
|
||||
top: 1rem;
|
||||
}
|
||||
|
||||
.jump-nav {
|
||||
@media (min-width: map-get($grid-breakpoints, "sm")) {
|
||||
padding-top: 1rem;
|
||||
position: sticky;
|
||||
top: 1rem;
|
||||
}
|
||||
|
||||
li {
|
||||
margin-bottom: .5rem;
|
||||
@@ -30,16 +29,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.section-heading {
|
||||
@extend .h4;
|
||||
margin-bottom: map-get($spacers, 3);
|
||||
}
|
||||
|
||||
.account-section {
|
||||
// These properties together will shift the hashlink position
|
||||
margin-bottom: map-get($spacers, 5);
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.custom-switch {
|
||||
padding: 0;
|
||||
|
||||
@@ -17,7 +17,7 @@ const ManagedProfileAlert = ({ profileDataManager }) => (
|
||||
<FormattedMessage
|
||||
id="account.settings.coaching.managed.alert"
|
||||
defaultMessage="Your name is managed by {managerTitle}. Contact your administrator for help."
|
||||
description="alert message informing the user their account data is managed by a third party"
|
||||
description="Alert message informing the user their account data is managed by a third party"
|
||||
values={{
|
||||
managerTitle: <b>{profileDataManager}</b>,
|
||||
}}
|
||||
|
||||
@@ -114,7 +114,7 @@ exports[`CoachingConsent disables name field on enterprise user 1`] = `
|
||||
className="mt-3"
|
||||
>
|
||||
<a
|
||||
className="mt-3 text-dark btn-link small"
|
||||
className="pgn__hyperlink default-link standalone-link mt-3 text-dark btn-link small"
|
||||
href="http://localhost:18000/dashboard/"
|
||||
onClick={[Function]}
|
||||
target="_self"
|
||||
@@ -263,7 +263,7 @@ exports[`CoachingConsent should render 1`] = `
|
||||
className="mt-3"
|
||||
>
|
||||
<a
|
||||
className="mt-3 text-dark btn-link small"
|
||||
className="pgn__hyperlink default-link standalone-link mt-3 text-dark btn-link small"
|
||||
href="http://localhost:18000/dashboard/"
|
||||
onClick={[Function]}
|
||||
target="_self"
|
||||
|
||||
@@ -72,7 +72,7 @@ export class DeleteAccount extends React.Component {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2 className="section-heading">
|
||||
<h2 className="section-heading h4 mb-3">
|
||||
{intl.formatMessage(messages['account.settings.delete.account.header'])}
|
||||
</h2>
|
||||
<p>{intl.formatMessage(messages['account.settings.delete.account.subheader'])}</p>
|
||||
|
||||
@@ -16,21 +16,6 @@ Array [
|
||||
className=""
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
data-focus-guard={true}
|
||||
style={
|
||||
Object {
|
||||
"height": "0px",
|
||||
"left": "1px",
|
||||
"overflow": "hidden",
|
||||
"padding": 0,
|
||||
"position": "fixed",
|
||||
"top": "1px",
|
||||
"width": "1px",
|
||||
}
|
||||
}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
<div
|
||||
data-focus-guard={true}
|
||||
style={
|
||||
@@ -207,21 +192,6 @@ Array [
|
||||
}
|
||||
tabIndex={0}
|
||||
/>
|
||||
<div
|
||||
data-focus-guard={true}
|
||||
style={
|
||||
Object {
|
||||
"height": "0px",
|
||||
"left": "1px",
|
||||
"overflow": "hidden",
|
||||
"padding": 0,
|
||||
"position": "fixed",
|
||||
"top": "1px",
|
||||
"width": "1px",
|
||||
}
|
||||
}
|
||||
tabIndex={1}
|
||||
/>
|
||||
<div
|
||||
data-focus-lock-disabled={false}
|
||||
onBlur={[Function]}
|
||||
@@ -418,21 +388,6 @@ Array [
|
||||
}
|
||||
tabIndex={0}
|
||||
/>
|
||||
<div
|
||||
data-focus-guard={true}
|
||||
style={
|
||||
Object {
|
||||
"height": "0px",
|
||||
"left": "1px",
|
||||
"overflow": "hidden",
|
||||
"padding": 0,
|
||||
"position": "fixed",
|
||||
"top": "1px",
|
||||
"width": "1px",
|
||||
}
|
||||
}
|
||||
tabIndex={1}
|
||||
/>
|
||||
<div
|
||||
data-focus-lock-disabled={false}
|
||||
onBlur={[Function]}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
exports[`DeleteAccount should match default section snapshot 1`] = `
|
||||
<div>
|
||||
<h2
|
||||
className="section-heading"
|
||||
className="section-heading h4 mb-3"
|
||||
>
|
||||
Delete My Account
|
||||
</h2>
|
||||
@@ -28,7 +28,7 @@ exports[`DeleteAccount should match default section snapshot 1`] = `
|
||||
</p>
|
||||
<p>
|
||||
<a
|
||||
className="default-link standalone-link"
|
||||
className="pgn__hyperlink default-link standalone-link"
|
||||
href="https://support.edx.org/hc/en-us/sections/115004139268-Manage-Your-Account-Settings"
|
||||
onClick={[Function]}
|
||||
target="_self"
|
||||
@@ -52,7 +52,7 @@ exports[`DeleteAccount should match default section snapshot 1`] = `
|
||||
exports[`DeleteAccount should match unverified account section snapshot 1`] = `
|
||||
<div>
|
||||
<h2
|
||||
className="section-heading"
|
||||
className="section-heading h4 mb-3"
|
||||
>
|
||||
Delete My Account
|
||||
</h2>
|
||||
@@ -77,7 +77,7 @@ exports[`DeleteAccount should match unverified account section snapshot 1`] = `
|
||||
</p>
|
||||
<p>
|
||||
<a
|
||||
className="default-link standalone-link"
|
||||
className="pgn__hyperlink default-link standalone-link"
|
||||
href="https://support.edx.org/hc/en-us/sections/115004139268-Manage-Your-Account-Settings"
|
||||
onClick={[Function]}
|
||||
target="_self"
|
||||
@@ -121,7 +121,7 @@ exports[`DeleteAccount should match unverified account section snapshot 1`] = `
|
||||
<span>
|
||||
Before proceeding, please
|
||||
<a
|
||||
className="default-link standalone-link"
|
||||
className="pgn__hyperlink default-link standalone-link"
|
||||
href="https://support.edx.org/hc/en-us/articles/115000940568-How-do-I-confirm-my-email-"
|
||||
onClick={[Function]}
|
||||
target="_self"
|
||||
@@ -138,7 +138,7 @@ exports[`DeleteAccount should match unverified account section snapshot 1`] = `
|
||||
exports[`DeleteAccount should match unverified account section snapshot 2`] = `
|
||||
<div>
|
||||
<h2
|
||||
className="section-heading"
|
||||
className="section-heading h4 mb-3"
|
||||
>
|
||||
Delete My Account
|
||||
</h2>
|
||||
@@ -163,7 +163,7 @@ exports[`DeleteAccount should match unverified account section snapshot 2`] = `
|
||||
</p>
|
||||
<p>
|
||||
<a
|
||||
className="default-link standalone-link"
|
||||
className="pgn__hyperlink default-link standalone-link"
|
||||
href="https://support.edx.org/hc/en-us/sections/115004139268-Manage-Your-Account-Settings"
|
||||
onClick={[Function]}
|
||||
target="_self"
|
||||
@@ -207,7 +207,7 @@ exports[`DeleteAccount should match unverified account section snapshot 2`] = `
|
||||
<span>
|
||||
Before proceeding, please
|
||||
<a
|
||||
className="default-link standalone-link"
|
||||
className="pgn__hyperlink default-link standalone-link"
|
||||
href="https://support.edx.org/hc/en-us/articles/207206067"
|
||||
onClick={[Function]}
|
||||
target="_self"
|
||||
|
||||
@@ -16,21 +16,6 @@ Array [
|
||||
className=""
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
data-focus-guard={true}
|
||||
style={
|
||||
Object {
|
||||
"height": "0px",
|
||||
"left": "1px",
|
||||
"overflow": "hidden",
|
||||
"padding": 0,
|
||||
"position": "fixed",
|
||||
"top": "1px",
|
||||
"width": "1px",
|
||||
}
|
||||
}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
<div
|
||||
data-focus-guard={true}
|
||||
style={
|
||||
@@ -128,21 +113,6 @@ Array [
|
||||
className=""
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
data-focus-guard={true}
|
||||
style={
|
||||
Object {
|
||||
"height": "0px",
|
||||
"left": "1px",
|
||||
"overflow": "hidden",
|
||||
"padding": 0,
|
||||
"position": "fixed",
|
||||
"top": "1px",
|
||||
"width": "1px",
|
||||
}
|
||||
}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
<div
|
||||
data-focus-guard={true}
|
||||
style={
|
||||
@@ -240,21 +210,6 @@ Array [
|
||||
className=""
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
data-focus-guard={true}
|
||||
style={
|
||||
Object {
|
||||
"height": "0px",
|
||||
"left": "1px",
|
||||
"overflow": "hidden",
|
||||
"padding": 0,
|
||||
"position": "fixed",
|
||||
"top": "1px",
|
||||
"width": "1px",
|
||||
}
|
||||
}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
<div
|
||||
data-focus-guard={true}
|
||||
style={
|
||||
@@ -352,21 +307,6 @@ Array [
|
||||
className=""
|
||||
role="dialog"
|
||||
>
|
||||
<div
|
||||
data-focus-guard={true}
|
||||
style={
|
||||
Object {
|
||||
"height": "0px",
|
||||
"left": "1px",
|
||||
"overflow": "hidden",
|
||||
"padding": 0,
|
||||
"position": "fixed",
|
||||
"top": "1px",
|
||||
"width": "1px",
|
||||
}
|
||||
}
|
||||
tabIndex={-1}
|
||||
/>
|
||||
<div
|
||||
data-focus-guard={true}
|
||||
style={
|
||||
@@ -479,21 +419,6 @@ Array [
|
||||
}
|
||||
tabIndex={0}
|
||||
/>
|
||||
<div
|
||||
data-focus-guard={true}
|
||||
style={
|
||||
Object {
|
||||
"height": "0px",
|
||||
"left": "1px",
|
||||
"overflow": "hidden",
|
||||
"padding": 0,
|
||||
"position": "fixed",
|
||||
"top": "1px",
|
||||
"width": "1px",
|
||||
}
|
||||
}
|
||||
tabIndex={1}
|
||||
/>
|
||||
<div
|
||||
data-focus-lock-disabled={false}
|
||||
onBlur={[Function]}
|
||||
|
||||
@@ -162,8 +162,8 @@ class DemographicsSection extends React.Component {
|
||||
const showWorkStatusDescribe = this.props.formValues.demographics_work_status === OTHER;
|
||||
|
||||
return (
|
||||
<div className="account-section" id="demographics-information" ref={this.props.forwardRef}>
|
||||
<h2 className="section-heading">
|
||||
<div className="account-section pt-3 mb-5" id="demographics-information" ref={this.props.forwardRef}>
|
||||
<h2 className="section-heading h4 mb-3">
|
||||
{this.props.intl.formatMessage(messages['account.settings.section.demographics.information'])}
|
||||
</h2>
|
||||
<p>
|
||||
|
||||
@@ -2,27 +2,27 @@
|
||||
|
||||
exports[`DemographicsSection should render 1`] = `
|
||||
<div
|
||||
className="account-section"
|
||||
className="account-section pt-3 mb-5"
|
||||
id="demographics-information"
|
||||
>
|
||||
<h2
|
||||
className="section-heading"
|
||||
className="section-heading h4 mb-3"
|
||||
>
|
||||
Optional Information
|
||||
</h2>
|
||||
<p>
|
||||
<a
|
||||
className="default-link standalone-link"
|
||||
className="pgn__hyperlink default-link standalone-link"
|
||||
href="http://localhost:5335/demographics"
|
||||
onClick={[Function]}
|
||||
rel="noopener noopener noreferrer"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Why does localhost collect this information?
|
||||
<span
|
||||
className="d-inline-block align-text-top"
|
||||
className="pgn__hyperlink__external-icon"
|
||||
title="Opens in a new tab"
|
||||
>
|
||||
|
||||
<span
|
||||
className="pgn__icon"
|
||||
style={
|
||||
@@ -34,7 +34,6 @@ exports[`DemographicsSection should render 1`] = `
|
||||
>
|
||||
<svg
|
||||
aria-hidden={true}
|
||||
aria-label=""
|
||||
fill="none"
|
||||
focusable={false}
|
||||
height={24}
|
||||
@@ -48,6 +47,11 @@ exports[`DemographicsSection should render 1`] = `
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
className="sr-only"
|
||||
>
|
||||
in a new tab
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
@@ -644,27 +648,27 @@ exports[`DemographicsSection should render 1`] = `
|
||||
|
||||
exports[`DemographicsSection should render an Alert if an error occurs 1`] = `
|
||||
<div
|
||||
className="account-section"
|
||||
className="account-section pt-3 mb-5"
|
||||
id="demographics-information"
|
||||
>
|
||||
<h2
|
||||
className="section-heading"
|
||||
className="section-heading h4 mb-3"
|
||||
>
|
||||
Optional Information
|
||||
</h2>
|
||||
<p>
|
||||
<a
|
||||
className="default-link standalone-link"
|
||||
className="pgn__hyperlink default-link standalone-link"
|
||||
href="http://localhost:5335/demographics"
|
||||
onClick={[Function]}
|
||||
rel="noopener noopener noreferrer"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Why does localhost collect this information?
|
||||
<span
|
||||
className="d-inline-block align-text-top"
|
||||
className="pgn__hyperlink__external-icon"
|
||||
title="Opens in a new tab"
|
||||
>
|
||||
|
||||
<span
|
||||
className="pgn__icon"
|
||||
style={
|
||||
@@ -676,7 +680,6 @@ exports[`DemographicsSection should render an Alert if an error occurs 1`] = `
|
||||
>
|
||||
<svg
|
||||
aria-hidden={true}
|
||||
aria-label=""
|
||||
fill="none"
|
||||
focusable={false}
|
||||
height={24}
|
||||
@@ -690,6 +693,11 @@ exports[`DemographicsSection should render an Alert if an error occurs 1`] = `
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
className="sr-only"
|
||||
>
|
||||
in a new tab
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
@@ -1300,27 +1308,27 @@ exports[`DemographicsSection should render an Alert if an error occurs 1`] = `
|
||||
|
||||
exports[`DemographicsSection should render an Alert when demographicsOptions props are empty 1`] = `
|
||||
<div
|
||||
className="account-section"
|
||||
className="account-section pt-3 mb-5"
|
||||
id="demographics-information"
|
||||
>
|
||||
<h2
|
||||
className="section-heading"
|
||||
className="section-heading h4 mb-3"
|
||||
>
|
||||
Optional Information
|
||||
</h2>
|
||||
<p>
|
||||
<a
|
||||
className="default-link standalone-link"
|
||||
className="pgn__hyperlink default-link standalone-link"
|
||||
href="http://localhost:5335/demographics"
|
||||
onClick={[Function]}
|
||||
rel="noopener noopener noreferrer"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Why does localhost collect this information?
|
||||
<span
|
||||
className="d-inline-block align-text-top"
|
||||
className="pgn__hyperlink__external-icon"
|
||||
title="Opens in a new tab"
|
||||
>
|
||||
|
||||
<span
|
||||
className="pgn__icon"
|
||||
style={
|
||||
@@ -1332,7 +1340,6 @@ exports[`DemographicsSection should render an Alert when demographicsOptions pro
|
||||
>
|
||||
<svg
|
||||
aria-hidden={true}
|
||||
aria-label=""
|
||||
fill="none"
|
||||
focusable={false}
|
||||
height={24}
|
||||
@@ -1346,6 +1353,11 @@ exports[`DemographicsSection should render an Alert when demographicsOptions pro
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
className="sr-only"
|
||||
>
|
||||
in a new tab
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
@@ -1369,27 +1381,27 @@ exports[`DemographicsSection should render an Alert when demographicsOptions pro
|
||||
|
||||
exports[`DemographicsSection should render ethnicity correctly when multiple options are selected 1`] = `
|
||||
<div
|
||||
className="account-section"
|
||||
className="account-section pt-3 mb-5"
|
||||
id="demographics-information"
|
||||
>
|
||||
<h2
|
||||
className="section-heading"
|
||||
className="section-heading h4 mb-3"
|
||||
>
|
||||
Optional Information
|
||||
</h2>
|
||||
<p>
|
||||
<a
|
||||
className="default-link standalone-link"
|
||||
className="pgn__hyperlink default-link standalone-link"
|
||||
href="http://localhost:5335/demographics"
|
||||
onClick={[Function]}
|
||||
rel="noopener noopener noreferrer"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Why does localhost collect this information?
|
||||
<span
|
||||
className="d-inline-block align-text-top"
|
||||
className="pgn__hyperlink__external-icon"
|
||||
title="Opens in a new tab"
|
||||
>
|
||||
|
||||
<span
|
||||
className="pgn__icon"
|
||||
style={
|
||||
@@ -1401,7 +1413,6 @@ exports[`DemographicsSection should render ethnicity correctly when multiple opt
|
||||
>
|
||||
<svg
|
||||
aria-hidden={true}
|
||||
aria-label=""
|
||||
fill="none"
|
||||
focusable={false}
|
||||
height={24}
|
||||
@@ -1415,6 +1426,11 @@ exports[`DemographicsSection should render ethnicity correctly when multiple opt
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
className="sr-only"
|
||||
>
|
||||
in a new tab
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
@@ -2004,27 +2020,27 @@ exports[`DemographicsSection should render ethnicity correctly when multiple opt
|
||||
|
||||
exports[`DemographicsSection should render ethnicity text correctly 1`] = `
|
||||
<div
|
||||
className="account-section"
|
||||
className="account-section pt-3 mb-5"
|
||||
id="demographics-information"
|
||||
>
|
||||
<h2
|
||||
className="section-heading"
|
||||
className="section-heading h4 mb-3"
|
||||
>
|
||||
Optional Information
|
||||
</h2>
|
||||
<p>
|
||||
<a
|
||||
className="default-link standalone-link"
|
||||
className="pgn__hyperlink default-link standalone-link"
|
||||
href="http://localhost:5335/demographics"
|
||||
onClick={[Function]}
|
||||
rel="noopener noopener noreferrer"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Why does localhost collect this information?
|
||||
<span
|
||||
className="d-inline-block align-text-top"
|
||||
className="pgn__hyperlink__external-icon"
|
||||
title="Opens in a new tab"
|
||||
>
|
||||
|
||||
<span
|
||||
className="pgn__icon"
|
||||
style={
|
||||
@@ -2036,7 +2052,6 @@ exports[`DemographicsSection should render ethnicity text correctly 1`] = `
|
||||
>
|
||||
<svg
|
||||
aria-hidden={true}
|
||||
aria-label=""
|
||||
fill="none"
|
||||
focusable={false}
|
||||
height={24}
|
||||
@@ -2050,6 +2065,11 @@ exports[`DemographicsSection should render ethnicity text correctly 1`] = `
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
className="sr-only"
|
||||
>
|
||||
in a new tab
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
@@ -2639,27 +2659,27 @@ exports[`DemographicsSection should render ethnicity text correctly 1`] = `
|
||||
|
||||
exports[`DemographicsSection should set user input correctly when user provides answers to work_status question 1`] = `
|
||||
<div
|
||||
className="account-section"
|
||||
className="account-section pt-3 mb-5"
|
||||
id="demographics-information"
|
||||
>
|
||||
<h2
|
||||
className="section-heading"
|
||||
className="section-heading h4 mb-3"
|
||||
>
|
||||
Optional Information
|
||||
</h2>
|
||||
<p>
|
||||
<a
|
||||
className="default-link standalone-link"
|
||||
className="pgn__hyperlink default-link standalone-link"
|
||||
href="http://localhost:5335/demographics"
|
||||
onClick={[Function]}
|
||||
rel="noopener noopener noreferrer"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Why does localhost collect this information?
|
||||
<span
|
||||
className="d-inline-block align-text-top"
|
||||
className="pgn__hyperlink__external-icon"
|
||||
title="Opens in a new tab"
|
||||
>
|
||||
|
||||
<span
|
||||
className="pgn__icon"
|
||||
style={
|
||||
@@ -2671,7 +2691,6 @@ exports[`DemographicsSection should set user input correctly when user provides
|
||||
>
|
||||
<svg
|
||||
aria-hidden={true}
|
||||
aria-label=""
|
||||
fill="none"
|
||||
focusable={false}
|
||||
height={24}
|
||||
@@ -2685,6 +2704,11 @@ exports[`DemographicsSection should set user input correctly when user provides
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
className="sr-only"
|
||||
>
|
||||
in a new tab
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
@@ -3281,27 +3305,27 @@ exports[`DemographicsSection should set user input correctly when user provides
|
||||
|
||||
exports[`DemographicsSection should set user input correctly when user provides gender self-description 1`] = `
|
||||
<div
|
||||
className="account-section"
|
||||
className="account-section pt-3 mb-5"
|
||||
id="demographics-information"
|
||||
>
|
||||
<h2
|
||||
className="section-heading"
|
||||
className="section-heading h4 mb-3"
|
||||
>
|
||||
Optional Information
|
||||
</h2>
|
||||
<p>
|
||||
<a
|
||||
className="default-link standalone-link"
|
||||
className="pgn__hyperlink default-link standalone-link"
|
||||
href="http://localhost:5335/demographics"
|
||||
onClick={[Function]}
|
||||
rel="noopener noopener noreferrer"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
Why does localhost collect this information?
|
||||
<span
|
||||
className="d-inline-block align-text-top"
|
||||
className="pgn__hyperlink__external-icon"
|
||||
title="Opens in a new tab"
|
||||
>
|
||||
|
||||
<span
|
||||
className="pgn__icon"
|
||||
style={
|
||||
@@ -3313,7 +3337,6 @@ exports[`DemographicsSection should set user input correctly when user provides
|
||||
>
|
||||
<svg
|
||||
aria-hidden={true}
|
||||
aria-label=""
|
||||
fill="none"
|
||||
focusable={false}
|
||||
height={24}
|
||||
@@ -3327,6 +3350,11 @@ exports[`DemographicsSection should set user input correctly when user provides
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
className="sr-only"
|
||||
>
|
||||
in a new tab
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
@@ -23,12 +23,12 @@ const messages = defineMessages({
|
||||
},
|
||||
'account.settings.name.change.id.name.label': {
|
||||
id: 'account.settings.name.change.id.name.label',
|
||||
defaultMessage: 'Enter your name as it appears on your government-issued ID.',
|
||||
defaultMessage: 'Enter your name as it appears on your unexpired student, work, or government-issued identification card.',
|
||||
description: 'Form label instructing the user to enter the name on their ID.',
|
||||
},
|
||||
'account.settings.name.change.id.name.placeholder': {
|
||||
id: 'account.settings.name.change.id.name.placeholder',
|
||||
defaultMessage: 'Enter the name on your government ID',
|
||||
defaultMessage: 'Enter the name on your photo ID',
|
||||
description: 'Form label instructing the user to enter the name on their ID.',
|
||||
},
|
||||
'account.settings.name.change.error.valid.name': {
|
||||
|
||||
@@ -70,7 +70,7 @@ describe('NameChange', () => {
|
||||
afterEach(() => jest.clearAllMocks());
|
||||
|
||||
it('renders populated input after clicking continue if verified_name in form data', async () => {
|
||||
const getInput = () => screen.queryByPlaceholderText('Enter the name on your government ID');
|
||||
const getInput = () => screen.queryByPlaceholderText('Enter the name on your photo ID');
|
||||
|
||||
render(reduxWrapper(<IntlNameChange {...props} />));
|
||||
expect(getInput()).toBeNull();
|
||||
@@ -82,7 +82,7 @@ describe('NameChange', () => {
|
||||
});
|
||||
|
||||
it('renders empty input after clicking continue if verified_name not in form data', async () => {
|
||||
const getInput = () => screen.queryByPlaceholderText('Enter the name on your government ID');
|
||||
const getInput = () => screen.queryByPlaceholderText('Enter the name on your photo ID');
|
||||
const formProps = {
|
||||
...props,
|
||||
formValues: {
|
||||
@@ -112,7 +112,7 @@ describe('NameChange', () => {
|
||||
const continueButton = screen.getByText('Continue');
|
||||
fireEvent.click(continueButton);
|
||||
|
||||
const input = screen.getByPlaceholderText('Enter the name on your government ID');
|
||||
const input = screen.getByPlaceholderText('Enter the name on your photo ID');
|
||||
fireEvent.change(input, { target: { value: 'Verified Name' } });
|
||||
|
||||
const submitButton = screen.getByText('Continue');
|
||||
@@ -139,7 +139,7 @@ describe('NameChange', () => {
|
||||
const continueButton = screen.getByText('Continue');
|
||||
fireEvent.click(continueButton);
|
||||
|
||||
const input = screen.getByPlaceholderText('Enter the name on your government ID');
|
||||
const input = screen.getByPlaceholderText('Enter the name on your photo ID');
|
||||
fireEvent.change(input, { target: { value: 'Verified Name' } });
|
||||
|
||||
const submitButton = screen.getByText('Continue');
|
||||
@@ -155,7 +155,7 @@ describe('NameChange', () => {
|
||||
const continueButton = screen.getByText('Continue');
|
||||
fireEvent.click(continueButton);
|
||||
|
||||
const input = screen.getByPlaceholderText('Enter the name on your government ID');
|
||||
const input = screen.getByPlaceholderText('Enter the name on your photo ID');
|
||||
fireEvent.change(input, { target: { value: 'Verified Name' } });
|
||||
|
||||
const submitButton = screen.getByText('Continue');
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
exports[`JumpNav should not render Optional Information link 1`] = `
|
||||
<div
|
||||
className="jump-nav"
|
||||
className="jump-nav jump-nav-sm position-sticky pt-3"
|
||||
>
|
||||
<ul
|
||||
className="list-unstyled"
|
||||
@@ -92,7 +92,7 @@ exports[`JumpNav should not render Optional Information link 1`] = `
|
||||
|
||||
exports[`JumpNav should render Optional Information link 1`] = `
|
||||
<div
|
||||
className="jump-nav"
|
||||
className="jump-nav jump-nav-sm position-sticky pt-3"
|
||||
>
|
||||
<ul
|
||||
className="list-unstyled"
|
||||
|
||||
@@ -20,16 +20,22 @@
|
||||
"account.settings.field.full.name": "الاسم الكامل",
|
||||
"account.settings.field.full.name.empty": "إضافة اسم",
|
||||
"account.settings.field.full.name.help.text": "الاسم المستخدم للتحقق من هويتك والذي سوف يظهر على الشهادات الخاصة بك.",
|
||||
"account.settings.field.full.name.help.text.non.certificate": "The name that appears on your public profile.",
|
||||
"account.settings.field.full.name.help.text.certificate": "This name is selected to appear on your certificates and public-facing records.",
|
||||
"account.settings.field.full.name.help.text.default": "The name that appears on your public profile.",
|
||||
"account.settings.field.full.name.help.text.default.certificate": "This name is selected to appear on your certificates and public-facing records.",
|
||||
"account.settings.field.name.verified": "Verified name",
|
||||
"account.settings.field.name.verified.help.text.verified": "This name has been verified by government ID.",
|
||||
"account.settings.field.name.verified.help.text.certificate": "This name has been verified by government ID and selected to appear on your certificates and public-facing records.",
|
||||
"account.settings.field.name.verified.help.text.verified": "This name has been verified by photo ID.",
|
||||
"account.settings.field.name.verified.help.text.verified.proctored": "This name has been verified by proctoring.",
|
||||
"account.settings.field.name.verified.help.text.verified.certificate": "This name has been verified by photo ID, and is selected to appear on your certificates and public-facing records.",
|
||||
"account.settings.field.name.verified.help.text.verified.proctored.certificate": "This name has been verified by proctoring, and is selected to appear on your certificates and public-facing records.",
|
||||
"account.settings.field.name.verified.help.text.submitted": "Verification has been submitted. This usually takes 48 hours or less. Verified name cannot be changed at this time.",
|
||||
"account.settings.field.name.verified.help.text.submitted.proctored": "Your proctored exam has been submitted. Verified name cannot be changed at this time. Please check back in 2-5 days.",
|
||||
"account.settings.field.name.verified.help.text.submitted.certificate": "When identity verification is successful, this name will appear on your certificates and public-facing records. Verified name cannot be changed at this time.",
|
||||
"account.settings.field.name.verified.verification.help": "Enter your name as it appears on your government-issued ID.",
|
||||
"account.settings.field.name.verified.help.text.submitted.proctored.certificate": "Once your proctored exam passes review, this name will appear on your certificate and public-facing records. Verified Name cannot be changed at this time.",
|
||||
"account.settings.field.name.verified.verification.help": "Enter your name as it appears on your unexpired student, work, or government-issued identification card.",
|
||||
"account.settings.field.full.name.help.text.submitted": "Verification has been submitted. This usually takes 48 hours or less. Full name cannot be changed at this time.",
|
||||
"account.settings.field.full.name.help.text.submitted.proctored": "Your proctored exam has been submitted. Full name cannot be changed at this time. Please check back in 2-5 days.",
|
||||
"account.settings.field.full.name.help.text.submitted.certificate": "When identity verification is successful, this name will appear on your certificates and public-facing records. Full name cannot be changed at this time.",
|
||||
"account.settings.field.full.name.help.text.submitted.proctored.certificate": "Once your proctored exam passes review, this name will appear on your certificates and public-facing records. Full name cannot be changed at this time.",
|
||||
"account.settings.field.name.verified.success.message": "Your identity verification request has successfully completed. You now have the option of selecting which name you prefer to appear on your certificates and public-records.",
|
||||
"account.settings.field.name.verified.success.message.header": "Your name change request is complete!",
|
||||
"account.settings.field.name.verified.failure.message": "Your most recent identity verification attempt did not pass. Related account settings have been restored.",
|
||||
@@ -186,8 +192,8 @@
|
||||
"account.settings.name.change.title.begin": "Before we begin",
|
||||
"account.settings.name.change.warning.one": "Warning: This action updates the name that appears on all certificates that have been earned on this account in the past and any certificates you are currently earning or will earn in the future.",
|
||||
"account.settings.name.change.warning.two": "This action cannot be undone without verifying your identity.",
|
||||
"account.settings.name.change.id.name.label": "Enter your name as it appears on your government-issued ID.",
|
||||
"account.settings.name.change.id.name.placeholder": "Enter the name on your government ID",
|
||||
"account.settings.name.change.id.name.label": "Enter your name as it appears on your unexpired student, work, or government-issued identification card.",
|
||||
"account.settings.name.change.id.name.placeholder": "Enter the name on your photo ID",
|
||||
"account.settings.name.change.error.valid.name": "Please enter a valid name.",
|
||||
"account.settings.name.change.error.general": "A technical error occurred. Please try again.",
|
||||
"account.settings.name.change.continue": "Continue",
|
||||
@@ -206,7 +212,6 @@
|
||||
"id.verification.access.blocked.denied": "We cannot verify your identity at this time. If you have yet to activate your account, please check your spam folder for the activation email from {email}.",
|
||||
"id.verification.next": "التالي",
|
||||
"id.verification.support": "support",
|
||||
"id.verification.continue.upload": "Continue with Upload",
|
||||
"id.verification.example.card.alt": "مثال على بطاقة هوية صحيحة بالاسم الكامل وصورة.",
|
||||
"id.verification.requirements.title": "متطلبات التحقق من الصورة",
|
||||
"id.verification.requirements.description": "In order to complete Photo Verification, you will need the following:",
|
||||
@@ -284,18 +289,13 @@
|
||||
"id.verification.photo.tips.list.well.lit": "أنّ الإضاءة جيّدة على وجهك.",
|
||||
"id.verification.photo.tips.list.inside.frame": "أنّ وجهك داخل إطار الصورة بالكامل.",
|
||||
"id.verification.portrait.photo.title.camera": "التقط صورة لنفسك",
|
||||
"id.verification.portrait.photo.title.upload": "ارفع صورتك",
|
||||
"id.verification.portrait.photo.preview.alt": "معاينة صورة وجه المستخدم.",
|
||||
"id.verification.portrait.photo.instructions.camera": "عندما يكون وجهك في موضعه، استخدم زر التقاط صورة أدناه لالتقاط الصورة.",
|
||||
"id.verification.portrait.photo.instructions.upload": "Please upload a portrait photo. Ensure your entire face fits inside the frame and is well-lit. Supported formats: ",
|
||||
"id.verification.camera.help.sight.question": "ماذا لو لم أتمكن من رؤية صورة الكاميرا ؟ أو إذا لم أتمكن من رؤية صورتي لتحديد أي جانب مرئي؟",
|
||||
"id.verification.camera.help.sight.answer.portrait": "قد تتمكن من إكمال إجراء التقاط الصور من دون مساعدة، ولكن قد يتطلب الأمر بضع محاولات ضبط وضع الكاميرا بشكل صحيح. يختلف وضع الكاميرا المثالي باختلاف الكمبيوتر، ولكن بشكل عام، يكون أفضل موضع للتصوير في الرأس هو 12 إلى 18 بوصة (30-45 سم) تقريبًا من الكاميرا، مع وضع الرأس في المنتصف بالنسبة إلى شاشة الكمبيوتر. إذا تم رفض الصور التي ترسلها، فحاول تحريك اتجاه الكمبيوتر أو الكاميرا لتغيير زاوية الإضاءة.",
|
||||
"id.verification.camera.help.sight.answer.id": "قد تتمكن من إكمال إجراء التقاط الصور من دون مساعدة، ولكن قد يتطلب الأمر بضع محاولات لضبط وضع الكاميرا بشكل صحيح. يختلف الوضع الأمثل للكاميرا باختلاف جهاز الكمبيوتر، ولكن بشكل عام، يكون أفضل وضع لصورة بطاقة تعريف من 8 إلى 12 بوصة (من 20 إلى 30 سم) عن الكاميرا، مع وضع بطاقة الهوية في الوسط بالنسبة للكاميرا. إذا تم رفض الصور التي ترسلها، فحاول تحريك اتجاه الكمبيوتر أو الكاميرا لتغيير زاوية الإضاءة. إن السبب الأكثر شيوعاً للرفض هو عدم القدرة على قراءة النص الموجود على بطاقة الهوية.",
|
||||
"id.verification.camera.help.difficulty.question.portrait": "ماذا لو واجهت صعوبة في تثبيت رأسي في الموضع المناسب للكاميرا؟",
|
||||
"id.verification.camera.help.difficulty.question.id": "ماذا لو واجهت صعوبة في تثبيت بطاقة هويتي في الموضع المناسب للكاميرا؟",
|
||||
"id.verification.camera.help.difficulty.answer": "If you require assistance with taking a photo for submission, contact {siteName} support for additional suggestions.",
|
||||
"id.verification.camera.help.upload.question": "What if I want to upload a photo instead?",
|
||||
"id.verification.camera.help.upload.answer": "On the next page you will have the option to switch to upload mode. By selecting that option, you will be able to upload a photo instead.",
|
||||
"id.verification.id.photo.unclear.question": "Is your ID card image not clear or too blurry?",
|
||||
"id.verification.id.tips.title": "Helpful Identification Card Tips",
|
||||
"id.verification.id.tips.description": "Next, we'll need you to take a photo of a valid identification card that includes your full name and photo, such as a driver’s license or passport. Please have your ID ready.",
|
||||
@@ -309,9 +309,9 @@
|
||||
"id.verification.id.photo.instructions.upload.error.invalidFileType": "The file you have selected is not a supported image type. Please choose from the following formats: ",
|
||||
"id.verification.id.photo.instructions.upload.error.fileTooLarge": "The file you have selected is too large. Please try again with a file less than 10MB.",
|
||||
"id.verification.name.check.title": "Double-Check Your Name",
|
||||
"id.verification.name.check.instructions": "Does the name below match the name on your government-issued ID? If not, update the name below to match your goverment-issued ID.",
|
||||
"id.verification.name.check.mismatch.information": "If the name below does not match your government-issued ID, your identity verification will be denied.",
|
||||
"id.verification.name.error": "Please enter your name as it appears on your government-issued ID.",
|
||||
"id.verification.name.check.instructions": "Does the name below match the name on your photo ID? If not, update the name below to match your photo ID.",
|
||||
"id.verification.name.check.mismatch.information": "If the name below does not match your photo ID, your identity verification will be denied.",
|
||||
"id.verification.name.error": "Please enter your name as it appears on your photo ID.",
|
||||
"id.verification.account.name.warning.prefix": "يُرجى الملاحظة:",
|
||||
"id.verification.account.name.settings": "إعدادات الحساب",
|
||||
"id.verification.name.label": "Name",
|
||||
@@ -331,7 +331,7 @@
|
||||
"id.verification.submission.alert.error.unsupported": "One or more of the files you have uploaded is in an unsupported format. Please choose from the following: ",
|
||||
"id.verification.review.error": "{siteName} Support Page",
|
||||
"id.verification.submitted.title": "جارِ التحقق من الهوية",
|
||||
"id.verification.submitted.text": "لقد تلقينا معلوماتك وجاري الآن العمل على التحقق من هويتك. ستصلك رسالة على لوحة المعلومات عند اكتمال عملية التحقق (عادةً خلال 5 أيام). في غضون ذلك، لا يزال بإمكانك الوصول إلى كل محتوى المساق المتوفر.",
|
||||
"id.verification.submitted.text": "We have received your information and are verifying your identity. You will be notified when the verification process is complete (usually within 5 days). In the meantime, you can still access all available course content.",
|
||||
"id.verification.return.dashboard": "العودة إلى لوحة المعلومات",
|
||||
"id.verification.return.course": "العودة للمساق",
|
||||
"id.verification.return.generic": "Return",
|
||||
@@ -341,10 +341,6 @@
|
||||
"id.verification.photo.camera.help.text": "If you are having trouble uploading a photo above, you may want to use your camera instead. To use your camera, click the button below.",
|
||||
"id.verification.upload.help.button": "Switch to Upload Mode",
|
||||
"id.verification.camera.help.button": "Switch to Camera Mode",
|
||||
"id.verification.choose.mode.title": "Photo Requirements Options",
|
||||
"id.verification.choose.mode.hep.text": "To complete verification, please select one of the following options to submit photos. You will be able to switch between these options throughout the process if needed.",
|
||||
"id.verification.choose.mode.radio.upload": "Upload photos from my device",
|
||||
"id.verification.choose.mode.radio.camera": "Take pictures using my camera",
|
||||
"id.verification.request.camera.access.instructions": "لالتقاط صورة باستخدام كاميرا الويب، قد تتلقى طلب المتصفح للوصول إلى الكاميرا. {clickAllow}",
|
||||
"id.verification.requirements.account.managed.alert": "Your account settings are managed by {managerTitle}. If the name on your photo ID does not match the name on your account, please contact your {profileDataManager} administrator or {support} for help before completing the Photo Verification process.",
|
||||
"id.verification.requirements.card.device.text": "أنت بحاجة إلى جهاز مزود بكاميرا. إذا تلقيت طلب المتصفح للوصول إلى الكاميرا، فيرجى التأكد من النقر فوق {السماح}.",
|
||||
|
||||
@@ -20,16 +20,22 @@
|
||||
"account.settings.field.full.name": "Nombre completo",
|
||||
"account.settings.field.full.name.empty": "Añade nombre",
|
||||
"account.settings.field.full.name.help.text": "El nombre que es usado para la verificación de identidad y aparece en sus certificados.",
|
||||
"account.settings.field.full.name.help.text.non.certificate": "El nombre que aparece en tu perfil público.",
|
||||
"account.settings.field.full.name.help.text.certificate": "Este nombre está seleccionado para aparecer en tus certificados y registros públicos.",
|
||||
"account.settings.field.full.name.help.text.default": "El nombre que aparece en tu perfil público.",
|
||||
"account.settings.field.full.name.help.text.default.certificate": "Este nombre está seleccionado para aparecer en tus certificados y registros públicos.",
|
||||
"account.settings.field.name.verified": "Nombre verificado",
|
||||
"account.settings.field.name.verified.help.text.verified": "Este nombre ha sido verificado por un ID gubernamental.",
|
||||
"account.settings.field.name.verified.help.text.certificate": "Este nombre ha sido verificado por un ID gubernamental y seleccionado para aparecer en tus certificados y registros públicos.",
|
||||
"account.settings.field.name.verified.help.text.verified": "Este nombre ha sido verificado por una identificación con foto.",
|
||||
"account.settings.field.name.verified.help.text.verified.proctored": "Este nombre ha sido verificado por supervisión.",
|
||||
"account.settings.field.name.verified.help.text.verified.certificate": "Este nombre ha sido verificado por una identificación con foto y está seleccionado para aparecer en sus certificados y registros públicos.",
|
||||
"account.settings.field.name.verified.help.text.verified.proctored.certificate": "Este nombre se ha verificado mediante supervisión y se ha seleccionado para que aparezca en sus certificados y registros públicos.",
|
||||
"account.settings.field.name.verified.help.text.submitted": "La verificación ha sido enviada. Este proceso usualmente toma 48 horas o menos. El nombre verificado no puede ser modificado en este momento. ",
|
||||
"account.settings.field.name.verified.help.text.submitted.proctored": "Su examen supervisado ha sido enviado. El nombre verificado no se puede cambiar en este momento. Vuelva a consultar en 2-5 días.",
|
||||
"account.settings.field.name.verified.help.text.submitted.certificate": "Cuando el proceso de verificación de identidad sea exitoso, este nombre aparecerá en tus certificados y registros públicos. El nombre verificado no puede ser cambiado en este momento. ",
|
||||
"account.settings.field.name.verified.verification.help": "Ingresa tu nombre tal como aparece en tu ID. ",
|
||||
"account.settings.field.name.verified.help.text.submitted.proctored.certificate": "Una vez que su examen supervisado pase la revisión, este nombre aparecerá en su certificado y en los registros públicos. El nombre verificado no se puede cambiar en este momento.",
|
||||
"account.settings.field.name.verified.verification.help": "Ingrese su nombre tal como aparece en su tarjeta de identificación vigente de estudiante, trabajo o emitida por el gobierno.",
|
||||
"account.settings.field.full.name.help.text.submitted": "La verificación ha sido enviada. Este proceso usualmente toma 48 horas o menos. El nombre verificado no puede ser modificado en este momento. ",
|
||||
"account.settings.field.full.name.help.text.submitted.proctored": "Su examen supervisado ha sido enviado. El nombre completo no se puede cambiar en este momento. Vuelva a consultar en 2-5 días.",
|
||||
"account.settings.field.full.name.help.text.submitted.certificate": "En cuanto el proceso de verificación de identidad sea exitoso, este nombre aparecerá en tus certificados y registros públicos. El nombre completo no puede ser cambiado en este momento. ",
|
||||
"account.settings.field.full.name.help.text.submitted.proctored.certificate": "Una vez que su examen supervisado pase la revisión, este nombre aparecerá en sus certificados y registros públicos. El nombre completo no se puede cambiar en este momento.",
|
||||
"account.settings.field.name.verified.success.message": "Tu solicitud de verificación de identidad se ha completado exitosamente. Ahora puedes seleccionar el nombre prefieres que aparezca en tus certificados y registros públicos.",
|
||||
"account.settings.field.name.verified.success.message.header": "¡Tu solicitud de cambio de nombre está completada!",
|
||||
"account.settings.field.name.verified.failure.message": "Tu más reciente intento de verificación de ID no fue aprobado. Las configuraciones relacionadas con el proceso se han restablecido.",
|
||||
@@ -186,8 +192,8 @@
|
||||
"account.settings.name.change.title.begin": "Antes de empezar",
|
||||
"account.settings.name.change.warning.one": "Atención: esta acción actualizará el nombre que aparece en todos los certificados obtenidos a través de esta cuenta en el pasado y en aquellos en que actualmente estás obteniendo o que adquirirás en el futuro.",
|
||||
"account.settings.name.change.warning.two": "Esta acción no podrá revertirse sin antes verificar tu identidad.",
|
||||
"account.settings.name.change.id.name.label": "Ingresa tu nombre tal como aparece en tu documento de identificación. ",
|
||||
"account.settings.name.change.id.name.placeholder": "Ingresa el nombre en tu ID ",
|
||||
"account.settings.name.change.id.name.label": "Ingrese su nombre tal como aparece en su tarjeta de identificación vigente de estudiante, trabajo o emitida por el gobierno.",
|
||||
"account.settings.name.change.id.name.placeholder": "Enter the name on your photo ID",
|
||||
"account.settings.name.change.error.valid.name": "Por favor ingresa un nombre valido.",
|
||||
"account.settings.name.change.error.general": "Ha ocurrido un error técnico. Por favor intentalo de nuevo.",
|
||||
"account.settings.name.change.continue": "Continuar",
|
||||
@@ -206,7 +212,6 @@
|
||||
"id.verification.access.blocked.denied": "No podemos verificar tu identidad en este momento. Si aún no has activado tu cuenta, comprueba en tu carpeta de correo no deseado el correo de activación de {email}.",
|
||||
"id.verification.next": "Siguiente",
|
||||
"id.verification.support": "soporte",
|
||||
"id.verification.continue.upload": "Continúa con la carga de archivos",
|
||||
"id.verification.example.card.alt": "Ejemplo de un documento de identidad válido con foto y nombre completo.",
|
||||
"id.verification.requirements.title": "Requerimientos de verificación por foto",
|
||||
"id.verification.requirements.description": "Para completa la verificación de foto, necesitarás lo siguiente:",
|
||||
@@ -284,18 +289,13 @@
|
||||
"id.verification.photo.tips.list.well.lit": "El rostro esté bien iluminado",
|
||||
"id.verification.photo.tips.list.inside.frame": "Tu cara está completamente dentro del marco de la foto.",
|
||||
"id.verification.portrait.photo.title.camera": "Toma una foto de ti mismo",
|
||||
"id.verification.portrait.photo.title.upload": "Sube una foto tuya",
|
||||
"id.verification.portrait.photo.preview.alt": "Previsualización de la foto con el rostro del usuario",
|
||||
"id.verification.portrait.photo.instructions.camera": "Cuando tu rostro esté en posición, usa el botón Tomar foto a continuación para tomar tu foto.",
|
||||
"id.verification.portrait.photo.instructions.upload": "Por favor sube una foto de retrato. Asegurate que toda tu cara encaje dentro del marco y que este bien iluminada. Los formatos admitidos son:",
|
||||
"id.verification.camera.help.sight.question": "¿Qué pasa si no puedo ver la imagen de la cámara o si no puedo ver mi foto para determinar qué lado es visible?",
|
||||
"id.verification.camera.help.sight.answer.portrait": "Es posible que puedas completar el procedimiento de captura de imágenes sin ayuda, pero es posible que necesites un par de intentos de envío para que la cámara se coloque correctamente. La posición óptima de la cámara varía con cada computadora, pero generalmente la mejor distancia para una foto de rostro es aproximadamente a 12 a 18 pulgadas (30 a 45 centímetros) de la cámara, con la cabeza centrada en relación con la pantalla de la computadora. Si las fotos que envías son rechazadas, intenta mover la computadora o la orientación de la cámara para cambiar el ángulo de iluminación.",
|
||||
"id.verification.camera.help.sight.answer.id": "Es posible que puedas completar el procedimiento de captura de imágenes sin ayuda, pero es posible que necesites un par de intentos de envío para que la cámara se coloque correctamente. El posicionamiento óptimo de la cámara varía con cada computadora pero, generalmente, la mejor distancia para una foto de un documento de identificación es a 8 a 12 pulgadas (20 a 30 centímetros) de la cámara, con el documento de identificación centrado en relación con la cámara. Si las fotos que envías son rechazadas, intenta mover la computadora o la orientación de la cámara para cambiar el ángulo de iluminación. La razón más común de rechazo es la imposibilidad de leer el texto del documento de identidad.",
|
||||
"id.verification.camera.help.difficulty.question.portrait": "¿Qué sucede si tengo dificultades para mantener la cabeza en posición con respecto a la cámara?",
|
||||
"id.verification.camera.help.difficulty.question.id": "¿Qué sucede si tengo dificultades para mantener mi identificación en posición con respecto a la cámara?",
|
||||
"id.verification.camera.help.difficulty.answer": "Si requieres de asistencia para cargar una foto, contacta el equipo de soporte de {siteName} para sugerencias adicionales.",
|
||||
"id.verification.camera.help.upload.question": "¿Qué pasa si en vez quiero subir una foto?",
|
||||
"id.verification.camera.help.upload.answer": "En la siguiente página tendrás la opción de cambiar a modo carga. Al seleccionar esa opción, podrás subir una foto en vez.",
|
||||
"id.verification.id.photo.unclear.question": "¿No es clara la foto de tu tarjeta de ID ó es muy borrosa?",
|
||||
"id.verification.id.tips.title": "Tips de ayuda para tarjetas de identificación",
|
||||
"id.verification.id.tips.description": "Después, necesitaremos que tomes una foto de una tarjeta de ID válida que tengas en la que incluya tu nombre completo y foto, como una licencia de conducción o un pasaporte. Por favor ten tú ID lista.",
|
||||
@@ -309,9 +309,9 @@
|
||||
"id.verification.id.photo.instructions.upload.error.invalidFileType": "El archivo que has seleccionado no cumple con el tipo de imágenes soportadas. Por favor escoge entre los siguientes formatos:",
|
||||
"id.verification.id.photo.instructions.upload.error.fileTooLarge": "El archivo que has seleccionado es demasiado grande. Vuelve a intentarlo con un archivo de menos de 10 MB.",
|
||||
"id.verification.name.check.title": "Revisa nuevamente tu nombre",
|
||||
"id.verification.name.check.instructions": "¿El nombre a continuación coincide con el nombre de tú ID? De no ser así, actualiza el nombre que se muestra a continuación para que coincida con tú ID.",
|
||||
"id.verification.name.check.mismatch.information": "En caso de que el nombre a continuación no coincida con el que aparece en tú ID, tu verificación de identidad será negada.",
|
||||
"id.verification.name.error": "Introduce tu nombre tal y como aparece en tu ID.",
|
||||
"id.verification.name.check.instructions": "Does the name below match the name on your photo ID? If not, update the name below to match your photo ID.",
|
||||
"id.verification.name.check.mismatch.information": "Si el nombre a continuación no coincide con su identificación con foto, se denegará su verificación de identidad.",
|
||||
"id.verification.name.error": "Ingrese su nombre tal como aparece en su identificación con foto.",
|
||||
"id.verification.account.name.warning.prefix": "Ten en cuenta:",
|
||||
"id.verification.account.name.settings": "Configuración de cuenta",
|
||||
"id.verification.name.label": "Nombre",
|
||||
@@ -331,7 +331,7 @@
|
||||
"id.verification.submission.alert.error.unsupported": "Uno o más de los siguientes archivos que has subido se encuentran en un formato no soportado. Por favor escoge de los siguientes formatos:",
|
||||
"id.verification.review.error": "Página de soporte de {siteName}",
|
||||
"id.verification.submitted.title": "Verificación de identidad en progreso.",
|
||||
"id.verification.submitted.text": "Hemos recibido tu información y estamos verificando tu identidad. Verás un mensaje en tu tablero cuando se complete el proceso de verificación (generalmente en un periodo de 5 días). Mientras tanto, aún puedes acceder a todo el contenido del curso disponible.",
|
||||
"id.verification.submitted.text": "Hemos recibido su información y estamos verificando su identidad. Se le notificará cuando se complete el proceso de verificación (generalmente dentro de los 5 días). Mientras tanto, aún puede acceder a todo el contenido del curso disponible.",
|
||||
"id.verification.return.dashboard": "Volver al panel principal",
|
||||
"id.verification.return.course": "Regresar al curso",
|
||||
"id.verification.return.generic": "Regresar",
|
||||
@@ -341,10 +341,6 @@
|
||||
"id.verification.photo.camera.help.text": "Si estás presentando problemas subiendo una foto, es posible que prefieras usar la cámara en vez. Para usar una cámara, has clic en el botón a continuación.",
|
||||
"id.verification.upload.help.button": "Cambia a modo de carga",
|
||||
"id.verification.camera.help.button": "Cambia a modo de cámara",
|
||||
"id.verification.choose.mode.title": "Opciones de requerimientos de foto",
|
||||
"id.verification.choose.mode.hep.text": "Para completar la verificación, por favor selecciona una de las siguientes opciones para cargar fotos. Podrás cambiar entre estas opciones durante el proceso en caso de requerirlo.",
|
||||
"id.verification.choose.mode.radio.upload": "Subir fotos de mi dispositivo",
|
||||
"id.verification.choose.mode.radio.camera": "Toma fotos usando mi cámara",
|
||||
"id.verification.request.camera.access.instructions": "Para tomar una foto con tu cámara web, es posible que recibas un aviso del navegador para acceder a tu cámara. {clickAllow}",
|
||||
"id.verification.requirements.account.managed.alert": "La configuración de tu perfil es administrada por {managerTitle}. Por lo tanto si el nombre en tu ID de foto coincide con tu nombre de cuenta por favor ponte en contacto con tu administrador de {profileDataManager} o con {soporte} para solicitar ayuda antes de completar el proceso de verificación de foto.",
|
||||
"id.verification.requirements.card.device.text": "Necesitas un dispositivo que tenga una cámara. Si has recibido un aviso del navegador para habilitar acceso a tu cámara, por favor asegúrate de seleccionar [allow].",
|
||||
|
||||
@@ -20,16 +20,22 @@
|
||||
"account.settings.field.full.name": "Nom complet",
|
||||
"account.settings.field.full.name.empty": "Ajouter un nom",
|
||||
"account.settings.field.full.name.help.text": "Le nom qui apparaîtra sur vos certificats et dans le cadre de toute vérification d'identité.",
|
||||
"account.settings.field.full.name.help.text.non.certificate": "Le nom qui apparaît sur votre profile public.",
|
||||
"account.settings.field.full.name.help.text.certificate": "Ce nom va apparaître sur vos attestations et dossiers publics.",
|
||||
"account.settings.field.full.name.help.text.default": "Le nom qui apparaît sur votre profile public.",
|
||||
"account.settings.field.full.name.help.text.default.certificate": "Ce nom va apparaître sur vos attestations et dossiers publics.",
|
||||
"account.settings.field.name.verified": "Nom vérifié",
|
||||
"account.settings.field.name.verified.help.text.verified": "Ce nom a été vérifié par une pièce d'identité gouvernementale.",
|
||||
"account.settings.field.name.verified.help.text.certificate": "This name has been verified by government ID and selected to appear on your certificates and public-facing records.",
|
||||
"account.settings.field.name.verified.help.text.verified": "Ce nom a été vérifié par une pièce d'identité avec photo.",
|
||||
"account.settings.field.name.verified.help.text.verified.proctored": "Ce nom a été vérifié par la surveillance.",
|
||||
"account.settings.field.name.verified.help.text.verified.certificate": "Ce nom a été vérifié par une pièce d'identité avec photo et est sélectionné pour apparaître sur vos certificats et vos dossiers publics.",
|
||||
"account.settings.field.name.verified.help.text.verified.proctored.certificate": "Ce nom a été vérifié par la surveillance et est sélectionné pour apparaître sur vos certificats et vos enregistrements publics.",
|
||||
"account.settings.field.name.verified.help.text.submitted": "La vérification a été soumise. Cela prend généralement 48 heures ou moins. Le nom vérifié ne peut pas être modifié pour le moment.",
|
||||
"account.settings.field.name.verified.help.text.submitted.proctored": "Votre examen surveillé a été soumis. Le nom vérifié ne peut pas être modifié pour le moment. Veuillez réessayer dans 2 à 5 jours.",
|
||||
"account.settings.field.name.verified.help.text.submitted.certificate": "Une fois la vérification d'identité réussie, ce nom apparaîtra sur vos attestations et vos dossiers publics. Le nom complet ne peut pas être modifié pour le moment.",
|
||||
"account.settings.field.name.verified.verification.help": "Enter your name as it appears on your government-issued ID.",
|
||||
"account.settings.field.name.verified.help.text.submitted.proctored.certificate": "Une fois que votre examen surveillé a réussi l'examen, ce nom apparaîtra sur votre certificat et vos dossiers publics. Le nom vérifié ne peut pas être modifié pour le moment.",
|
||||
"account.settings.field.name.verified.verification.help": "Entrez votre nom tel qu'il apparaît sur votre carte d'étudiant, de travail ou d'identité émise par le gouvernement.",
|
||||
"account.settings.field.full.name.help.text.submitted": "La vérification a été soumise. Cela prend généralement 48 heures ou moins. Le nom vérifié ne peut pas être modifié pour le moment.",
|
||||
"account.settings.field.full.name.help.text.submitted.proctored": "Votre examen surveillé a été soumis. Le nom complet ne peut pas être modifié pour le moment. Veuillez réessayer dans 2 à 5 jours.",
|
||||
"account.settings.field.full.name.help.text.submitted.certificate": "Une fois la vérification d'identité réussie, ce nom apparaîtra sur vos attestations et vos dossiers publics. Le nom complet ne peut pas être modifié pour le moment.",
|
||||
"account.settings.field.full.name.help.text.submitted.proctored.certificate": "Une fois que votre examen surveillé a réussi l'examen, ce nom apparaîtra sur vos certificats et vos dossiers publics. Le nom complet ne peut pas être modifié pour le moment.",
|
||||
"account.settings.field.name.verified.success.message": "Votre demande de vérification d'identité a été complétée avec succès. Vous avez maintenant la possibilité de sélectionner le nom que vous préférez voir apparaître sur vos attestations et documents publics.",
|
||||
"account.settings.field.name.verified.success.message.header": "Votre demande de changement de nom est terminée!",
|
||||
"account.settings.field.name.verified.failure.message": "Votre dernière tentative de vérification d'identité n'a pas abouti. Les paramètres du compte correspondant ont été restaurés.",
|
||||
@@ -133,7 +139,7 @@
|
||||
"account.settings.delete.account.text.change.instead": "Vous souhaitez modifier votre adresse électronique, votre nom ou votre mot de passe?",
|
||||
"account.settings.delete.account.button": "Supprimer mon compte",
|
||||
"account.settings.delete.account.please.activate": "activez votre compte",
|
||||
"account.settings.delete.account.please.confirm": "confirm your account",
|
||||
"account.settings.delete.account.please.confirm": "confirmer votre compte",
|
||||
"account.settings.delete.account.please.unlink": "dissocier tous les comptes de médias sociaux",
|
||||
"account.settings.delete.account.modal.header": "Êtes-vous certain ?",
|
||||
"account.settings.delete.account.modal.text.1": "Vous avez sélectionné \"Supprimer mon compte\". La suppression de votre compte et de vos données personnelles est permanente et ne peut être annulée. {siteName} ne pourra ni récupérer votre compte ni les données supprimées.",
|
||||
@@ -186,8 +192,8 @@
|
||||
"account.settings.name.change.title.begin": "Avant que l'on commençons",
|
||||
"account.settings.name.change.warning.one": "Warning: This action updates the name that appears on all certificates that have been earned on this account in the past and any certificates you are currently earning or will earn in the future.",
|
||||
"account.settings.name.change.warning.two": "Cette action ne peut pas être renversée sans vérification d'identité.",
|
||||
"account.settings.name.change.id.name.label": "Enter your name as it appears on your government-issued ID.",
|
||||
"account.settings.name.change.id.name.placeholder": "Enter the name on your government ID",
|
||||
"account.settings.name.change.id.name.label": "Entrez votre nom tel qu'il apparaît sur votre carte d'étudiant, de travail ou d'identité émise par le gouvernement.",
|
||||
"account.settings.name.change.id.name.placeholder": "Entrez le nom sur votre pièce d'identité avec photo",
|
||||
"account.settings.name.change.error.valid.name": "Entrez un nom valide",
|
||||
"account.settings.name.change.error.general": "Une erreur est survenue. Veuillez réessayer.",
|
||||
"account.settings.name.change.continue": "Continuer",
|
||||
@@ -206,7 +212,6 @@
|
||||
"id.verification.access.blocked.denied": "Nous ne pouvez pas vérifier votre identité pour le moment. Si vous n'avez pas encore activé votre compte, veuillez vérifier votre dossier de pourriels pour le courriel d'activation de {email}.",
|
||||
"id.verification.next": "Suivant",
|
||||
"id.verification.support": "support",
|
||||
"id.verification.continue.upload": "Continuer avec le téléchargement",
|
||||
"id.verification.example.card.alt": "Exemple de carte d'identité valide avec un nom complet et une photo.",
|
||||
"id.verification.requirements.title": "Exigences de vérification des photos",
|
||||
"id.verification.requirements.description": "Afin de procéder à la vérification des photos, vous aurez besoin des éléments suivants :",
|
||||
@@ -284,18 +289,13 @@
|
||||
"id.verification.photo.tips.list.well.lit": "Votre visage est bien éclairé",
|
||||
"id.verification.photo.tips.list.inside.frame": " Votre visage est entièrement dans le cadre.",
|
||||
"id.verification.portrait.photo.title.camera": "Prenez une photo de vous",
|
||||
"id.verification.portrait.photo.title.upload": "Téléchargez une photo de vous",
|
||||
"id.verification.portrait.photo.preview.alt": "Aperçu de la photo du visage de l'utilisateur.",
|
||||
"id.verification.portrait.photo.instructions.camera": "Lorsque votre visage est en position, utilisez le bouton Prendre une photo ci-dessous pour prendre votre photo.",
|
||||
"id.verification.portrait.photo.instructions.upload": "Veuillez télécharger une photo de portrait. Assurez-vous que tout votre visage s'insère dans le cadre et est bien éclairé. Formats pris en charge :",
|
||||
"id.verification.camera.help.sight.question": "Que faire si je ne peux pas voir l'image de la caméra ou si je ne peux pas voir ma photo pour déterminer quel côté est visible?",
|
||||
"id.verification.camera.help.sight.answer.portrait": "Vous pourrez peut-être terminer la procédure de capture d'image sans aide, mais cela peut prendre quelques tentatives de soumission pour obtenir le bon positionnement de la caméra. Le positionnement optimal de la caméra varie avec chaque ordinateur, mais généralement la meilleure position pour une prise de vue de la tête est d'environ 12-18 pouces (30-45 centimètres) de la caméra, la tête étant centrée par rapport à l'écran de l'ordinateur. Si les photos que vous soumettez sont rejetées, essayez de déplacer l’orientation de l’ordinateur ou de l’appareil photo pour modifier l’angle d’éclairage.",
|
||||
"id.verification.camera.help.sight.answer.id": "Vous pourrez peut-être terminer la procédure de capture d'image sans aide, mais cela peut prendre quelques tentatives de soumission pour obtenir le bon positionnement de la caméra. Le positionnement optimal de la caméra varie avec chaque ordinateur, mais généralement, la meilleure position pour une photo d'une carte d'identité est de 8-12 pouces (20-30 centimètres) de la caméra, la carte d'identité étant centrée par rapport à la caméra. Si les photos que vous soumettez sont rejetées, essayez de déplacer l’orientation de l’ordinateur ou de l’appareil photo pour modifier l’angle d’éclairage. La raison la plus courante de rejet est l'incapacité de lire le texte sur la carte d'identité.",
|
||||
"id.verification.camera.help.difficulty.question.portrait": "Que faire si j'ai des difficultés à maintenir ma tête en position par rapport à la caméra ?",
|
||||
"id.verification.camera.help.difficulty.question.id": "Que faire si j'ai des difficultés à tenir ma carte d'identité en position par rapport à la caméra ?",
|
||||
"id.verification.camera.help.difficulty.answer": "Si vous avez besoin d'aide pour prendre une photo à soumettre, contactez le support {siteName} pour des suggestions supplémentaires.",
|
||||
"id.verification.camera.help.upload.question": "Et si je souhaite télécharger une photo à la place ?",
|
||||
"id.verification.camera.help.upload.answer": "Sur la page suivante, vous aurez la possibilité de passer en mode téléchargement. En sélectionnant cette option, vous pourrez télécharger une photo à la place.",
|
||||
"id.verification.id.photo.unclear.question": "L'image de votre carte d'identité n'est pas claire ou trop floue ?",
|
||||
"id.verification.id.tips.title": "Conseils utiles pour les cartes d'identité",
|
||||
"id.verification.id.tips.description": "Ensuite, nous vous demanderons de prendre une photo d'une carte d'identité valide comportant votre nom complet et votre photo, comme un permis de conduire ou un passeport. Veuillez préparer votre carte d'identité.",
|
||||
@@ -309,9 +309,9 @@
|
||||
"id.verification.id.photo.instructions.upload.error.invalidFileType": "Le fichier que vous avez sélectionné n'est pas un type d'image pris en charge. Veuillez choisir parmi les formats suivants :",
|
||||
"id.verification.id.photo.instructions.upload.error.fileTooLarge": "Le fichier que vous avez sélectionné est trop volumineux. Veuillez réessayer avec un fichier de moins de 10 Mo.",
|
||||
"id.verification.name.check.title": "Vérifiez votre nom",
|
||||
"id.verification.name.check.instructions": "Le nom ci-dessous correspond-il au nom figurant sur votre pièce d'identité officielle ? Si ce n'est pas le cas, mettez à jour le nom ci-dessous afin qu'il corresponde à votre pièce d'identité.",
|
||||
"id.verification.name.check.mismatch.information": "Si le nom ci-dessous ne correspond pas à votre pièce d'identité, la vérification de votre identité sera refusée.",
|
||||
"id.verification.name.error": "Veuillez entrer votre nom tel qu'il apparaît sur votre pièce d'identité. ",
|
||||
"id.verification.name.check.instructions": "Le nom ci-dessous correspond-il au nom sur votre pièce d'identité avec photo ? Si ce n'est pas le cas, mettez à jour le nom ci-dessous pour qu'il corresponde à votre pièce d'identité avec photo.",
|
||||
"id.verification.name.check.mismatch.information": "Si le nom ci-dessous ne correspond pas à votre pièce d'identité avec photo, votre vérification d'identité sera refusée.",
|
||||
"id.verification.name.error": "Veuillez entrer votre nom tel qu'il apparaît sur votre pièce d'identité avec photo.",
|
||||
"id.verification.account.name.warning.prefix": "Veuillez noter:",
|
||||
"id.verification.account.name.settings": "Paramètres du compte",
|
||||
"id.verification.name.label": "Nom",
|
||||
@@ -331,7 +331,7 @@
|
||||
"id.verification.submission.alert.error.unsupported": "Un ou plusieurs des fichiers que vous avez téléchargés sont dans un format non pris en charge. Veuillez choisir parmi les formats suivants :",
|
||||
"id.verification.review.error": "Page de Support {siteName}",
|
||||
"id.verification.submitted.title": "Vérification d'identité en cours",
|
||||
"id.verification.submitted.text": "Nous avons reçu vos informations et vérifions votre identité. Vous verrez un message sur votre tableau de bord lorsque le processus de vérification sera terminé (généralement dans les 5 jours). En attendant, vous pouvez toujours accéder à tous les contenus de cours disponibles.",
|
||||
"id.verification.submitted.text": "Nous avons reçu vos informations et vérifions votre identité. Vous serez averti lorsque le processus de vérification sera terminé (généralement dans les 5 jours). En attendant, vous pouvez toujours accéder à tous les contenus de cours disponibles.",
|
||||
"id.verification.return.dashboard": "Retour au Tableau de bord",
|
||||
"id.verification.return.course": "Revenir au cours",
|
||||
"id.verification.return.generic": "Retour",
|
||||
@@ -341,10 +341,6 @@
|
||||
"id.verification.photo.camera.help.text": "Si vous rencontrez des difficultés pour télécharger une photo ci-dessus, vous pouvez utiliser votre appareil photo à la place. Pour utiliser votre appareil photo, cliquez sur le bouton ci-dessous.",
|
||||
"id.verification.upload.help.button": "Passer en mode de téléchargement",
|
||||
"id.verification.camera.help.button": "Passer en mode caméra",
|
||||
"id.verification.choose.mode.title": "Options d'exigences relatives aux photos",
|
||||
"id.verification.choose.mode.hep.text": "Pour terminer la vérification, veuillez sélectionner l'une des options suivantes pour soumettre des photos. Vous pourrez basculer entre ces options tout au long du processus si nécessaire.",
|
||||
"id.verification.choose.mode.radio.upload": "Télécharger des photos depuis mon appareil",
|
||||
"id.verification.choose.mode.radio.camera": "Prendre des photos avec mon appareil photo",
|
||||
"id.verification.request.camera.access.instructions": "Afin de prendre une photo à l'aide de votre webcam, vous pouvez recevoir une invite du navigateur pour accéder à votre caméra. {clickAllow}",
|
||||
"id.verification.requirements.account.managed.alert": "Les paramètres de votre compte sont gérés par {managerTitle}. Si le nom sur votre pièce d'identité avec photo ne correspond pas au nom de votre compte, veuillez contacter votre administrateur {profileDataManager} ou {support} pour obtenir de l'aide avant de terminer le processus de vérification des photos.",
|
||||
"id.verification.requirements.card.device.text": "Vous avez besoin d'un appareil équipé d'une caméra. Si vous recevez une invite du navigateur pour accéder à votre caméra, assurez-vous de cliquer sur {allow}.",
|
||||
|
||||
@@ -20,16 +20,22 @@
|
||||
"account.settings.field.full.name": "Full name",
|
||||
"account.settings.field.full.name.empty": "Add name",
|
||||
"account.settings.field.full.name.help.text": "The name that is used for ID verification and that appears on your certificates.",
|
||||
"account.settings.field.full.name.help.text.non.certificate": "The name that appears on your public profile.",
|
||||
"account.settings.field.full.name.help.text.certificate": "This name is selected to appear on your certificates and public-facing records.",
|
||||
"account.settings.field.full.name.help.text.default": "The name that appears on your public profile.",
|
||||
"account.settings.field.full.name.help.text.default.certificate": "This name is selected to appear on your certificates and public-facing records.",
|
||||
"account.settings.field.name.verified": "Verified name",
|
||||
"account.settings.field.name.verified.help.text.verified": "This name has been verified by government ID.",
|
||||
"account.settings.field.name.verified.help.text.certificate": "This name has been verified by government ID and selected to appear on your certificates and public-facing records.",
|
||||
"account.settings.field.name.verified.help.text.verified": "This name has been verified by photo ID.",
|
||||
"account.settings.field.name.verified.help.text.verified.proctored": "This name has been verified by proctoring.",
|
||||
"account.settings.field.name.verified.help.text.verified.certificate": "This name has been verified by photo ID, and is selected to appear on your certificates and public-facing records.",
|
||||
"account.settings.field.name.verified.help.text.verified.proctored.certificate": "This name has been verified by proctoring, and is selected to appear on your certificates and public-facing records.",
|
||||
"account.settings.field.name.verified.help.text.submitted": "Verification has been submitted. This usually takes 48 hours or less. Verified name cannot be changed at this time.",
|
||||
"account.settings.field.name.verified.help.text.submitted.proctored": "Your proctored exam has been submitted. Verified name cannot be changed at this time. Please check back in 2-5 days.",
|
||||
"account.settings.field.name.verified.help.text.submitted.certificate": "When identity verification is successful, this name will appear on your certificates and public-facing records. Verified name cannot be changed at this time.",
|
||||
"account.settings.field.name.verified.verification.help": "Enter your name as it appears on your government-issued ID.",
|
||||
"account.settings.field.name.verified.help.text.submitted.proctored.certificate": "Once your proctored exam passes review, this name will appear on your certificate and public-facing records. Verified Name cannot be changed at this time.",
|
||||
"account.settings.field.name.verified.verification.help": "Enter your name as it appears on your unexpired student, work, or government-issued identification card.",
|
||||
"account.settings.field.full.name.help.text.submitted": "Verification has been submitted. This usually takes 48 hours or less. Full name cannot be changed at this time.",
|
||||
"account.settings.field.full.name.help.text.submitted.proctored": "Your proctored exam has been submitted. Full name cannot be changed at this time. Please check back in 2-5 days.",
|
||||
"account.settings.field.full.name.help.text.submitted.certificate": "When identity verification is successful, this name will appear on your certificates and public-facing records. Full name cannot be changed at this time.",
|
||||
"account.settings.field.full.name.help.text.submitted.proctored.certificate": "Once your proctored exam passes review, this name will appear on your certificates and public-facing records. Full name cannot be changed at this time.",
|
||||
"account.settings.field.name.verified.success.message": "Your identity verification request has successfully completed. You now have the option of selecting which name you prefer to appear on your certificates and public-records.",
|
||||
"account.settings.field.name.verified.success.message.header": "Your name change request is complete!",
|
||||
"account.settings.field.name.verified.failure.message": "Your most recent identity verification attempt did not pass. Related account settings have been restored.",
|
||||
@@ -186,8 +192,8 @@
|
||||
"account.settings.name.change.title.begin": "Before we begin",
|
||||
"account.settings.name.change.warning.one": "Warning: This action updates the name that appears on all certificates that have been earned on this account in the past and any certificates you are currently earning or will earn in the future.",
|
||||
"account.settings.name.change.warning.two": "This action cannot be undone without verifying your identity.",
|
||||
"account.settings.name.change.id.name.label": "Enter your name as it appears on your government-issued ID.",
|
||||
"account.settings.name.change.id.name.placeholder": "Enter the name on your government ID",
|
||||
"account.settings.name.change.id.name.label": "Enter your name as it appears on your unexpired student, work, or government-issued identification card.",
|
||||
"account.settings.name.change.id.name.placeholder": "Enter the name on your photo ID",
|
||||
"account.settings.name.change.error.valid.name": "Please enter a valid name.",
|
||||
"account.settings.name.change.error.general": "A technical error occurred. Please try again.",
|
||||
"account.settings.name.change.continue": "Continue",
|
||||
@@ -206,7 +212,6 @@
|
||||
"id.verification.access.blocked.denied": "We cannot verify your identity at this time. If you have yet to activate your account, please check your spam folder for the activation email from {email}.",
|
||||
"id.verification.next": "Next",
|
||||
"id.verification.support": "support",
|
||||
"id.verification.continue.upload": "Continue with Upload",
|
||||
"id.verification.example.card.alt": "Example of a valid identification card with a full name and photo.",
|
||||
"id.verification.requirements.title": "Photo Verification Requirements",
|
||||
"id.verification.requirements.description": "In order to complete Photo Verification, you will need the following:",
|
||||
@@ -284,18 +289,13 @@
|
||||
"id.verification.photo.tips.list.well.lit": "Your face is well-lit.",
|
||||
"id.verification.photo.tips.list.inside.frame": "Your entire face fits inside the frame.",
|
||||
"id.verification.portrait.photo.title.camera": "Take a Photo of Yourself",
|
||||
"id.verification.portrait.photo.title.upload": "Upload a Photo of Yourself",
|
||||
"id.verification.portrait.photo.preview.alt": "Preview of photo of user's face.",
|
||||
"id.verification.portrait.photo.instructions.camera": "When your face is in position, use the Take Photo button below to take your photo.",
|
||||
"id.verification.portrait.photo.instructions.upload": "Please upload a portrait photo. Ensure your entire face fits inside the frame and is well-lit. Supported formats: ",
|
||||
"id.verification.camera.help.sight.question": "What if I can't see the camera image or if I can't see my photo to determine which side is visible?",
|
||||
"id.verification.camera.help.sight.answer.portrait": "You may be able to complete the image capture procedure without assistance, but it may take a couple of submission attempts to get the camera positioning right. Optimal camera positioning varies with each computer, but generally the best position for a headshot is approximately 12-18 inches (30-45 centimeters) from the camera, with your head centered relative to the computer screen. If the photos you submit are rejected, try moving the computer or camera orientation to change the lighting angle.",
|
||||
"id.verification.camera.help.sight.answer.id": "You may be able to complete the image capture procedure without assistance, but it may take a couple of submission attempts to get the camera positioning right. Optimal camera positioning varies with each computer, but generally, the best position for a photo of an ID card is 8-12 inches (20-30 centimeters) from the camera, with the ID card centered relative to the camera. If the photos you submit are rejected, try moving the computer or camera orientation to change the lighting angle. The most common reason for rejection is inability to read the text on the ID card.",
|
||||
"id.verification.camera.help.difficulty.question.portrait": "What if I have difficulty holding my head in position relative to the camera?",
|
||||
"id.verification.camera.help.difficulty.question.id": "What if I have difficulty holding my ID in position relative to the camera?",
|
||||
"id.verification.camera.help.difficulty.answer": "If you require assistance with taking a photo for submission, contact {siteName} support for additional suggestions.",
|
||||
"id.verification.camera.help.upload.question": "What if I want to upload a photo instead?",
|
||||
"id.verification.camera.help.upload.answer": "On the next page you will have the option to switch to upload mode. By selecting that option, you will be able to upload a photo instead.",
|
||||
"id.verification.id.photo.unclear.question": "Is your ID card image not clear or too blurry?",
|
||||
"id.verification.id.tips.title": "Helpful Identification Card Tips",
|
||||
"id.verification.id.tips.description": "Next, we'll need you to take a photo of a valid identification card that includes your full name and photo, such as a driver’s license or passport. Please have your ID ready.",
|
||||
@@ -309,9 +309,9 @@
|
||||
"id.verification.id.photo.instructions.upload.error.invalidFileType": "The file you have selected is not a supported image type. Please choose from the following formats: ",
|
||||
"id.verification.id.photo.instructions.upload.error.fileTooLarge": "The file you have selected is too large. Please try again with a file less than 10MB.",
|
||||
"id.verification.name.check.title": "Double-Check Your Name",
|
||||
"id.verification.name.check.instructions": "Does the name below match the name on your government-issued ID? If not, update the name below to match your goverment-issued ID.",
|
||||
"id.verification.name.check.mismatch.information": "If the name below does not match your government-issued ID, your identity verification will be denied.",
|
||||
"id.verification.name.error": "Please enter your name as it appears on your government-issued ID.",
|
||||
"id.verification.name.check.instructions": "Does the name below match the name on your photo ID? If not, update the name below to match your photo ID.",
|
||||
"id.verification.name.check.mismatch.information": "If the name below does not match your photo ID, your identity verification will be denied.",
|
||||
"id.verification.name.error": "Please enter your name as it appears on your photo ID.",
|
||||
"id.verification.account.name.warning.prefix": "Please Note:",
|
||||
"id.verification.account.name.settings": "Account Settings",
|
||||
"id.verification.name.label": "Name",
|
||||
@@ -331,7 +331,7 @@
|
||||
"id.verification.submission.alert.error.unsupported": "One or more of the files you have uploaded is in an unsupported format. Please choose from the following: ",
|
||||
"id.verification.review.error": "{siteName} Support Page",
|
||||
"id.verification.submitted.title": "Identity Verification in Progress",
|
||||
"id.verification.submitted.text": "We have received your information and are verifying your identity. You will see a message on your dashboard when the verification process is complete (usually within 5 days). In the meantime, you can still access all available course content.",
|
||||
"id.verification.submitted.text": "We have received your information and are verifying your identity. You will be notified when the verification process is complete (usually within 5 days). In the meantime, you can still access all available course content.",
|
||||
"id.verification.return.dashboard": "Return to Your Dashboard",
|
||||
"id.verification.return.course": "Return to Course",
|
||||
"id.verification.return.generic": "Return",
|
||||
@@ -341,10 +341,6 @@
|
||||
"id.verification.photo.camera.help.text": "If you are having trouble uploading a photo above, you may want to use your camera instead. To use your camera, click the button below.",
|
||||
"id.verification.upload.help.button": "Switch to Upload Mode",
|
||||
"id.verification.camera.help.button": "Switch to Camera Mode",
|
||||
"id.verification.choose.mode.title": "Photo Requirements Options",
|
||||
"id.verification.choose.mode.hep.text": "To complete verification, please select one of the following options to submit photos. You will be able to switch between these options throughout the process if needed.",
|
||||
"id.verification.choose.mode.radio.upload": "Upload photos from my device",
|
||||
"id.verification.choose.mode.radio.camera": "Take pictures using my camera",
|
||||
"id.verification.request.camera.access.instructions": "In order to take a photo using your webcam, you may receive a browser prompt for access to your camera. {clickAllow}",
|
||||
"id.verification.requirements.account.managed.alert": "Your account settings are managed by {managerTitle}. If the name on your photo ID does not match the name on your account, please contact your {profileDataManager} administrator or {support} for help before completing the Photo Verification process.",
|
||||
"id.verification.requirements.card.device.text": "You need a device that has a camera. If you receive a browser prompt for access to your camera, please make sure to click {allow}.",
|
||||
|
||||
@@ -262,7 +262,6 @@ class Camera extends React.Component {
|
||||
const dataUri = this.cameraPhoto.getDataUri(config);
|
||||
this.setState({ dataUri });
|
||||
this.props.onImageCapture(dataUri);
|
||||
this.props.setPhotoMode('camera');
|
||||
}
|
||||
|
||||
playShutterClick() {
|
||||
@@ -360,7 +359,6 @@ class Camera extends React.Component {
|
||||
Camera.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
onImageCapture: PropTypes.func.isRequired,
|
||||
setPhotoMode: PropTypes.func.isRequired,
|
||||
isPortrait: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,30 +1,14 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Collapsible } from '@edx/paragon';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import messages from './IdVerification.messages';
|
||||
import IdVerificationContext from './IdVerificationContext';
|
||||
|
||||
function CameraHelp(props) {
|
||||
const { optimizelyExperimentName } = useContext(IdVerificationContext);
|
||||
|
||||
return (
|
||||
<div>
|
||||
{ optimizelyExperimentName
|
||||
&& (
|
||||
<Collapsible
|
||||
styling="card"
|
||||
title={props.intl.formatMessage(messages['id.verification.camera.help.upload.question'])}
|
||||
className="mb-4 shadow"
|
||||
defaultOpen={props.isOpen}
|
||||
>
|
||||
<p>
|
||||
{props.intl.formatMessage(messages['id.verification.camera.help.upload.answer'])}
|
||||
</p>
|
||||
</Collapsible>
|
||||
)}
|
||||
<Collapsible
|
||||
styling="card"
|
||||
title={props.intl.formatMessage(messages['id.verification.camera.help.sight.question'])}
|
||||
|
||||
@@ -2,72 +2,55 @@ import React, { useContext } from 'react';
|
||||
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Button, Collapsible } from '@edx/paragon';
|
||||
import { Link } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import IdVerificationContext, { MEDIA_ACCESS } from './IdVerificationContext';
|
||||
import IdVerificationContext from './IdVerificationContext';
|
||||
import messages from './IdVerification.messages';
|
||||
|
||||
function CollapsibleImageHelp(props) {
|
||||
const {
|
||||
userId, shouldUseCamera, setShouldUseCamera, optimizelyExperimentName, mediaAccess,
|
||||
userId, useCameraForId, setUseCameraForId,
|
||||
} = useContext(IdVerificationContext);
|
||||
|
||||
function handleClick() {
|
||||
const toggleTo = shouldUseCamera ? 'upload' : 'camera';
|
||||
const toggleTo = useCameraForId ? 'upload' : 'camera';
|
||||
const eventName = `edx.id_verification.toggle_to.${toggleTo}`;
|
||||
sendTrackEvent(eventName, {
|
||||
category: 'id_verification',
|
||||
user_id: userId,
|
||||
});
|
||||
setShouldUseCamera(!shouldUseCamera);
|
||||
setUseCameraForId(!useCameraForId);
|
||||
}
|
||||
|
||||
if (optimizelyExperimentName && mediaAccess !== MEDIA_ACCESS.DENIED && mediaAccess !== MEDIA_ACCESS.UNSUPPORTED) {
|
||||
return (
|
||||
<Collapsible
|
||||
styling="card"
|
||||
title={shouldUseCamera ? props.intl.formatMessage(messages['id.verification.photo.upload.help.title']) : props.intl.formatMessage(messages['id.verification.photo.camera.help.title'])}
|
||||
className="mb-4 shadow"
|
||||
defaultOpen
|
||||
return (
|
||||
<Collapsible
|
||||
styling="card"
|
||||
title={useCameraForId
|
||||
? props.intl.formatMessage(messages['id.verification.photo.upload.help.title'])
|
||||
: props.intl.formatMessage(messages['id.verification.photo.camera.help.title'])}
|
||||
className="mb-4 shadow"
|
||||
defaultOpen
|
||||
>
|
||||
<p data-testid="help-text">
|
||||
{useCameraForId
|
||||
? props.intl.formatMessage(messages['id.verification.photo.upload.help.text'])
|
||||
: props.intl.formatMessage(messages['id.verification.photo.camera.help.text'])}
|
||||
</p>
|
||||
<Button
|
||||
title={useCameraForId ? 'Upload Photo' : 'Take Photo'} // TO-DO: translation
|
||||
data-testid="toggle-button"
|
||||
onClick={handleClick}
|
||||
style={{ marginTop: '0.5rem' }}
|
||||
>
|
||||
<p data-testid="help-text">
|
||||
{shouldUseCamera
|
||||
? props.intl.formatMessage(messages['id.verification.photo.upload.help.text'])
|
||||
: props.intl.formatMessage(messages['id.verification.photo.camera.help.text'])}
|
||||
</p>
|
||||
{ (mediaAccess === MEDIA_ACCESS.PENDING && !shouldUseCamera)
|
||||
? (
|
||||
// if a user has not enabled camera access yet, and they are trying to switch
|
||||
// to camera mode, direct them to panel that requests camera access
|
||||
<Link
|
||||
to={{ pathname: 'request-camera-access', state: { fromPortraitCapture: props.isPortrait, fromIdCapture: !props.isPortrait } }}
|
||||
className="btn btn-primary"
|
||||
data-testid="access-link"
|
||||
>
|
||||
{props.intl.formatMessage(messages['id.verification.photo.camera.help.button'])}
|
||||
</Link>
|
||||
)
|
||||
: (
|
||||
<Button
|
||||
title={shouldUseCamera ? 'Upload Portrait Photo' : 'Take Portrait Photo'}
|
||||
data-testid="toggle-button"
|
||||
onClick={handleClick}
|
||||
style={{ marginTop: '0.5rem' }}
|
||||
>
|
||||
{shouldUseCamera ? props.intl.formatMessage(messages['id.verification.photo.upload.help.button']) : props.intl.formatMessage(messages['id.verification.photo.camera.help.button'])}
|
||||
</Button>
|
||||
)}
|
||||
</Collapsible>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
{useCameraForId
|
||||
? props.intl.formatMessage(messages['id.verification.photo.upload.help.button'])
|
||||
: props.intl.formatMessage(messages['id.verification.photo.camera.help.button'])}
|
||||
</Button>
|
||||
</Collapsible>
|
||||
);
|
||||
}
|
||||
|
||||
CollapsibleImageHelp.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
isPortrait: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(CollapsibleImageHelp);
|
||||
|
||||
@@ -11,11 +11,6 @@ const messages = defineMessages({
|
||||
defaultMessage: 'support',
|
||||
description: 'Website support.',
|
||||
},
|
||||
'id.verification.continue.upload': {
|
||||
id: 'id.verification.continue.upload',
|
||||
defaultMessage: 'Continue with Upload',
|
||||
description: 'Button to continue with upload.',
|
||||
},
|
||||
'id.verification.example.card.alt': {
|
||||
id: 'id.verification.example.card.alt',
|
||||
defaultMessage: 'Example of a valid identification card with a full name and photo.',
|
||||
@@ -401,26 +396,11 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Take a Photo of Yourself',
|
||||
description: 'Title for the Portrait Photo page if camera access is enabled.',
|
||||
},
|
||||
'id.verification.portrait.photo.title.upload': {
|
||||
id: 'id.verification.portrait.photo.title.upload',
|
||||
defaultMessage: 'Upload a Photo of Yourself',
|
||||
description: 'Title for the Portrait Photo page if camera access is disabled.',
|
||||
},
|
||||
'id.verification.portrait.photo.preview.alt': {
|
||||
id: 'id.verification.portrait.photo.preview.alt',
|
||||
defaultMessage: 'Preview of photo of user\'s face.',
|
||||
description: 'Alt text for the portrait photo preview.',
|
||||
},
|
||||
'id.verification.portrait.photo.instructions.camera': {
|
||||
id: 'id.verification.portrait.photo.instructions.camera',
|
||||
defaultMessage: 'When your face is in position, use the Take Photo button below to take your photo.',
|
||||
description: 'Instructions to use the camera to take a portrait photo..',
|
||||
},
|
||||
'id.verification.portrait.photo.instructions.upload': {
|
||||
id: 'id.verification.portrait.photo.instructions.upload',
|
||||
defaultMessage: 'Please upload a portrait photo. Ensure your entire face fits inside the frame and is well-lit. Supported formats: ',
|
||||
description: 'Instructions for portrait photo upload.',
|
||||
},
|
||||
'id.verification.camera.help.sight.question': {
|
||||
id: 'id.verification.camera.help.sight.question',
|
||||
defaultMessage: 'What if I can\'t see the camera image or if I can\'t see my photo to determine which side is visible?',
|
||||
@@ -451,16 +431,6 @@ const messages = defineMessages({
|
||||
defaultMessage: 'If you require assistance with taking a photo for submission, contact {siteName} support for additional suggestions.',
|
||||
description: 'Confirming what to do if the user has difficult holding their head relative to the camera.',
|
||||
},
|
||||
'id.verification.camera.help.upload.question': {
|
||||
id: 'id.verification.camera.help.upload.question',
|
||||
defaultMessage: 'What if I want to upload a photo instead?',
|
||||
description: 'Question on what to do if the user would like to upload a photo instead.',
|
||||
},
|
||||
'id.verification.camera.help.upload.answer': {
|
||||
id: 'id.verification.camera.help.upload.answer',
|
||||
defaultMessage: 'On the next page you will have the option to switch to upload mode. By selecting that option, you will be able to upload a photo instead.',
|
||||
description: 'Confirming what to do if the user would like to upload a photo.',
|
||||
},
|
||||
'id.verification.id.photo.unclear.question': {
|
||||
id: 'id.verification.id.photo.unclear.question',
|
||||
defaultMessage: 'Is your ID card image not clear or too blurry?',
|
||||
@@ -528,17 +498,17 @@ const messages = defineMessages({
|
||||
},
|
||||
'id.verification.name.check.instructions': {
|
||||
id: 'id.verification.name.check.instructions',
|
||||
defaultMessage: 'Does the name below match the name on your government-issued ID? If not, update the name below to match your goverment-issued ID.',
|
||||
description: 'Text to instruct the user to check that the name displayed on the page matches what is on their government-issued ID.',
|
||||
defaultMessage: 'Does the name below match the name on your photo ID? If not, update the name below to match your photo ID.',
|
||||
description: 'Text to instruct the user to check that the name displayed on the page matches what is on their photo ID.',
|
||||
},
|
||||
'id.verification.name.check.mismatch.information': {
|
||||
id: 'id.verification.name.check.mismatch.information',
|
||||
defaultMessage: 'If the name below does not match your government-issued ID, your identity verification will be denied.',
|
||||
description: 'Text to inform the user that if the name displayed on the page does not match what is on their government-issued ID, identity verification will be denied.',
|
||||
defaultMessage: 'If the name below does not match your photo ID, your identity verification will be denied.',
|
||||
description: 'Text to inform the user that if the name displayed on the page does not match what is on their photo ID, identity verification will be denied.',
|
||||
},
|
||||
'id.verification.name.error': {
|
||||
id: 'id.verification.name.error',
|
||||
defaultMessage: 'Please enter your name as it appears on your government-issued ID.',
|
||||
defaultMessage: 'Please enter your name as it appears on your photo ID.',
|
||||
description: 'Error that shows when the user needs to update their name to match the name on their ID.',
|
||||
},
|
||||
'id.verification.account.name.warning.prefix': {
|
||||
@@ -638,7 +608,7 @@ const messages = defineMessages({
|
||||
},
|
||||
'id.verification.submitted.text': {
|
||||
id: 'id.verification.submitted.text',
|
||||
defaultMessage: 'We have received your information and are verifying your identity. You will see a message on your dashboard when the verification process is complete (usually within 5 days). In the meantime, you can still access all available course content.',
|
||||
defaultMessage: 'We have received your information and are verifying your identity. You will be notified when the verification process is complete (usually within 5 days). In the meantime, you can still access all available course content.',
|
||||
description: 'Text confirming that ID verification request has been received.',
|
||||
},
|
||||
'id.verification.return.dashboard': {
|
||||
@@ -686,26 +656,6 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Switch to Camera Mode',
|
||||
description: 'Button used to switch to camera mode.',
|
||||
},
|
||||
'id.verification.choose.mode.title': {
|
||||
id: 'id.verification.choose.mode.title',
|
||||
defaultMessage: 'Photo Requirements Options',
|
||||
description: 'Title for section that allows user to choose photo mode.',
|
||||
},
|
||||
'id.verification.choose.mode.help.text': {
|
||||
id: 'id.verification.choose.mode.hep.text',
|
||||
defaultMessage: 'To complete verification, please select one of the following options to submit photos. You will be able to switch between these options throughout the process if needed.',
|
||||
description: 'Help text for section that allows user to choose photo mode.',
|
||||
},
|
||||
'id.verification.choose.mode.radio.upload': {
|
||||
id: 'id.verification.choose.mode.radio.upload',
|
||||
defaultMessage: 'Upload photos from my device',
|
||||
description: 'Radio button to choose to upload photos.',
|
||||
},
|
||||
'id.verification.choose.mode.radio.camera': {
|
||||
id: 'id.verification.choose.mode.radio.camera',
|
||||
defaultMessage: 'Take pictures using my camera',
|
||||
description: 'Radio button to choose to use camera for photos.',
|
||||
},
|
||||
});
|
||||
|
||||
export default messages;
|
||||
|
||||
@@ -45,12 +45,8 @@ export default function IdVerificationContextProvider({ children }) {
|
||||
}
|
||||
}, [authenticatedUser]);
|
||||
|
||||
const [optimizelyExperimentName, setOptimizelyExperimentName] = useState('');
|
||||
const [shouldUseCamera, setShouldUseCamera] = useState(false);
|
||||
|
||||
// The following are used to keep track of how a user has submitted photos
|
||||
const [portraitPhotoMode, setPortraitPhotoMode] = useState('');
|
||||
const [idPhotoMode, setIdPhotoMode] = useState('');
|
||||
// Default to upload for the ID image
|
||||
const [useCameraForId, setUseCameraForId] = useState(false);
|
||||
|
||||
// If the user reaches the end of the flow and goes back to retake their photos,
|
||||
// this flag ensures that they are directed straight back to the summary panel
|
||||
@@ -86,31 +82,23 @@ export default function IdVerificationContextProvider({ children }) {
|
||||
// when determining the context value nameOnAccount.
|
||||
nameOnAccount: verifiedName || authenticatedUser.name,
|
||||
profileDataManager,
|
||||
optimizelyExperimentName,
|
||||
shouldUseCamera,
|
||||
portraitPhotoMode,
|
||||
idPhotoMode,
|
||||
useCameraForId,
|
||||
reachedSummary,
|
||||
setFacePhotoFile,
|
||||
setIdPhotoFile,
|
||||
setIdPhotoName,
|
||||
setOptimizelyExperimentName,
|
||||
setShouldUseCamera,
|
||||
setPortraitPhotoMode,
|
||||
setIdPhotoMode,
|
||||
setUseCameraForId,
|
||||
setReachedSummary,
|
||||
tryGetUserMedia: async () => {
|
||||
try {
|
||||
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
|
||||
setMediaAccess(MEDIA_ACCESS.GRANTED);
|
||||
setMediaStream(stream);
|
||||
setShouldUseCamera(true);
|
||||
// stop the stream, as we are not using it yet
|
||||
const tracks = stream.getTracks();
|
||||
tracks.forEach(track => track.stop());
|
||||
} catch (err) {
|
||||
setMediaAccess(MEDIA_ACCESS.DENIED);
|
||||
setShouldUseCamera(false);
|
||||
}
|
||||
},
|
||||
stopUserMedia: () => {
|
||||
|
||||
@@ -14,7 +14,6 @@ import './getUserMediaShim';
|
||||
import IdVerificationContextProvider from './IdVerificationContextProvider';
|
||||
import { VerifiedNameContextProvider } from './VerifiedNameContext';
|
||||
import ReviewRequirementsPanel from './panels/ReviewRequirementsPanel';
|
||||
import ChooseModePanel from './panels/ChooseModePanel';
|
||||
import RequestCameraAccessPanel from './panels/RequestCameraAccessPanel';
|
||||
import PortraitPhotoContextPanel from './panels/PortraitPhotoContextPanel';
|
||||
import TakePortraitPhotoPanel from './panels/TakePortraitPhotoPanel';
|
||||
@@ -57,7 +56,6 @@ function IdVerificationPage(props) {
|
||||
<IdVerificationContextProvider>
|
||||
<Switch>
|
||||
<Route path={`${path}/review-requirements`} component={ReviewRequirementsPanel} />
|
||||
<Route path={`${path}/choose-mode`} component={ChooseModePanel} />
|
||||
<Route path={`${path}/request-camera-access`} component={RequestCameraAccessPanel} />
|
||||
<Route path={`${path}/portrait-photo-context`} component={PortraitPhotoContextPanel} />
|
||||
<Route path={`${path}/take-portrait-photo`} component={TakePortraitPhotoPanel} />
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Alert } from '@edx/paragon';
|
||||
import messages from './IdVerification.messages';
|
||||
import SupportedMediaTypes from './SupportedMediaTypes';
|
||||
|
||||
export default function ImageFileUpload({ onFileChange, setPhotoMode, intl }) {
|
||||
export default function ImageFileUpload({ onFileChange, intl }) {
|
||||
const [error, setError] = useState(null);
|
||||
const errorTypes = {
|
||||
invalidFileType: 'invalidFileType',
|
||||
@@ -28,7 +28,6 @@ export default function ImageFileUpload({ onFileChange, setPhotoMode, intl }) {
|
||||
const fileReader = new FileReader();
|
||||
fileReader.addEventListener('load', () => {
|
||||
onFileChange(fileReader.result);
|
||||
setPhotoMode('upload');
|
||||
});
|
||||
fileReader.readAsDataURL(fileObject);
|
||||
}
|
||||
@@ -59,6 +58,5 @@ export default function ImageFileUpload({ onFileChange, setPhotoMode, intl }) {
|
||||
|
||||
ImageFileUpload.propTypes = {
|
||||
onFileChange: PropTypes.func.isRequired,
|
||||
setPhotoMode: PropTypes.func.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
max-width: 100%;
|
||||
}
|
||||
.card.accent {
|
||||
border-top: solid 4px theme-color('warning');
|
||||
border-top-width: 4px;
|
||||
border-top-style: solid;
|
||||
}
|
||||
.image-preview {
|
||||
margin-bottom: 1rem;
|
||||
|
||||
@@ -64,9 +64,6 @@ export async function submitIdVerification(verificationData) {
|
||||
facePhotoFile: 'face_image',
|
||||
idPhotoFile: 'photo_id_image',
|
||||
idPhotoName: 'full_name',
|
||||
optimizelyExperimentName: 'experiment_name',
|
||||
portraitPhotoMode: 'portrait_photo_mode',
|
||||
idPhotoMode: 'id_photo_mode',
|
||||
};
|
||||
const postData = {};
|
||||
// Don't include blank/null/undefined values.
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
import React, { useContext } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Form } from '@edx/paragon';
|
||||
|
||||
import { useNextPanelSlug } from '../routing-utilities';
|
||||
import BasePanel from './BasePanel';
|
||||
import IdVerificationContext from '../IdVerificationContext';
|
||||
import messages from '../IdVerification.messages';
|
||||
|
||||
function ChooseModePanel(props) {
|
||||
const panelSlug = 'choose-mode';
|
||||
const { userId, shouldUseCamera, setShouldUseCamera } = useContext(IdVerificationContext);
|
||||
|
||||
function onPhotoModeChange(value) {
|
||||
setShouldUseCamera(value);
|
||||
const mode = value ? 'camera' : 'upload';
|
||||
const eventName = `edx.id_verification.choose.${mode}`;
|
||||
sendTrackEvent(eventName, {
|
||||
category: 'id_verification',
|
||||
user_id: userId,
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<BasePanel
|
||||
name={panelSlug}
|
||||
title={props.intl.formatMessage(messages['id.verification.choose.mode.title'])}
|
||||
>
|
||||
<p>
|
||||
{props.intl.formatMessage(messages['id.verification.choose.mode.help.text'])}
|
||||
</p>
|
||||
<fieldset>
|
||||
<Form.Group controlId="formChoosePhotoOption" style={{ marginLeft: '1.25rem' }}>
|
||||
<Form.Check
|
||||
type="radio"
|
||||
id="useUploadMode"
|
||||
label={props.intl.formatMessage(messages['id.verification.choose.mode.radio.upload'])}
|
||||
name="photoMode"
|
||||
checked={!shouldUseCamera}
|
||||
onChange={() => onPhotoModeChange(false)}
|
||||
/>
|
||||
<Form.Check
|
||||
type="radio"
|
||||
id="useCameraMode"
|
||||
label={props.intl.formatMessage(messages['id.verification.choose.mode.radio.camera'])}
|
||||
name="photoMode"
|
||||
checked={shouldUseCamera}
|
||||
onChange={() => onPhotoModeChange(true)}
|
||||
/>
|
||||
</Form.Group>
|
||||
</fieldset>
|
||||
<div className="action-row">
|
||||
<Link to={useNextPanelSlug(panelSlug)} className="btn btn-primary" data-testid="next-button">
|
||||
{props.intl.formatMessage(messages['id.verification.next'])}
|
||||
</Link>
|
||||
</div>
|
||||
</BasePanel>
|
||||
);
|
||||
}
|
||||
|
||||
ChooseModePanel.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(ChooseModePanel);
|
||||
@@ -17,7 +17,7 @@ function IdContextPanel(props) {
|
||||
title={props.intl.formatMessage(messages['id.verification.id.tips.title'])}
|
||||
>
|
||||
<p>{props.intl.formatMessage(messages['id.verification.id.tips.description'])}</p>
|
||||
<div className="card mb-4 shadow accent">
|
||||
<div className="card mb-4 shadow accent border-warning">
|
||||
<div className="card-body">
|
||||
<h6>
|
||||
{props.intl.formatMessage(messages['id.verification.photo.tips.list.title'])}
|
||||
|
||||
@@ -18,7 +18,7 @@ function PortraitPhotoContextPanel(props) {
|
||||
<p>
|
||||
{props.intl.formatMessage(messages['id.verification.photo.tips.description'])}
|
||||
</p>
|
||||
<div className="card mb-4 shadow accent">
|
||||
<div className="card mb-4 shadow accent border-warning">
|
||||
<div className="card-body">
|
||||
<h6>
|
||||
{props.intl.formatMessage(messages['id.verification.photo.tips.list.title'])}
|
||||
|
||||
@@ -19,7 +19,7 @@ function RequestCameraAccessPanel(props) {
|
||||
const panelSlug = 'request-camera-access';
|
||||
const nextPanelSlug = useNextPanelSlug(panelSlug);
|
||||
const {
|
||||
tryGetUserMedia, mediaAccess, userId, optimizelyExperimentName,
|
||||
tryGetUserMedia, mediaAccess, userId,
|
||||
} = useContext(IdVerificationContext);
|
||||
const browserName = Bowser.parse(window.navigator.userAgent).browser.name;
|
||||
|
||||
@@ -54,12 +54,6 @@ function RequestCameraAccessPanel(props) {
|
||||
</a>
|
||||
);
|
||||
|
||||
const nextButtonLink = (
|
||||
<Link to={nextPanelSlug} className="btn btn-primary" data-testid="next-button">
|
||||
{props.intl.formatMessage(messages['id.verification.continue.upload'])}
|
||||
</Link>
|
||||
);
|
||||
|
||||
return (
|
||||
<BasePanel
|
||||
name={panelSlug}
|
||||
@@ -105,7 +99,7 @@ function RequestCameraAccessPanel(props) {
|
||||
</p>
|
||||
<EnableCameraDirectionsPanel browserName={browserName} intl={props.intl} />
|
||||
<div className="action-row">
|
||||
{optimizelyExperimentName ? nextButtonLink : returnLink}
|
||||
{returnLink}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -117,7 +111,7 @@ function RequestCameraAccessPanel(props) {
|
||||
</p>
|
||||
<UnsupportedCameraDirectionsPanel browserName={browserName} intl={props.intl} />
|
||||
<div className="action-row">
|
||||
{optimizelyExperimentName ? nextButtonLink : returnLink}
|
||||
{returnLink}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -13,31 +13,15 @@ import messages from '../IdVerification.messages';
|
||||
import exampleCard from '../assets/example-card.png';
|
||||
|
||||
function ReviewRequirementsPanel(props) {
|
||||
const {
|
||||
userId, profileDataManager, setOptimizelyExperimentName,
|
||||
} = useContext(IdVerificationContext);
|
||||
const { userId, profileDataManager } = useContext(IdVerificationContext);
|
||||
const panelSlug = 'review-requirements';
|
||||
const nextPanelSlug = useNextPanelSlug(panelSlug);
|
||||
|
||||
const getExperiments = () => {
|
||||
const {
|
||||
experimentVariables: {
|
||||
experimentName = '',
|
||||
} = {},
|
||||
} = window;
|
||||
|
||||
if (experimentName) {
|
||||
setOptimizelyExperimentName(experimentName);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
sendTrackEvent('edx.id_verification.started', {
|
||||
category: 'id_verification',
|
||||
user_id: userId,
|
||||
});
|
||||
|
||||
getExperiments();
|
||||
}, [userId]);
|
||||
|
||||
function renderManagedProfileMessage() {
|
||||
@@ -77,7 +61,7 @@ function ReviewRequirementsPanel(props) {
|
||||
<p>
|
||||
{props.intl.formatMessage(messages['id.verification.requirements.description'])}
|
||||
</p>
|
||||
<div className="card mb-4 shadow accent">
|
||||
<div className="card mb-4 shadow accent border-warning">
|
||||
<div className="card-body">
|
||||
<h6 aria-level="3">
|
||||
{props.intl.formatMessage(messages['id.verification.requirements.card.device.title'])}
|
||||
@@ -94,7 +78,7 @@ function ReviewRequirementsPanel(props) {
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card mb-4 shadow accent">
|
||||
<div className="card mb-4 shadow accent border-warning">
|
||||
<div className="card-body">
|
||||
<h6 aria-level="3">
|
||||
{props.intl.formatMessage(messages['id.verification.requirements.card.id.title'])}
|
||||
|
||||
@@ -26,10 +26,7 @@ function SummaryPanel(props) {
|
||||
nameOnAccount,
|
||||
idPhotoName,
|
||||
stopUserMedia,
|
||||
optimizelyExperimentName,
|
||||
setReachedSummary,
|
||||
portraitPhotoMode,
|
||||
idPhotoMode,
|
||||
} = useContext(IdVerificationContext);
|
||||
const nameToBeUsed = idPhotoName || nameOnAccount || '';
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
@@ -79,11 +76,6 @@ function SummaryPanel(props) {
|
||||
*/
|
||||
verificationData.idPhotoName = nameOnAccount;
|
||||
}
|
||||
if (optimizelyExperimentName) {
|
||||
verificationData.optimizelyExperimentName = optimizelyExperimentName;
|
||||
verificationData.portraitPhotoMode = portraitPhotoMode;
|
||||
verificationData.idPhotoMode = idPhotoMode;
|
||||
}
|
||||
const result = await submitIdVerification(verificationData);
|
||||
if (result.success) {
|
||||
stopUserMedia();
|
||||
@@ -208,7 +200,7 @@ function SummaryPanel(props) {
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
{!optimizelyExperimentName && <CameraHelpWithUpload />}
|
||||
<CameraHelpWithUpload />
|
||||
<div className="form-group">
|
||||
<label htmlFor="name-to-be-used" className="font-weight-bold">
|
||||
{props.intl.formatMessage(messages['id.verification.name.label'])}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
@@ -17,24 +17,36 @@ import SupportedMediaTypes from '../SupportedMediaTypes';
|
||||
function TakeIdPhotoPanel(props) {
|
||||
const panelSlug = 'take-id-photo';
|
||||
const nextPanelSlug = useNextPanelSlug(panelSlug);
|
||||
const {
|
||||
setIdPhotoFile, idPhotoFile, optimizelyExperimentName, shouldUseCamera, setIdPhotoMode,
|
||||
} = useContext(IdVerificationContext);
|
||||
const { setIdPhotoFile, idPhotoFile, useCameraForId } = useContext(IdVerificationContext);
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// This prevents focus switching to the heading when taking a photo
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<BasePanel
|
||||
name={panelSlug}
|
||||
title={shouldUseCamera ? props.intl.formatMessage(messages['id.verification.id.photo.title.camera']) : props.intl.formatMessage(messages['id.verification.id.photo.title.upload'])}
|
||||
focusOnMount={!mounted}
|
||||
title={useCameraForId
|
||||
? props.intl.formatMessage(messages['id.verification.id.photo.title.camera'])
|
||||
: props.intl.formatMessage(messages['id.verification.id.photo.title.upload'])}
|
||||
>
|
||||
<div>
|
||||
{idPhotoFile && !shouldUseCamera && <ImagePreview src={idPhotoFile} alt={props.intl.formatMessage(messages['id.verification.id.photo.preview.alt'])} />}
|
||||
{idPhotoFile && !useCameraForId && (
|
||||
<ImagePreview
|
||||
src={idPhotoFile}
|
||||
alt={props.intl.formatMessage(messages['id.verification.id.photo.preview.alt'])}
|
||||
/>
|
||||
)}
|
||||
|
||||
{shouldUseCamera ? (
|
||||
{useCameraForId ? (
|
||||
<div>
|
||||
<p>
|
||||
{props.intl.formatMessage(messages['id.verification.id.photo.instructions.camera'])}
|
||||
</p>
|
||||
<Camera onImageCapture={setIdPhotoFile} setPhotoMode={setIdPhotoMode} isPortrait={false} />
|
||||
<Camera onImageCapture={setIdPhotoFile} isPortrait={false} />
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ marginBottom: '1.25rem' }}>
|
||||
@@ -42,12 +54,12 @@ function TakeIdPhotoPanel(props) {
|
||||
{props.intl.formatMessage(messages['id.verification.id.photo.instructions.upload'])}
|
||||
<SupportedMediaTypes />
|
||||
</p>
|
||||
<ImageFileUpload onFileChange={setIdPhotoFile} setPhotoMode={setIdPhotoMode} intl={props.intl} />
|
||||
<ImageFileUpload onFileChange={setIdPhotoFile} intl={props.intl} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{shouldUseCamera && !optimizelyExperimentName && <CameraHelp />}
|
||||
<CollapsibleImageHelp isPortrait={false} />
|
||||
{useCameraForId && <CameraHelp />}
|
||||
<CollapsibleImageHelp />
|
||||
<div className="action-row" style={{ visibility: idPhotoFile ? 'unset' : 'hidden' }}>
|
||||
<Link to={nextPanelSlug} className="btn btn-primary" data-testid="next-button">
|
||||
{props.intl.formatMessage(messages['id.verification.next'])}
|
||||
|
||||
@@ -1,53 +1,39 @@
|
||||
import React, { useContext } from 'react';
|
||||
import React, { useContext, useEffect, useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { useNextPanelSlug } from '../routing-utilities';
|
||||
import BasePanel from './BasePanel';
|
||||
import ImageFileUpload from '../ImageFileUpload';
|
||||
import ImagePreview from '../ImagePreview';
|
||||
import Camera from '../Camera';
|
||||
import CameraHelp from '../CameraHelp';
|
||||
import IdVerificationContext from '../IdVerificationContext';
|
||||
|
||||
import messages from '../IdVerification.messages';
|
||||
import CollapsibleImageHelp from '../CollapsibleImageHelp';
|
||||
import SupportedMediaTypes from '../SupportedMediaTypes';
|
||||
|
||||
function TakePortraitPhotoPanel(props) {
|
||||
const panelSlug = 'take-portrait-photo';
|
||||
const nextPanelSlug = useNextPanelSlug(panelSlug);
|
||||
const {
|
||||
setFacePhotoFile, facePhotoFile, shouldUseCamera, optimizelyExperimentName, setPortraitPhotoMode,
|
||||
} = useContext(IdVerificationContext);
|
||||
const { setFacePhotoFile, facePhotoFile } = useContext(IdVerificationContext);
|
||||
const [mounted, setMounted] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
// This prevents focus switching to the heading when taking a photo
|
||||
setMounted(true);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<BasePanel
|
||||
name={panelSlug}
|
||||
title={shouldUseCamera ? props.intl.formatMessage(messages['id.verification.portrait.photo.title.camera']) : props.intl.formatMessage(messages['id.verification.portrait.photo.title.upload'])}
|
||||
focusOnMount={!mounted}
|
||||
title={props.intl.formatMessage(messages['id.verification.portrait.photo.title.camera'])}
|
||||
>
|
||||
<div>
|
||||
{facePhotoFile && !shouldUseCamera && <ImagePreview src={facePhotoFile} alt={props.intl.formatMessage(messages['id.verification.portrait.photo.preview.alt'])} />}
|
||||
|
||||
{shouldUseCamera ? (
|
||||
<div>
|
||||
<p>
|
||||
{props.intl.formatMessage(messages['id.verification.portrait.photo.instructions.camera'])}
|
||||
</p>
|
||||
<Camera onImageCapture={setFacePhotoFile} setPhotoMode={setPortraitPhotoMode} isPortrait />
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ marginBottom: '1.25rem' }}>
|
||||
<p data-testid="upload-text">
|
||||
{props.intl.formatMessage(messages['id.verification.portrait.photo.instructions.upload'])}
|
||||
<SupportedMediaTypes />
|
||||
</p>
|
||||
<ImageFileUpload onFileChange={setFacePhotoFile} setPhotoMode={setPortraitPhotoMode} intl={props.intl} />
|
||||
</div>
|
||||
)}
|
||||
<p>
|
||||
{props.intl.formatMessage(messages['id.verification.portrait.photo.instructions.camera'])}
|
||||
</p>
|
||||
<Camera onImageCapture={setFacePhotoFile} isPortrait />
|
||||
</div>
|
||||
{shouldUseCamera && !optimizelyExperimentName && <CameraHelp isPortrait />}
|
||||
<CollapsibleImageHelp isPortrait />
|
||||
<CameraHelp isPortrait />
|
||||
<div className="action-row" style={{ visibility: facePhotoFile ? 'unset' : 'hidden' }}>
|
||||
<Link to={nextPanelSlug} className="btn btn-primary" data-testid="next-button">
|
||||
{props.intl.formatMessage(messages['id.verification.next'])}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useContext } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import IdVerificationContext, { MEDIA_ACCESS } from './IdVerificationContext';
|
||||
import IdVerificationContext from './IdVerificationContext';
|
||||
|
||||
const SLUGS = {
|
||||
REVIEW_REQUIREMENTS: 'review-requirements',
|
||||
@@ -17,7 +16,6 @@ const SLUGS = {
|
||||
|
||||
const panelSteps = [
|
||||
SLUGS.REVIEW_REQUIREMENTS,
|
||||
SLUGS.CHOOSE_MODE,
|
||||
SLUGS.REQUEST_CAMERA_ACCESS,
|
||||
SLUGS.PORTRAIT_PHOTO_CONTEXT,
|
||||
SLUGS.TAKE_PORTRAIT_PHOTO,
|
||||
@@ -31,15 +29,7 @@ const panelSteps = [
|
||||
// eslint-disable-next-line import/prefer-default-export
|
||||
export const useNextPanelSlug = (originSlug) => {
|
||||
// Go back to the summary view if that's where they came from
|
||||
const location = useLocation();
|
||||
const isFromPortrait = location.state && location.state.fromPortraitCapture;
|
||||
const isFromId = location.state && location.state.fromIdCapture;
|
||||
const {
|
||||
mediaAccess,
|
||||
optimizelyExperimentName,
|
||||
reachedSummary,
|
||||
shouldUseCamera,
|
||||
} = useContext(IdVerificationContext);
|
||||
const { reachedSummary } = useContext(IdVerificationContext);
|
||||
|
||||
const canRerouteToSummary = [
|
||||
SLUGS.TAKE_PORTRAIT_PHOTO,
|
||||
@@ -51,32 +41,6 @@ export const useNextPanelSlug = (originSlug) => {
|
||||
return SLUGS.SUMMARY;
|
||||
}
|
||||
|
||||
// the following are used as part of an A/B experiment
|
||||
if (isFromPortrait) {
|
||||
if (mediaAccess === MEDIA_ACCESS.GRANTED) {
|
||||
return SLUGS.PORTRAIT_PHOTO_CONTEXT;
|
||||
}
|
||||
return SLUGS.TAKE_PORTRAIT_PHOTO;
|
||||
}
|
||||
if (isFromId) {
|
||||
if (mediaAccess === MEDIA_ACCESS.GRANTED) {
|
||||
return SLUGS.ID_CONTEXT;
|
||||
}
|
||||
return SLUGS.TAKE_ID_PHOTO;
|
||||
}
|
||||
if (originSlug === SLUGS.REVIEW_REQUIREMENTS && !optimizelyExperimentName) {
|
||||
return SLUGS.REQUEST_CAMERA_ACCESS;
|
||||
}
|
||||
if (originSlug === SLUGS.CHOOSE_MODE && !shouldUseCamera) {
|
||||
return SLUGS.TAKE_PORTRAIT_PHOTO;
|
||||
}
|
||||
if (originSlug === SLUGS.TAKE_PORTRAIT_PHOTO && !shouldUseCamera) {
|
||||
return SLUGS.TAKE_ID_PHOTO;
|
||||
}
|
||||
if (originSlug === SLUGS.REQUEST_CAMERA_ACCESS && mediaAccess !== MEDIA_ACCESS.GRANTED) {
|
||||
return SLUGS.TAKE_PORTRAIT_PHOTO;
|
||||
}
|
||||
|
||||
const nextIndex = panelSteps.indexOf(originSlug) + 1;
|
||||
return nextIndex < panelSteps.length ? panelSteps[nextIndex] : null;
|
||||
};
|
||||
@@ -84,11 +48,8 @@ export const useNextPanelSlug = (originSlug) => {
|
||||
// check if the user is too far into the flow and if so, return the slug of the
|
||||
// furthest panel they are allow to be.
|
||||
export const useVerificationRedirectSlug = (slug) => {
|
||||
const { facePhotoFile, idPhotoFile, optimizelyExperimentName } = useContext(IdVerificationContext);
|
||||
const { facePhotoFile, idPhotoFile } = useContext(IdVerificationContext);
|
||||
const indexOfCurrentPanel = panelSteps.indexOf(slug);
|
||||
if (!optimizelyExperimentName && slug === SLUGS.CHOOSE_MODE) {
|
||||
return SLUGS.REVIEW_REQUIREMENTS;
|
||||
}
|
||||
if (!facePhotoFile) {
|
||||
if (indexOfCurrentPanel > panelSteps.indexOf(SLUGS.TAKE_PORTRAIT_PHOTO)) {
|
||||
return SLUGS.PORTRAIT_PHOTO_CONTEXT;
|
||||
|
||||
@@ -27,7 +27,6 @@ describe('SubmittedPanel', () => {
|
||||
const defaultProps = {
|
||||
intl: {},
|
||||
onImageCapture: jest.fn(),
|
||||
setPhotoMode: jest.fn(),
|
||||
isPortrait: true,
|
||||
};
|
||||
|
||||
@@ -57,7 +56,6 @@ describe('SubmittedPanel', () => {
|
||||
expect(button).toHaveTextContent('Take Photo');
|
||||
fireEvent.click(button);
|
||||
expect(defaultProps.onImageCapture).toHaveBeenCalled();
|
||||
expect(defaultProps.setPhotoMode).toHaveBeenCalledWith('camera');
|
||||
});
|
||||
|
||||
it('shows correct help text for portrait photo capture', async () => {
|
||||
|
||||
@@ -2,7 +2,7 @@ import React from 'react';
|
||||
import { Router } from 'react-router-dom';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import {
|
||||
render, cleanup, screen, act, fireEvent,
|
||||
render, cleanup, screen, act,
|
||||
} from '@testing-library/react';
|
||||
import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import * as analytics from '@edx/frontend-platform/analytics';
|
||||
@@ -22,71 +22,18 @@ const IntlCollapsible = injectIntl(CollapsibleImageHelp);
|
||||
const history = createMemoryHistory();
|
||||
|
||||
describe('CollapsibleImageHelpPanel', () => {
|
||||
const defaultProps = {
|
||||
intl: {},
|
||||
isPortrait: true,
|
||||
};
|
||||
const defaultProps = { intl: {} };
|
||||
|
||||
const contextValue = {
|
||||
shouldUseCamera: true,
|
||||
setShouldUseCamera: jest.fn(),
|
||||
optimizelyExperimentName: '',
|
||||
mediaAccess: 'granted',
|
||||
useCameraForId: true,
|
||||
setUseCameraForId: jest.fn(),
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it('does not return if not part of experiment', async () => {
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlCollapsible {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
|
||||
const titleText = screen.queryByText('Upload a Photo Instead');
|
||||
expect(titleText).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('does not return if media access denied or unsupported', async () => {
|
||||
let titleText = '';
|
||||
contextValue.mediaAccess = 'denied';
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlCollapsible {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
|
||||
titleText = screen.queryByText('Upload a Photo Instead');
|
||||
expect(titleText).not.toBeInTheDocument();
|
||||
|
||||
contextValue.mediaAccess = 'unsupported';
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlCollapsible {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
|
||||
titleText = screen.queryByText('Upload a Photo Instead');
|
||||
expect(titleText).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows the correct text if user should switch to upload', async () => {
|
||||
contextValue.optimizelyExperimentName = 'test';
|
||||
contextValue.mediaAccess = 'granted';
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
@@ -106,9 +53,7 @@ describe('CollapsibleImageHelpPanel', () => {
|
||||
});
|
||||
|
||||
it('shows the correct text if user should switch to camera', async () => {
|
||||
contextValue.optimizelyExperimentName = 'test';
|
||||
contextValue.mediaAccess = 'granted';
|
||||
contextValue.shouldUseCamera = false;
|
||||
contextValue.useCameraForId = false;
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
@@ -126,27 +71,4 @@ describe('CollapsibleImageHelpPanel', () => {
|
||||
const button = screen.getByTestId('toggle-button');
|
||||
expect(button).toHaveTextContent('Switch to Camera Mode');
|
||||
});
|
||||
|
||||
it('shows the correct text if user should switch to camera with pending media access', async () => {
|
||||
contextValue.optimizelyExperimentName = 'test';
|
||||
contextValue.mediaAccess = 'pending';
|
||||
contextValue.shouldUseCamera = false;
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlCollapsible {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
|
||||
const titleText = screen.getByText('Use Your Camera Instead');
|
||||
expect(titleText).toBeInTheDocument();
|
||||
const helpText = screen.getByTestId('help-text');
|
||||
expect(helpText.textContent).toContain('If you are having trouble uploading a photo above');
|
||||
const accessLink = screen.getByTestId('access-link');
|
||||
fireEvent.click(accessLink);
|
||||
expect(history.location.pathname).toEqual('/request-camera-access');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Router } from 'react-router-dom';
|
||||
import { createMemoryHistory } from 'history';
|
||||
import {
|
||||
render, cleanup, act, screen, fireEvent,
|
||||
} from '@testing-library/react';
|
||||
import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import IdVerificationContext from '../../IdVerificationContext';
|
||||
import ChooseModePanel from '../../panels/ChooseModePanel';
|
||||
|
||||
jest.mock('@edx/frontend-platform/analytics', () => ({
|
||||
sendTrackEvent: jest.fn(),
|
||||
}));
|
||||
|
||||
const IntlChooseModePanel = injectIntl(ChooseModePanel);
|
||||
|
||||
const history = createMemoryHistory();
|
||||
|
||||
describe('ChooseModePanel', () => {
|
||||
const defaultProps = {
|
||||
intl: {},
|
||||
};
|
||||
|
||||
const contextValue = {
|
||||
optimizelyExperimentName: 'test',
|
||||
shouldUseCamera: false,
|
||||
reachedSummary: false,
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it('renders correctly', async () => {
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlChooseModePanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
|
||||
// check that radio button for upload is selected
|
||||
const uploadRadioButton = await screen.findByLabelText('Upload photos from my device');
|
||||
expect(uploadRadioButton).toBeChecked();
|
||||
|
||||
// check that if upload is selected, next button goes to correct screen
|
||||
const nextButton = await screen.findByTestId('next-button');
|
||||
expect(nextButton.getAttribute('href')).toEqual('/take-portrait-photo');
|
||||
});
|
||||
|
||||
it('renders correctly if user wants to use camera', async () => {
|
||||
contextValue.shouldUseCamera = true;
|
||||
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlChooseModePanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
|
||||
// check that radio button for camera is selected
|
||||
const cameraRadioButton = await screen.findByLabelText('Take pictures using my camera');
|
||||
expect(cameraRadioButton).toBeChecked();
|
||||
|
||||
// check that if upload is selected, next button goes to correct screen
|
||||
const nextButton = await screen.findByTestId('next-button');
|
||||
expect(nextButton.getAttribute('href')).toEqual('/request-camera-access');
|
||||
});
|
||||
|
||||
it('reroutes correctly if reachedSummary is true', async () => {
|
||||
contextValue.shouldUseCamera = true;
|
||||
contextValue.reachedSummary = true;
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlChooseModePanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const nextButton = await screen.findByTestId('next-button');
|
||||
fireEvent.click(nextButton);
|
||||
expect(history.location.pathname).toEqual('/request-camera-access');
|
||||
});
|
||||
|
||||
it('redirects if user is not part of experiment', async () => {
|
||||
contextValue.optimizelyExperimentName = '';
|
||||
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlChooseModePanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
|
||||
// check that radio button is not in document
|
||||
const cameraRadioButton = await screen.queryByLabelText('Take pictures using my camera');
|
||||
expect(cameraRadioButton).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -23,7 +23,6 @@ describe('IdContextPanel', () => {
|
||||
};
|
||||
|
||||
const contextValue = {
|
||||
optimizelyExperimentName: '',
|
||||
facePhotoFile: 'test.jpg',
|
||||
reachedSummary: false,
|
||||
};
|
||||
@@ -62,33 +61,4 @@ describe('IdContextPanel', () => {
|
||||
fireEvent.click(button);
|
||||
expect(history.location.pathname).toEqual('/take-id-photo');
|
||||
});
|
||||
|
||||
it('does not show help text for photo upload if not part of experiment', async () => {
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlIdContextPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const title = await screen.queryByText('What if I want to upload a photo instead?');
|
||||
expect(title).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows help text for photo upload if part of experiment', async () => {
|
||||
contextValue.optimizelyExperimentName = 'test';
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlIdContextPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const title = await screen.queryByText('What if I want to upload a photo instead?');
|
||||
expect(title).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,10 +22,7 @@ describe('PortraitPhotoContextPanel', () => {
|
||||
intl: {},
|
||||
};
|
||||
|
||||
const contextValue = {
|
||||
optimizelyExperimentName: '',
|
||||
reachedSummary: false,
|
||||
};
|
||||
const contextValue = { reachedSummary: false };
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
@@ -61,33 +58,4 @@ describe('PortraitPhotoContextPanel', () => {
|
||||
fireEvent.click(button);
|
||||
expect(history.location.pathname).toEqual('/take-portrait-photo');
|
||||
});
|
||||
|
||||
it('does not show help text for photo upload if not part of experiment', async () => {
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlPortraitPhotoContextPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const title = await screen.queryByText('What if I want to upload a photo instead?');
|
||||
expect(title).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows help text for photo upload if part of experiment', async () => {
|
||||
contextValue.optimizelyExperimentName = 'test';
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlPortraitPhotoContextPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const title = await screen.queryByText('What if I want to upload a photo instead?');
|
||||
expect(title).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@ import { createMemoryHistory } from 'history';
|
||||
import {
|
||||
render, screen, cleanup, act, fireEvent,
|
||||
} from '@testing-library/react';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import IdVerificationContext from '../../IdVerificationContext';
|
||||
import RequestCameraAccessPanel from '../../panels/RequestCameraAccessPanel';
|
||||
@@ -84,27 +85,6 @@ describe('RequestCameraAccessPanel', () => {
|
||||
expect(text).toHaveTextContent(/It looks like we're unable to access your camera./);
|
||||
});
|
||||
|
||||
it('renders correctly with media access denied in optimizely experiment', async () => {
|
||||
contextValue.mediaAccess = 'denied';
|
||||
contextValue.optimizelyExperimentName = 'test';
|
||||
Bowser.parse = jest.fn().mockReturnValue({ browser: { name: '' } });
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlRequestCameraAccessPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const text = await screen.findByTestId('camera-access-failure');
|
||||
expect(text).toHaveTextContent(/It looks like we're unable to access your camera./);
|
||||
const nextButton = await screen.findByText('Continue with Upload');
|
||||
fireEvent.click(nextButton);
|
||||
expect(history.location.pathname).toEqual('/take-portrait-photo');
|
||||
contextValue.optimizelyExperimentName = '';
|
||||
});
|
||||
|
||||
it('renders correctly with media access unsupported with Chrome browser', async () => {
|
||||
contextValue.mediaAccess = 'unsupported';
|
||||
Bowser.parse = jest.fn().mockReturnValue({ browser: { name: 'Chrome' } });
|
||||
@@ -203,9 +183,25 @@ describe('RequestCameraAccessPanel', () => {
|
||||
expect(text).toHaveTextContent(/Open the Flash Player/);
|
||||
});
|
||||
|
||||
it('reroutes correctly to portrait context', async () => {
|
||||
it('routes to dashboard when camera access is denied', async () => {
|
||||
contextValue.mediaAccess = 'denied';
|
||||
|
||||
Bowser.parse = jest.fn().mockReturnValue({ browser: { name: '' } });
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlRequestCameraAccessPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const button = await screen.findByRole('link');
|
||||
expect(button).toHaveAttribute('href', `${getConfig().LMS_BASE_URL}/dashboard`);
|
||||
});
|
||||
|
||||
it('routes correctly to portrait context', async () => {
|
||||
contextValue.mediaAccess = 'granted';
|
||||
history.location.state = { fromPortraitCapture: true };
|
||||
|
||||
Bowser.parse = jest.fn().mockReturnValue({ browser: { name: '' } });
|
||||
await act(async () => render((
|
||||
@@ -221,105 +217,4 @@ describe('RequestCameraAccessPanel', () => {
|
||||
fireEvent.click(button);
|
||||
expect(history.location.pathname).toEqual('/portrait-photo-context');
|
||||
});
|
||||
|
||||
it('reroutes correctly to ID context', async () => {
|
||||
contextValue.mediaAccess = 'granted';
|
||||
history.location.state = { fromIdCapture: true };
|
||||
|
||||
Bowser.parse = jest.fn().mockReturnValue({ browser: { name: '' } });
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlRequestCameraAccessPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const button = await screen.findByTestId('next-button');
|
||||
fireEvent.click(button);
|
||||
expect(history.location.pathname).toEqual('/id-context');
|
||||
});
|
||||
|
||||
it('reroutes to portrait context when reachedSummary is true', async () => {
|
||||
contextValue.mediaAccess = 'granted';
|
||||
contextValue.reachedSummary = true;
|
||||
history.location.state = { fromPortraitCapture: true };
|
||||
|
||||
Bowser.parse = jest.fn().mockReturnValue({ browser: { name: '' } });
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlRequestCameraAccessPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const button = await screen.findByTestId('next-button');
|
||||
fireEvent.click(button);
|
||||
expect(history.location.pathname).toEqual('/portrait-photo-context');
|
||||
});
|
||||
|
||||
it('reroutes to ID context when reachedSummary is true', async () => {
|
||||
contextValue.mediaAccess = 'granted';
|
||||
contextValue.reachedSummary = true;
|
||||
history.location.state = { fromIdCapture: true };
|
||||
|
||||
Bowser.parse = jest.fn().mockReturnValue({ browser: { name: '' } });
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlRequestCameraAccessPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const button = await screen.findByTestId('next-button');
|
||||
fireEvent.click(button);
|
||||
expect(history.location.pathname).toEqual('/id-context');
|
||||
});
|
||||
|
||||
it('reroutes correctly to portrait context with no media access', async () => {
|
||||
contextValue.mediaAccess = 'denied';
|
||||
contextValue.optimizelyExperimentName = 'test';
|
||||
history.location.state = { fromPortraitCapture: true };
|
||||
|
||||
Bowser.parse = jest.fn().mockReturnValue({ browser: { name: '' } });
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlRequestCameraAccessPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const button = await screen.findByTestId('next-button');
|
||||
fireEvent.click(button);
|
||||
expect(history.location.pathname).toEqual('/take-portrait-photo');
|
||||
contextValue.optimizelyExperimentName = '';
|
||||
});
|
||||
|
||||
it('reroutes correctly to ID context with no media access', async () => {
|
||||
contextValue.mediaAccess = 'denied';
|
||||
contextValue.optimizelyExperimentName = 'test';
|
||||
history.location.state = { fromIdCapture: true };
|
||||
|
||||
Bowser.parse = jest.fn().mockReturnValue({ browser: { name: '' } });
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlRequestCameraAccessPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
const button = await screen.findByTestId('next-button');
|
||||
fireEvent.click(button);
|
||||
expect(history.location.pathname).toEqual('/take-id-photo');
|
||||
contextValue.optimizelyExperimentName = '';
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,7 +22,7 @@ describe('ReviewRequirementsPanel', () => {
|
||||
intl: {},
|
||||
};
|
||||
|
||||
const context = { setOptimizelyExperimentName: jest.fn() };
|
||||
const context = {};
|
||||
|
||||
const getPanel = async () => {
|
||||
await act(async () => render((
|
||||
@@ -47,13 +47,6 @@ describe('ReviewRequirementsPanel', () => {
|
||||
expect(history.location.pathname).toEqual('/request-camera-access');
|
||||
});
|
||||
|
||||
it('updates optimizely experiment name in context', async () => {
|
||||
window.experimentVariables = {};
|
||||
window.experimentVariables.experimentName = 'test-experiment';
|
||||
await getPanel();
|
||||
expect(context.setOptimizelyExperimentName).toHaveBeenCalledWith('test-experiment');
|
||||
});
|
||||
|
||||
it('displays an alert if the user\'s account information is managed by a third party', async () => {
|
||||
context.profileDataManager = 'test-org';
|
||||
await getPanel();
|
||||
|
||||
@@ -32,9 +32,6 @@ describe('SummaryPanel', () => {
|
||||
idPhotoFile: 'test.jpg',
|
||||
nameOnAccount: 'test name',
|
||||
idPhotoName: 'test name',
|
||||
optimizelyExperimentName: 'test-experiment',
|
||||
portraitPhotoMode: 'camera',
|
||||
idPhotoMode: 'upload',
|
||||
stopUserMedia: jest.fn(),
|
||||
setReachedSummary: jest.fn(),
|
||||
};
|
||||
@@ -76,13 +73,11 @@ describe('SummaryPanel', () => {
|
||||
});
|
||||
|
||||
it('allows user to upload ID photo', async () => {
|
||||
appContextValue.optimizelyExperimentName = '';
|
||||
await getPanel();
|
||||
const collapsible = await screen.getAllByRole('button', { 'aria-expanded': false })[0];
|
||||
fireEvent.click(collapsible);
|
||||
const uploadButton = await screen.getByTestId('fileUpload');
|
||||
expect(uploadButton).toBeVisible();
|
||||
appContextValue.optimizelyExperimentName = 'test-experiment';
|
||||
});
|
||||
|
||||
it('displays warning if account is managed by a third party', async () => {
|
||||
@@ -97,9 +92,6 @@ describe('SummaryPanel', () => {
|
||||
facePhotoFile: appContextValue.facePhotoFile,
|
||||
idPhotoFile: appContextValue.idPhotoFile,
|
||||
idPhotoName: appContextValue.idPhotoName,
|
||||
optimizelyExperimentName: appContextValue.optimizelyExperimentName,
|
||||
portraitPhotoMode: appContextValue.portraitPhotoMode,
|
||||
idPhotoMode: appContextValue.idPhotoMode,
|
||||
courseRunKey: null,
|
||||
};
|
||||
await getPanel();
|
||||
@@ -114,9 +106,6 @@ describe('SummaryPanel', () => {
|
||||
const verificationData = {
|
||||
facePhotoFile: appContextValue.facePhotoFile,
|
||||
idPhotoFile: appContextValue.idPhotoFile,
|
||||
portraitPhotoMode: appContextValue.portraitPhotoMode,
|
||||
idPhotoMode: appContextValue.idPhotoMode,
|
||||
optimizelyExperimentName: appContextValue.optimizelyExperimentName,
|
||||
courseRunKey: null,
|
||||
idPhotoName: appContextValue.nameOnAccount,
|
||||
};
|
||||
@@ -131,9 +120,6 @@ describe('SummaryPanel', () => {
|
||||
const verificationData = {
|
||||
facePhotoFile: appContextValue.facePhotoFile,
|
||||
idPhotoFile: appContextValue.idPhotoFile,
|
||||
portraitPhotoMode: appContextValue.portraitPhotoMode,
|
||||
idPhotoMode: appContextValue.idPhotoMode,
|
||||
optimizelyExperimentName: appContextValue.optimizelyExperimentName,
|
||||
courseRunKey: null,
|
||||
idPhotoName: appContextValue.nameOnAccount,
|
||||
};
|
||||
@@ -208,11 +194,4 @@ describe('SummaryPanel', () => {
|
||||
'One or more of the files you have uploaded is in an unsupported format. Please choose from the following:',
|
||||
);
|
||||
});
|
||||
|
||||
it('does not show ID upload option if user is in experiment', async () => {
|
||||
await getPanel();
|
||||
const collapsible = await screen.queryByTestId('collapsible');
|
||||
expect(collapsible).not.toBeInTheDocument();
|
||||
appContextValue.optimizelyExperimentName = 'test-experiment';
|
||||
});
|
||||
});
|
||||
|
||||
@@ -28,6 +28,7 @@ describe('TakeIdPhotoPanel', () => {
|
||||
idPhotoFile: null,
|
||||
reachedSummary: false,
|
||||
setIdPhotoFile: jest.fn(),
|
||||
useCameraForId: false,
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
@@ -83,9 +84,6 @@ describe('TakeIdPhotoPanel', () => {
|
||||
});
|
||||
|
||||
it('shows correct text if user should use upload', async () => {
|
||||
contextValue.optimizelyExperimentName = 'test';
|
||||
contextValue.shouldUseCamera = false;
|
||||
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
|
||||
@@ -28,7 +28,6 @@ describe('TakePortraitPhotoPanel', () => {
|
||||
idPhotoFile: null,
|
||||
reachedSummary: false,
|
||||
setFacePhotoFile: jest.fn(),
|
||||
setShouldUseCamera: jest.fn(),
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
@@ -51,7 +50,6 @@ describe('TakePortraitPhotoPanel', () => {
|
||||
|
||||
it('shows next button after photo is taken and routes to IdContextPanel', async () => {
|
||||
contextValue.facePhotoFile = 'test.jpg';
|
||||
contextValue.shouldUseCamera = true;
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
@@ -84,26 +82,4 @@ describe('TakePortraitPhotoPanel', () => {
|
||||
fireEvent.click(button);
|
||||
expect(history.location.pathname).toEqual('/summary');
|
||||
});
|
||||
|
||||
it('shows correct text if user should use upload', async () => {
|
||||
contextValue.optimizelyExperimentName = 'test';
|
||||
contextValue.shouldUseCamera = false;
|
||||
|
||||
await act(async () => render((
|
||||
<Router history={history}>
|
||||
<IntlProvider locale="en">
|
||||
<IdVerificationContext.Provider value={contextValue}>
|
||||
<IntlTakePortraitPhotoPanel {...defaultProps} />
|
||||
</IdVerificationContext.Provider>
|
||||
</IntlProvider>
|
||||
</Router>
|
||||
)));
|
||||
|
||||
// check that upload title and text are correct
|
||||
const title = await screen.findByText('Upload a Photo of Yourself');
|
||||
expect(title).toBeVisible();
|
||||
|
||||
const text = await screen.findByTestId('upload-text');
|
||||
expect(text.textContent).toContain('Please upload a portrait photo');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user