Compare commits
29 Commits
fixing-san
...
open-relea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ad8be560b4 | ||
|
|
5dea66540d | ||
|
|
400648f09f | ||
|
|
f781f311a2 | ||
|
|
9469adc386 | ||
|
|
691a248477 | ||
|
|
4bf0021982 | ||
|
|
b04257a62d | ||
|
|
bce8aa712d | ||
|
|
25bc11ea8d | ||
|
|
bcece267bc | ||
|
|
1575034ed3 | ||
|
|
922f1e2ac4 | ||
|
|
b03fe545ba | ||
|
|
47fce8aa7d | ||
|
|
5fd6754025 | ||
|
|
428372d198 | ||
|
|
4f83726387 | ||
|
|
40cb949d4e | ||
|
|
daa54be1c8 | ||
|
|
b126635981 | ||
|
|
94e1207d37 | ||
|
|
9ec6e1cf05 | ||
|
|
eddce35a37 | ||
|
|
38cbd4d0d8 | ||
|
|
b962b3f4ca | ||
|
|
ad9b40d9e9 | ||
|
|
7990fe7483 | ||
|
|
def1415f14 |
2
.env
2
.env
@@ -26,3 +26,5 @@ ENABLE_PROGRESSIVE_PROFILING=''
|
||||
MARKETING_EMAILS_OPT_IN=''
|
||||
ENABLE_COPPA_COMPLIANCE=''
|
||||
SHOW_DYNAMIC_PROFILING_PAGE=''
|
||||
ZENDESK_KEY=''
|
||||
ZENDESK_LOGO_URL=''
|
||||
|
||||
@@ -31,3 +31,5 @@ DISABLE_ENTERPRISE_LOGIN=''
|
||||
REGISTER_CONVERSION_COOKIE_NAME='openedx-user-register-conversion'
|
||||
ENABLE_COPPA_COMPLIANCE=''
|
||||
MARKETING_EMAILS_OPT_IN=''
|
||||
ZENDESK_KEY=''
|
||||
ZENDESK_LOGO_URL=''
|
||||
|
||||
@@ -24,3 +24,5 @@ DISABLE_ENTERPRISE_LOGIN=''
|
||||
REGISTER_CONVERSION_COOKIE_NAME='openedx-user-register-conversion'
|
||||
MARKETING_EMAILS_OPT_IN=''
|
||||
ENABLE_COPPA_COMPLIANCE=''
|
||||
ZENDESK_KEY=''
|
||||
ZENDESK_LOGO_URL=''
|
||||
|
||||
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
node: [12, 14, 16]
|
||||
node: [16]
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
13
.github/workflows/lockfileversion-check.yml
vendored
Normal file
13
.github/workflows/lockfileversion-check.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
#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
|
||||
@@ -5,5 +5,4 @@ nick: Authn MFE
|
||||
oeps: {}
|
||||
owner: edx/vanguards
|
||||
openedx-release:
|
||||
maybe: true # Delete this "maybe" line when you have decided about Open edX inclusion.
|
||||
ref: master
|
||||
|
||||
173
package-lock.json
generated
173
package-lock.json
generated
@@ -47,11 +47,11 @@
|
||||
"regenerator-runtime": "0.13.9",
|
||||
"reselect": "4.1.5",
|
||||
"sanitize-html": "2.7.0",
|
||||
"semver-regex": "3.1.3",
|
||||
"semver-regex": "3.1.4",
|
||||
"universal-cookie": "4.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@edx/frontend-build": "9.1.2",
|
||||
"@edx/frontend-build": "9.1.4",
|
||||
"@edx/reactifex": "1.0.3",
|
||||
"babel-plugin-formatjs": "10.3.18",
|
||||
"codecov": "3.8.2",
|
||||
@@ -2005,9 +2005,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/frontend-build": {
|
||||
"version": "9.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-build/-/frontend-build-9.1.2.tgz",
|
||||
"integrity": "sha512-qC7ReTYGEiAfd1WMMEKR2JnO6/kvTh80COOqA4CslV8l6kchMP7pw9dVPDj+/njWFhK/sn9hoi+Ro4wlcylwyg==",
|
||||
"version": "9.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-build/-/frontend-build-9.1.4.tgz",
|
||||
"integrity": "sha512-Lw4EqZ8iMnIDLA/xlAEByPzzoyU9faStktYgnRGhPM9DPyQ504NQ0I/qv7vX6qWJ2wII458TNAgGzu9zr2hHWQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/cli": "7.16.0",
|
||||
@@ -2051,8 +2051,8 @@
|
||||
"react-dev-utils": "12.0.0",
|
||||
"react-refresh": "0.10.0",
|
||||
"resolve-url-loader": "5.0.0-beta.1",
|
||||
"sass": "1.26.11",
|
||||
"sass-loader": "10.0.5",
|
||||
"sass": "1.49.9",
|
||||
"sass-loader": "12.6.0",
|
||||
"source-map-loader": "0.2.4",
|
||||
"style-loader": "2.0.0",
|
||||
"url-loader": "4.1.1",
|
||||
@@ -7063,9 +7063,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/async": {
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
||||
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
|
||||
"version": "2.6.4",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
|
||||
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.14"
|
||||
@@ -14692,6 +14692,12 @@
|
||||
"url": "https://opencollective.com/immer"
|
||||
}
|
||||
},
|
||||
"node_modules/immutable": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz",
|
||||
"integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/import-fresh": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||
@@ -24212,34 +24218,33 @@
|
||||
}
|
||||
},
|
||||
"node_modules/sass": {
|
||||
"version": "1.26.11",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.26.11.tgz",
|
||||
"integrity": "sha512-W1l/+vjGjIamsJ6OnTe0K37U2DBO/dgsv2Z4c89XQ8ZOO6l/VwkqwLSqoYzJeJs6CLuGSTRWc91GbQFL3lvrvw==",
|
||||
"version": "1.49.9",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.49.9.tgz",
|
||||
"integrity": "sha512-YlYWkkHP9fbwaFRZQRXgDi3mXZShslVmmo+FVK3kHLUELHHEYrCmL1x6IUjC7wLS6VuJSAFXRQS/DxdsC4xL1A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"chokidar": ">=2.0.0 <4.0.0"
|
||||
"chokidar": ">=3.0.0 <4.0.0",
|
||||
"immutable": "^4.0.0",
|
||||
"source-map-js": ">=0.6.2 <2.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"sass": "sass.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.9.0"
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/sass-loader": {
|
||||
"version": "10.0.5",
|
||||
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.0.5.tgz",
|
||||
"integrity": "sha512-2LqoNPtKkZq/XbXNQ4C64GFEleSEHKv6NPSI+bMC/l+jpEXGJhiRYkAQToO24MR7NU4JRY2RpLpJ/gjo2Uf13w==",
|
||||
"version": "12.6.0",
|
||||
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz",
|
||||
"integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"klona": "^2.0.4",
|
||||
"loader-utils": "^2.0.0",
|
||||
"neo-async": "^2.6.2",
|
||||
"schema-utils": "^3.0.0",
|
||||
"semver": "^7.3.2"
|
||||
"neo-async": "^2.6.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10.13.0"
|
||||
"node": ">= 12.13.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
@@ -24247,9 +24252,10 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"fibers": ">= 3.1.0",
|
||||
"node-sass": "^4.0.0 || ^5.0.0",
|
||||
"node-sass": "^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0",
|
||||
"sass": "^1.3.0",
|
||||
"webpack": "^4.36.0 || ^5.0.0"
|
||||
"sass-embedded": "*",
|
||||
"webpack": "^5.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"fibers": {
|
||||
@@ -24260,42 +24266,12 @@
|
||||
},
|
||||
"sass": {
|
||||
"optional": true
|
||||
},
|
||||
"sass-embedded": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/sass-loader/node_modules/lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"yallist": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/sass-loader/node_modules/semver": {
|
||||
"version": "7.3.5",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
|
||||
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"lru-cache": "^6.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/sass-loader/node_modules/yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/saxes": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
|
||||
@@ -24387,9 +24363,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/semver-regex": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.3.tgz",
|
||||
"integrity": "sha512-Aqi54Mk9uYTjVexLnR67rTyBusmwd04cLkHy9hNvk3+G3nT2Oyg7E0l4XVbOaNwIvQ3hHeYxGcyEy+mKreyBFQ==",
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.4.tgz",
|
||||
"integrity": "sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA==",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
@@ -29482,9 +29458,9 @@
|
||||
"requires": {}
|
||||
},
|
||||
"@edx/frontend-build": {
|
||||
"version": "9.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-build/-/frontend-build-9.1.2.tgz",
|
||||
"integrity": "sha512-qC7ReTYGEiAfd1WMMEKR2JnO6/kvTh80COOqA4CslV8l6kchMP7pw9dVPDj+/njWFhK/sn9hoi+Ro4wlcylwyg==",
|
||||
"version": "9.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@edx/frontend-build/-/frontend-build-9.1.4.tgz",
|
||||
"integrity": "sha512-Lw4EqZ8iMnIDLA/xlAEByPzzoyU9faStktYgnRGhPM9DPyQ504NQ0I/qv7vX6qWJ2wII458TNAgGzu9zr2hHWQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/cli": "7.16.0",
|
||||
@@ -29528,8 +29504,8 @@
|
||||
"react-dev-utils": "12.0.0",
|
||||
"react-refresh": "0.10.0",
|
||||
"resolve-url-loader": "5.0.0-beta.1",
|
||||
"sass": "1.26.11",
|
||||
"sass-loader": "10.0.5",
|
||||
"sass": "1.49.9",
|
||||
"sass-loader": "12.6.0",
|
||||
"source-map-loader": "0.2.4",
|
||||
"style-loader": "2.0.0",
|
||||
"url-loader": "4.1.1",
|
||||
@@ -33486,9 +33462,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"async": {
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
||||
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
|
||||
"version": "2.6.4",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz",
|
||||
"integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lodash": "^4.17.14"
|
||||
@@ -39416,6 +39392,12 @@
|
||||
"integrity": "sha512-lk7UNmSbAukB5B6dh9fnh5D0bJTOFKxVg2cyJWTYrWRfhLrLMBquONcUs3aFq507hNoIZEDDh8lb8UtOizSMhA==",
|
||||
"dev": true
|
||||
},
|
||||
"immutable": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0.tgz",
|
||||
"integrity": "sha512-zIE9hX70qew5qTUjSS7wi1iwj/l7+m54KWU247nhM3v806UdGj1yDndXj+IOYxxtW9zyLI+xqFNZjTuDaLUqFw==",
|
||||
"dev": true
|
||||
},
|
||||
"import-fresh": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
|
||||
@@ -46630,51 +46612,24 @@
|
||||
}
|
||||
},
|
||||
"sass": {
|
||||
"version": "1.26.11",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.26.11.tgz",
|
||||
"integrity": "sha512-W1l/+vjGjIamsJ6OnTe0K37U2DBO/dgsv2Z4c89XQ8ZOO6l/VwkqwLSqoYzJeJs6CLuGSTRWc91GbQFL3lvrvw==",
|
||||
"version": "1.49.9",
|
||||
"resolved": "https://registry.npmjs.org/sass/-/sass-1.49.9.tgz",
|
||||
"integrity": "sha512-YlYWkkHP9fbwaFRZQRXgDi3mXZShslVmmo+FVK3kHLUELHHEYrCmL1x6IUjC7wLS6VuJSAFXRQS/DxdsC4xL1A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chokidar": ">=2.0.0 <4.0.0"
|
||||
"chokidar": ">=3.0.0 <4.0.0",
|
||||
"immutable": "^4.0.0",
|
||||
"source-map-js": ">=0.6.2 <2.0.0"
|
||||
}
|
||||
},
|
||||
"sass-loader": {
|
||||
"version": "10.0.5",
|
||||
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-10.0.5.tgz",
|
||||
"integrity": "sha512-2LqoNPtKkZq/XbXNQ4C64GFEleSEHKv6NPSI+bMC/l+jpEXGJhiRYkAQToO24MR7NU4JRY2RpLpJ/gjo2Uf13w==",
|
||||
"version": "12.6.0",
|
||||
"resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-12.6.0.tgz",
|
||||
"integrity": "sha512-oLTaH0YCtX4cfnJZxKSLAyglED0naiYfNG1iXfU5w1LNZ+ukoA5DtyDIN5zmKVZwYNJP4KRc5Y3hkWga+7tYfA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"klona": "^2.0.4",
|
||||
"loader-utils": "^2.0.0",
|
||||
"neo-async": "^2.6.2",
|
||||
"schema-utils": "^3.0.0",
|
||||
"semver": "^7.3.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"lru-cache": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"yallist": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "7.3.5",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz",
|
||||
"integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lru-cache": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"yallist": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
|
||||
"dev": true
|
||||
}
|
||||
"neo-async": "^2.6.2"
|
||||
}
|
||||
},
|
||||
"saxes": {
|
||||
@@ -46750,9 +46705,9 @@
|
||||
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
|
||||
},
|
||||
"semver-regex": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.3.tgz",
|
||||
"integrity": "sha512-Aqi54Mk9uYTjVexLnR67rTyBusmwd04cLkHy9hNvk3+G3nT2Oyg7E0l4XVbOaNwIvQ3hHeYxGcyEy+mKreyBFQ=="
|
||||
"version": "3.1.4",
|
||||
"resolved": "https://registry.npmjs.org/semver-regex/-/semver-regex-3.1.4.tgz",
|
||||
"integrity": "sha512-6IiqeZNgq01qGf0TId0t3NvKzSvUsjcpdEO3AQNeIjR6A2+ckTnQlDpl4qu1bjRv0RzN3FP9hzFmws3lKqRWkA=="
|
||||
},
|
||||
"semver-truncate": {
|
||||
"version": "1.1.2",
|
||||
|
||||
@@ -72,11 +72,11 @@
|
||||
"regenerator-runtime": "0.13.9",
|
||||
"reselect": "4.1.5",
|
||||
"sanitize-html": "2.7.0",
|
||||
"semver-regex": "3.1.3",
|
||||
"semver-regex": "3.1.4",
|
||||
"universal-cookie": "4.0.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@edx/frontend-build": "9.1.2",
|
||||
"@edx/frontend-build": "9.1.4",
|
||||
"@edx/reactifex": "1.0.3",
|
||||
"babel-plugin-formatjs": "10.3.18",
|
||||
"codecov": "3.8.2",
|
||||
|
||||
@@ -14,6 +14,58 @@
|
||||
src="<%= process.env.MARKETING_SITE_BASE_URL %>/optimizelyjs/<%= process.env.OPTIMIZELY_PROJECT_ID %>.js"
|
||||
></script>
|
||||
<% } %>
|
||||
<% if (process.env.ZENDESK_KEY) { %>
|
||||
<script
|
||||
id="ze-snippet"
|
||||
src="https://static.zdassets.com/ekr/snippet.js?key=<%= process.env.ZENDESK_KEY %>"
|
||||
>
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
window.zESettings = {
|
||||
cookies: true,
|
||||
webWidget: {
|
||||
contactOptions: {
|
||||
enabled: false,
|
||||
},
|
||||
chat: {
|
||||
suppress: false,
|
||||
},
|
||||
contactForm: {
|
||||
ticketForms: [
|
||||
{
|
||||
id: 360003368814,
|
||||
subject: false,
|
||||
fields: [
|
||||
{
|
||||
id: 'description',
|
||||
prefill: {
|
||||
'*': '',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
selectTicketForm: {
|
||||
'*': 'Please choose your request type:',
|
||||
},
|
||||
attachments: true,
|
||||
},
|
||||
helpCenter: {
|
||||
originalArticleButton: true,
|
||||
},
|
||||
answerBot: {
|
||||
suppress: false,
|
||||
contactOnlyAfterQuery: true,
|
||||
title: { '*': 'edX Support' },
|
||||
avatar: {
|
||||
url: '<%= process.env.ZENDESK_LOGO_URL %>',
|
||||
name: { '*': 'edX Support' },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
<% } %>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -37,12 +37,12 @@ $accent-a-light: #c9f2f5;
|
||||
width: 12rem;
|
||||
}
|
||||
|
||||
.stateful-button-variation1-width {
|
||||
width: 16.4rem;
|
||||
.register-stateful-button-width {
|
||||
min-width: 14.4rem;
|
||||
}
|
||||
|
||||
.login-button-width {
|
||||
width: 6rem;
|
||||
min-width: 6rem;
|
||||
}
|
||||
|
||||
.tpa-skeleton {
|
||||
@@ -624,6 +624,7 @@ select.form-control {
|
||||
position: absolute;
|
||||
background-color: #fff;
|
||||
width: 464px;
|
||||
z-index: 100 !important;
|
||||
}
|
||||
|
||||
.email-error-alert {
|
||||
@@ -857,7 +858,6 @@ select.form-control {
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
margin-top: 1rem;
|
||||
margin-left: 3px;
|
||||
}
|
||||
.suggested-username {
|
||||
@@ -866,7 +866,7 @@ select.form-control {
|
||||
margin-left: 15px;
|
||||
}
|
||||
.suggested-username-close-button {
|
||||
right: 0;
|
||||
right: 1rem;
|
||||
position: absolute;
|
||||
}
|
||||
.suggested-username-with-error {
|
||||
@@ -881,3 +881,7 @@ select.form-control {
|
||||
overflow-x: auto;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.pgn__form-control-decorator-trailing {
|
||||
right: 0 !important;
|
||||
}
|
||||
|
||||
@@ -20,51 +20,53 @@ const LargeLeftLayout = (props) => {
|
||||
new ClipboardJS('.copyIcon'); // eslint-disable-line no-new
|
||||
|
||||
return (
|
||||
<div className="min-vh-100 pr-0 mt-lg-n2 d-flex align-items-center">
|
||||
<Toast
|
||||
onClose={() => setToastShow(false)}
|
||||
show={showToast}
|
||||
>
|
||||
{intl.formatMessage(messages['code.copied'])}
|
||||
</Toast>
|
||||
<svg className={classNames(
|
||||
'large-screen-svg-line',
|
||||
{
|
||||
'variation1-bar-color mt-n6 pt-0 ml-5': experimentName === 'variation1' && isRegistrationPage,
|
||||
'variation2-bar-color': experimentName === 'variation2' && isRegistrationPage,
|
||||
'ml-5': experimentName !== 'variation1' || !isRegistrationPage,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<line x1="50" y1="0" x2="10" y2="215" />
|
||||
</svg>
|
||||
<div className={classNames({ 'pl-4': experimentName === 'variation1' && isRegistrationPage })}>
|
||||
<h1 className={classNames('large-heading', { 'mb-4.5': experimentName === 'variation1' && isRegistrationPage })}>
|
||||
{intl.formatMessage(messages['start.learning'])}
|
||||
<span
|
||||
className={((experimentName === 'variation1' || experimentName === 'variation2') && isRegistrationPage) ? 'text-accent-b' : 'text-accent-a'}
|
||||
>
|
||||
<br />
|
||||
{intl.formatMessage(messages['with.site.name'], { siteName: getConfig().SITE_NAME })}
|
||||
</span>
|
||||
</h1>
|
||||
{experimentName === 'variation1' && isRegistrationPage ? (
|
||||
<span className="text-light-300 dicount-heading">
|
||||
<span className="lead mr-3">
|
||||
<SideDiscountBanner />
|
||||
<div className="min-vh-100 d-flex justify-content-left align-items-center">
|
||||
<div className="d-flex pr-0 mt-lg-n2">
|
||||
<Toast
|
||||
onClose={() => setToastShow(false)}
|
||||
show={showToast}
|
||||
>
|
||||
{intl.formatMessage(messages['code.copied'])}
|
||||
</Toast>
|
||||
<svg className={classNames(
|
||||
'large-screen-svg-line',
|
||||
{
|
||||
'variation1-bar-color mt-n6 pt-0 ml-5': experimentName === 'variation1' && isRegistrationPage,
|
||||
'variation2-bar-color': experimentName === 'variation2' && isRegistrationPage,
|
||||
'ml-5': experimentName !== 'variation1' || !isRegistrationPage,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<line x1="50" y1="0" x2="10" y2="215" />
|
||||
</svg>
|
||||
<div className={classNames({ 'pl-4': experimentName === 'variation1' && isRegistrationPage })}>
|
||||
<h1 className={classNames('large-heading', { 'mb-4.5': experimentName === 'variation1' && isRegistrationPage })}>
|
||||
{intl.formatMessage(messages['start.learning'])}
|
||||
<span
|
||||
className={((experimentName === 'variation1' || experimentName === 'variation2') && isRegistrationPage) ? 'text-accent-b' : 'text-accent-a'}
|
||||
>
|
||||
<br />
|
||||
{intl.formatMessage(messages['with.site.name'], { siteName: getConfig().SITE_NAME })}
|
||||
</span>
|
||||
<span className="dashed-border d-inline-flex flex-wrap align-items-center">
|
||||
<span id="edx-welcome" className="text-white edx-welcome font-weight-bold mr-1">EDXWELCOME</span>
|
||||
<FontAwesomeIcon
|
||||
className="text-light-700 copyIcon ml-1.5 hover-discount-icon"
|
||||
icon={faCut}
|
||||
data-clipboard-action="copy"
|
||||
data-clipboard-target="#edx-welcome"
|
||||
onClick={() => setToastShow(true)}
|
||||
/>
|
||||
</h1>
|
||||
{experimentName === 'variation1' && isRegistrationPage ? (
|
||||
<span className="text-light-300 dicount-heading">
|
||||
<span className="lead mr-3">
|
||||
<SideDiscountBanner />
|
||||
</span>
|
||||
<span className="dashed-border d-inline-flex flex-wrap align-items-center">
|
||||
<span id="edx-welcome" className="text-white edx-welcome font-weight-bold mr-1">EDXWELCOME</span>
|
||||
<FontAwesomeIcon
|
||||
className="text-light-700 copyIcon ml-1.5 hover-discount-icon"
|
||||
icon={faCut}
|
||||
data-clipboard-action="copy"
|
||||
data-clipboard-target="#edx-welcome"
|
||||
onClick={() => setToastShow(true)}
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
) : null}
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -26,6 +26,7 @@ const FormGroup = (props) => {
|
||||
as={props.as}
|
||||
readOnly={props.readOnly}
|
||||
type={props.type}
|
||||
aria-invalid={props.errorMessage !== ''}
|
||||
className="form-field"
|
||||
autoComplete={props.autoComplete}
|
||||
name={props.name}
|
||||
|
||||
@@ -63,6 +63,7 @@ const PasswordField = (props) => {
|
||||
type={isPasswordHidden ? 'password' : 'text'}
|
||||
name={props.name}
|
||||
value={props.value}
|
||||
aria-invalid={props.errorMessage !== ''}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
onChange={props.handleChange}
|
||||
|
||||
@@ -12,9 +12,9 @@ export const getThirdPartyAuthContextBegin = () => ({
|
||||
type: THIRD_PARTY_AUTH_CONTEXT.BEGIN,
|
||||
});
|
||||
|
||||
export const getThirdPartyAuthContextSuccess = (thirdPartyAuthContext) => ({
|
||||
export const getThirdPartyAuthContextSuccess = (fieldDescriptions, thirdPartyAuthContext) => ({
|
||||
type: THIRD_PARTY_AUTH_CONTEXT.SUCCESS,
|
||||
payload: { thirdPartyAuthContext },
|
||||
payload: { fieldDescriptions, thirdPartyAuthContext },
|
||||
});
|
||||
|
||||
export const getThirdPartyAuthContextFailure = () => ({
|
||||
|
||||
@@ -3,6 +3,8 @@ import { THIRD_PARTY_AUTH_CONTEXT } from './actions';
|
||||
import { PENDING_STATE, COMPLETE_STATE } from '../../data/constants';
|
||||
|
||||
export const defaultState = {
|
||||
extendedProfile: [],
|
||||
fieldDescriptions: {},
|
||||
thirdPartyAuthApiStatus: null,
|
||||
};
|
||||
|
||||
@@ -16,6 +18,8 @@ const reducer = (state = defaultState, action) => {
|
||||
case THIRD_PARTY_AUTH_CONTEXT.SUCCESS:
|
||||
return {
|
||||
...state,
|
||||
extendedProfile: action.payload.fieldDescriptions.extendedProfile,
|
||||
fieldDescriptions: action.payload.fieldDescriptions.fields,
|
||||
thirdPartyAuthContext: action.payload.thirdPartyAuthContext,
|
||||
thirdPartyAuthApiStatus: COMPLETE_STATE,
|
||||
};
|
||||
|
||||
@@ -18,10 +18,10 @@ import {
|
||||
export function* fetchThirdPartyAuthContext(action) {
|
||||
try {
|
||||
yield put(getThirdPartyAuthContextBegin());
|
||||
const { thirdPartyAuthContext } = yield call(getThirdPartyAuthContext, action.payload.urlParams);
|
||||
const { fieldDescriptions, thirdPartyAuthContext } = yield call(getThirdPartyAuthContext, action.payload.urlParams);
|
||||
|
||||
yield put(getThirdPartyAuthContextSuccess(
|
||||
thirdPartyAuthContext,
|
||||
fieldDescriptions, thirdPartyAuthContext,
|
||||
));
|
||||
} catch (e) {
|
||||
yield put(getThirdPartyAuthContextFailure());
|
||||
|
||||
@@ -8,3 +8,13 @@ export const thirdPartyAuthContextSelector = createSelector(
|
||||
commonComponentsSelector,
|
||||
commonComponents => commonComponents.thirdPartyAuthContext,
|
||||
);
|
||||
|
||||
export const fieldDescriptionSelector = createSelector(
|
||||
commonComponentsSelector,
|
||||
commonComponents => commonComponents.fieldDescriptions,
|
||||
);
|
||||
|
||||
export const extendedProfileSelector = createSelector(
|
||||
commonComponentsSelector,
|
||||
commonComponents => commonComponents.extendedProfile,
|
||||
);
|
||||
|
||||
@@ -18,6 +18,11 @@ export async function getThirdPartyAuthContext(urlParams) {
|
||||
throw (e);
|
||||
});
|
||||
return {
|
||||
thirdPartyAuthContext: camelCaseObject(convertKeyNames(data, { fullname: 'name' })),
|
||||
fieldDescriptions: data.registration_fields || {},
|
||||
// For backward compatibility with the API, once https://github.com/openedx/edx-platform/pull/30198 is merged
|
||||
// and deployed update it to use data.context_data
|
||||
thirdPartyAuthContext: camelCaseObject(
|
||||
convertKeyNames(data.context_data || data, { fullname: 'name' }),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ describe('fetchThirdPartyAuthContext', () => {
|
||||
|
||||
it('should call service and dispatch success action', async () => {
|
||||
const getThirdPartyAuthContext = jest.spyOn(api, 'getThirdPartyAuthContext')
|
||||
.mockImplementation(() => Promise.resolve({ thirdPartyAuthContext: data }));
|
||||
.mockImplementation(() => Promise.resolve({ thirdPartyAuthContext: data, fieldDescriptions: {} }));
|
||||
|
||||
const dispatched = [];
|
||||
await runSaga(
|
||||
@@ -38,7 +38,7 @@ describe('fetchThirdPartyAuthContext', () => {
|
||||
expect(getThirdPartyAuthContext).toHaveBeenCalledTimes(1);
|
||||
expect(dispatched).toEqual([
|
||||
actions.getThirdPartyAuthContextBegin(),
|
||||
actions.getThirdPartyAuthContextSuccess(data),
|
||||
actions.getThirdPartyAuthContextSuccess({}, data),
|
||||
]);
|
||||
getThirdPartyAuthContext.mockClear();
|
||||
});
|
||||
|
||||
@@ -6,7 +6,17 @@ import { ExpandMore } from '@edx/paragon/icons';
|
||||
|
||||
const FormFieldRenderer = (props) => {
|
||||
let formField = null;
|
||||
const { fieldData, onChangeHandler, value } = props;
|
||||
const {
|
||||
errorMessage, fieldData, onChangeHandler, isRequired, value,
|
||||
} = props;
|
||||
|
||||
const handleFocus = (e) => {
|
||||
if (props.handleFocus) { props.handleFocus(e); }
|
||||
};
|
||||
|
||||
const handleOnBlur = (e) => {
|
||||
if (props.handleBlur) { props.handleBlur(e); }
|
||||
};
|
||||
|
||||
switch (fieldData.type) {
|
||||
case 'select': {
|
||||
@@ -14,63 +24,95 @@ const FormFieldRenderer = (props) => {
|
||||
return null;
|
||||
}
|
||||
formField = (
|
||||
<Form.Group controlId={fieldData.name} className="mb-3">
|
||||
<Form.Group controlId={fieldData.name} isInvalid={isRequired && errorMessage}>
|
||||
<Form.Control
|
||||
as="select"
|
||||
name={fieldData.name}
|
||||
value={value}
|
||||
aria-invalid={isRequired && Boolean(errorMessage)}
|
||||
onChange={(e) => onChangeHandler(e)}
|
||||
trailingElement={<Icon src={ExpandMore} />}
|
||||
floatingLabel={fieldData.label}
|
||||
onBlur={handleOnBlur}
|
||||
onFocus={handleFocus}
|
||||
>
|
||||
<option key="default" value="">{fieldData.label}</option>
|
||||
{fieldData.options.map(option => (
|
||||
<option className="data-hj-suppress" key={option[0]} value={option[0]}>{option[1]}</option>
|
||||
))}
|
||||
</Form.Control>
|
||||
{isRequired && errorMessage && (
|
||||
<Form.Control.Feedback id={`${fieldData.name}-error`} type="invalid" className="form-text-size" hasIcon={false}>
|
||||
{errorMessage}
|
||||
</Form.Control.Feedback>
|
||||
)}
|
||||
</Form.Group>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'textarea': {
|
||||
formField = (
|
||||
<Form.Group controlId={fieldData.name} className="mb-3">
|
||||
<Form.Group controlId={fieldData.name} isInvalid={isRequired && errorMessage}>
|
||||
<Form.Control
|
||||
as="textarea"
|
||||
name={fieldData.name}
|
||||
value={value}
|
||||
aria-invalid={isRequired && Boolean(errorMessage)}
|
||||
onChange={(e) => onChangeHandler(e)}
|
||||
floatingLabel={fieldData.label}
|
||||
onBlur={handleOnBlur}
|
||||
onFocus={handleFocus}
|
||||
/>
|
||||
{isRequired && errorMessage && (
|
||||
<Form.Control.Feedback id={`${fieldData.name}-error`} type="invalid" className="form-text-size" hasIcon={false}>
|
||||
{errorMessage}
|
||||
</Form.Control.Feedback>
|
||||
)}
|
||||
</Form.Group>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'text': {
|
||||
formField = (
|
||||
<Form.Group controlId={fieldData.name} className="mb-3">
|
||||
<Form.Group controlId={fieldData.name} isInvalid={isRequired && errorMessage}>
|
||||
<Form.Control
|
||||
name={fieldData.name}
|
||||
value={value}
|
||||
aria-invalid={isRequired && Boolean(errorMessage)}
|
||||
onChange={(e) => onChangeHandler(e)}
|
||||
floatingLabel={fieldData.label}
|
||||
onBlur={handleOnBlur}
|
||||
onFocus={handleFocus}
|
||||
/>
|
||||
{isRequired && errorMessage && (
|
||||
<Form.Control.Feedback id={`${fieldData.name}-error`} type="invalid" className="form-text-size" hasIcon={false}>
|
||||
{errorMessage}
|
||||
</Form.Control.Feedback>
|
||||
)}
|
||||
</Form.Group>
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'checkbox': {
|
||||
formField = (
|
||||
<Form.Group className="mb-3">
|
||||
<Form.Group isInvalid={isRequired && errorMessage}>
|
||||
<Form.Checkbox
|
||||
id={fieldData.name}
|
||||
checked={!!value}
|
||||
name={fieldData.name}
|
||||
value={value}
|
||||
aria-invalid={isRequired && Boolean(errorMessage)}
|
||||
onChange={(e) => onChangeHandler(e)}
|
||||
onBlur={handleOnBlur}
|
||||
onFocus={handleFocus}
|
||||
>
|
||||
{fieldData.label}
|
||||
</Form.Checkbox>
|
||||
{isRequired && errorMessage && (
|
||||
<Form.Control.Feedback id={`${fieldData.name}-error`} type="invalid" className="form-text-size" hasIcon={false}>
|
||||
{errorMessage}
|
||||
</Form.Control.Feedback>
|
||||
)}
|
||||
</Form.Group>
|
||||
);
|
||||
break;
|
||||
@@ -83,6 +125,10 @@ const FormFieldRenderer = (props) => {
|
||||
};
|
||||
FormFieldRenderer.defaultProps = {
|
||||
value: '',
|
||||
handleBlur: null,
|
||||
handleFocus: null,
|
||||
errorMessage: '',
|
||||
isRequired: false,
|
||||
};
|
||||
|
||||
FormFieldRenderer.propTypes = {
|
||||
@@ -92,6 +138,10 @@ FormFieldRenderer.propTypes = {
|
||||
name: PropTypes.string,
|
||||
}).isRequired,
|
||||
onChangeHandler: PropTypes.func.isRequired,
|
||||
handleBlur: PropTypes.func,
|
||||
handleFocus: PropTypes.func,
|
||||
errorMessage: PropTypes.string,
|
||||
isRequired: PropTypes.bool,
|
||||
value: PropTypes.string,
|
||||
};
|
||||
|
||||
|
||||
@@ -102,4 +102,96 @@ describe('FieldRendererTests', () => {
|
||||
const fieldRenderer = mount(<FieldRenderer fieldData={fieldData} onChangeHandler={() => {}} />);
|
||||
expect(fieldRenderer.html()).toBeNull();
|
||||
});
|
||||
|
||||
it('should run onBlur and onFocus functions for a field if given', () => {
|
||||
const fieldData = { type: 'text', label: 'Test', name: 'test-field' };
|
||||
let functionValue = '';
|
||||
|
||||
const onBlur = (e) => {
|
||||
functionValue = `${e.target.name} blurred`;
|
||||
};
|
||||
|
||||
const onFocus = (e) => {
|
||||
functionValue = `${e.target.name} focussed`;
|
||||
};
|
||||
|
||||
const fieldRenderer = mount(
|
||||
<FieldRenderer
|
||||
handleFocus={onFocus}
|
||||
handleBlur={onBlur}
|
||||
value={value}
|
||||
fieldData={fieldData}
|
||||
onChangeHandler={changeHandler}
|
||||
/>,
|
||||
);
|
||||
const field = fieldRenderer.find('#test-field').last();
|
||||
|
||||
field.simulate('focus');
|
||||
expect(functionValue).toEqual('test-field focussed');
|
||||
|
||||
field.simulate('blur');
|
||||
expect(functionValue).toEqual('test-field blurred');
|
||||
});
|
||||
|
||||
it('should render error message for required text fields', () => {
|
||||
const fieldData = { type: 'text', label: 'First Name', name: 'first-name-field' };
|
||||
|
||||
const fieldRenderer = mount(
|
||||
<FieldRenderer
|
||||
isRequired
|
||||
fieldData={fieldData}
|
||||
onChangeHandler={changeHandler}
|
||||
errorMessage="Enter your first name"
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(fieldRenderer.find('.form-text-size').last().text()).toEqual('Enter your first name');
|
||||
});
|
||||
|
||||
it('should render error message for required select fields', () => {
|
||||
const fieldData = {
|
||||
type: 'select', label: 'Preference', name: 'preference-field', options: [['a', 'Opt 1'], ['b', 'Opt 2']],
|
||||
};
|
||||
|
||||
const fieldRenderer = mount(
|
||||
<FieldRenderer
|
||||
isRequired
|
||||
fieldData={fieldData}
|
||||
onChangeHandler={changeHandler}
|
||||
errorMessage="Select your preference"
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(fieldRenderer.find('.form-text-size').last().text()).toEqual('Select your preference');
|
||||
});
|
||||
|
||||
it('should render error message for required textarea fields', () => {
|
||||
const fieldData = { type: 'textarea', label: 'Goals', name: 'goals-field' };
|
||||
|
||||
const fieldRenderer = mount(
|
||||
<FieldRenderer
|
||||
isRequired
|
||||
fieldData={fieldData}
|
||||
onChangeHandler={changeHandler}
|
||||
errorMessage="Tell us your goals"
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(fieldRenderer.find('.form-text-size').last().text()).toEqual('Tell us your goals');
|
||||
});
|
||||
|
||||
it('should render error message for required checkbox fields', () => {
|
||||
const fieldData = { type: 'checkbox', label: 'Honor Code', name: 'honor-code-field' };
|
||||
|
||||
const fieldRenderer = mount(
|
||||
<FieldRenderer
|
||||
isRequired
|
||||
fieldData={fieldData}
|
||||
onChangeHandler={changeHandler}
|
||||
errorMessage="You must agree to our Honor Code"
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(fieldRenderer.find('.form-text-size').last().text()).toEqual('You must agree to our Honor Code');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -97,11 +97,11 @@ const ForgotPasswordPage = (props) => {
|
||||
{ siteName: getConfig().SITE_NAME })}
|
||||
</title>
|
||||
</Helmet>
|
||||
<Form className="mw-xs">
|
||||
<Form id="forget-password-form" name="forget-password-form" className="mw-xs">
|
||||
<ForgotPasswordAlert email={props.email} emailError={errors.email} status={status} />
|
||||
<h4>
|
||||
<h2 className="h4">
|
||||
{intl.formatMessage(messages['forgot.password.page.heading'])}
|
||||
</h4>
|
||||
</h2>
|
||||
<p className="mb-4">
|
||||
{intl.formatMessage(messages['forgot.password.page.instructions'])}
|
||||
</p>
|
||||
@@ -116,6 +116,8 @@ const ForgotPasswordPage = (props) => {
|
||||
helpText={[intl.formatMessage(messages['forgot.password.email.help.text'], { platformName })]}
|
||||
/>
|
||||
<StatefulButton
|
||||
id="submit-forget-password"
|
||||
name="submit-forget-password"
|
||||
type="submit"
|
||||
variant="brand"
|
||||
className="login-button-width"
|
||||
@@ -129,6 +131,7 @@ const ForgotPasswordPage = (props) => {
|
||||
/>
|
||||
<Hyperlink
|
||||
id="forgot-password"
|
||||
name="forgot-password"
|
||||
className="ml-4 font-weight-500 text-body"
|
||||
destination={supportUrl}
|
||||
onClick={e => {
|
||||
|
||||
@@ -118,6 +118,8 @@
|
||||
"password.security.block.body": "Our system detected that your password is vulnerable. Change your password so that your account stays secure.",
|
||||
"password.security.close.button": "Close",
|
||||
"password.security.redirect.to.reset.password.button": "Reset your password",
|
||||
"register.page.terms.of.service.and.honor.code": "By creating an account, you agree to the {tosAndHonorCode} and you acknowledge that {platformName} and each\n Member process your personal data in accordance with the {privacyPolicy}.",
|
||||
"register.page.honor.code": "I agree to the {platformName} {tosAndHonorCode}",
|
||||
"register.page.title": "Register | {siteName}",
|
||||
"registration.fullname.label": "Full name",
|
||||
"registration.email.label": "Email",
|
||||
@@ -129,7 +131,6 @@
|
||||
"help.text.username.1": "The name that will identify you in your courses.",
|
||||
"help.text.username.2": "This can not be changed later.",
|
||||
"help.text.email": "For account activation and important updates",
|
||||
"create.account.button": "Create an account",
|
||||
"create.account.for.free.button": "Create an account for free",
|
||||
"create.an.account.btn.pending.state": "Loading",
|
||||
"registration.other.options.heading": "Or register with:",
|
||||
@@ -153,6 +154,8 @@
|
||||
"registration.tpa.session.expired": "Registration using {provider} has timed out.",
|
||||
"terms.of.service.and.honor.code": "Terms of Service and Honor Code",
|
||||
"privacy.policy": "Privacy Policy",
|
||||
"honor.code": "Honor Code",
|
||||
"terms.of.service": "Terms of Service",
|
||||
"registration.year.of.birth.label": "Year of birth (optional)",
|
||||
"registration.field.gender.options.label": "Gender (optional)",
|
||||
"registration.goals.label": "Tell us why you're interested in edX (optional)",
|
||||
@@ -173,7 +176,7 @@
|
||||
"registration.using.tpa.form.heading": "Finish creating your account",
|
||||
"did.you.mean.alert.text": "Did you mean",
|
||||
"certificate.msg": "*Offer not eligible for GTx’s Analytics: Essential Tools and Methods MicroMasters Program, ColumbiaX’s Corporate Finance Professional Certificate Program, or courses or programs offered by Wharton, and NYIF.",
|
||||
"register.page.terms.of.service.and.honor.code": "By creating an account, you agree to the {tosAndHonorCode} and you acknowledge that {platformName} and each\n Member process your personal data in accordance with the {privacyPolicy}.",
|
||||
"register.page.terms.of.service": "I agree to the {platformName} {termsOfService}",
|
||||
"sign.in": "Sign in",
|
||||
"reset.password.page.title": "Reset Password | {siteName}",
|
||||
"reset.password": "Reset password",
|
||||
|
||||
@@ -118,6 +118,8 @@
|
||||
"password.security.block.body": "Our system detected that your password is vulnerable. Change your password so that your account stays secure.",
|
||||
"password.security.close.button": "Close",
|
||||
"password.security.redirect.to.reset.password.button": "Reset your password",
|
||||
"register.page.terms.of.service.and.honor.code": "By creating an account, you agree to the {tosAndHonorCode} and you acknowledge that {platformName} and each\n Member process your personal data in accordance with the {privacyPolicy}.",
|
||||
"register.page.honor.code": "I agree to the {platformName} {tosAndHonorCode}",
|
||||
"register.page.title": "Register | {siteName}",
|
||||
"registration.fullname.label": "Full name",
|
||||
"registration.email.label": "Email",
|
||||
@@ -129,7 +131,6 @@
|
||||
"help.text.username.1": "The name that will identify you in your courses.",
|
||||
"help.text.username.2": "This can not be changed later.",
|
||||
"help.text.email": "For account activation and important updates",
|
||||
"create.account.button": "Create an account",
|
||||
"create.account.for.free.button": "Create an account for free",
|
||||
"create.an.account.btn.pending.state": "Loading",
|
||||
"registration.other.options.heading": "Or register with:",
|
||||
@@ -153,6 +154,8 @@
|
||||
"registration.tpa.session.expired": "Registration using {provider} has timed out.",
|
||||
"terms.of.service.and.honor.code": "Terms of Service and Honor Code",
|
||||
"privacy.policy": "Privacy Policy",
|
||||
"honor.code": "Honor Code",
|
||||
"terms.of.service": "Terms of Service",
|
||||
"registration.year.of.birth.label": "Year of birth (optional)",
|
||||
"registration.field.gender.options.label": "Gender (optional)",
|
||||
"registration.goals.label": "Tell us why you're interested in edX (optional)",
|
||||
@@ -173,7 +176,7 @@
|
||||
"registration.using.tpa.form.heading": "Finish creating your account",
|
||||
"did.you.mean.alert.text": "Did you mean",
|
||||
"certificate.msg": "*Offer not eligible for GTx’s Analytics: Essential Tools and Methods MicroMasters Program, ColumbiaX’s Corporate Finance Professional Certificate Program, or courses or programs offered by Wharton, and NYIF.",
|
||||
"register.page.terms.of.service.and.honor.code": "By creating an account, you agree to the {tosAndHonorCode} and you acknowledge that {platformName} and each\n Member process your personal data in accordance with the {privacyPolicy}.",
|
||||
"register.page.terms.of.service": "I agree to the {platformName} {termsOfService}",
|
||||
"sign.in": "Sign in",
|
||||
"reset.password.page.title": "Reset Password | {siteName}",
|
||||
"reset.password": "Reset password",
|
||||
|
||||
@@ -118,6 +118,8 @@
|
||||
"password.security.block.body": "Our system detected that your password is vulnerable. Change your password so that your account stays secure.",
|
||||
"password.security.close.button": "Close",
|
||||
"password.security.redirect.to.reset.password.button": "Reset your password",
|
||||
"register.page.terms.of.service.and.honor.code": "Al crear una cuenta, aceptas el {tosAndHonorCode} y reconoces que {platformName} y cada\n Miembro procesa tus datos personales de acuerdo con la {privacyPolicy}.",
|
||||
"register.page.honor.code": "I agree to the {platformName} {tosAndHonorCode}",
|
||||
"register.page.title": "Register | {siteName}",
|
||||
"registration.fullname.label": "Nombre completo",
|
||||
"registration.email.label": "Correo electrónico",
|
||||
@@ -129,7 +131,6 @@
|
||||
"help.text.username.1": "El nombre que te identificará en tus cursos.",
|
||||
"help.text.username.2": "Esto no puede modificarse posteriormente.",
|
||||
"help.text.email": "Para la activación de la cuenta y las actualizaciones importantes",
|
||||
"create.account.button": "Crear una cuenta",
|
||||
"create.account.for.free.button": "Crea una cuenta gratis",
|
||||
"create.an.account.btn.pending.state": "Cargando",
|
||||
"registration.other.options.heading": "O regístrese con:",
|
||||
@@ -153,6 +154,8 @@
|
||||
"registration.tpa.session.expired": "Inscripción usando {provider} ha expirado.",
|
||||
"terms.of.service.and.honor.code": "Condiciones de servicio y código de honor",
|
||||
"privacy.policy": "Política de privacidad ",
|
||||
"honor.code": "Honor Code",
|
||||
"terms.of.service": "Terms of Service",
|
||||
"registration.year.of.birth.label": "Año de nacimiento (opcional)",
|
||||
"registration.field.gender.options.label": "Género (opcional)",
|
||||
"registration.goals.label": "Díganos por qué estás interesado en edX (opcional)",
|
||||
@@ -173,7 +176,7 @@
|
||||
"registration.using.tpa.form.heading": "Termina de crear tu cuenta",
|
||||
"did.you.mean.alert.text": "¿Quieres decir",
|
||||
"certificate.msg": "*No se incluyen los programas de: MicroMasters en Analytics: Essential Tools and Methods de GTx, Certificación Profesional de Corporate Finance de ColumbiaX o cursos o programas ofrecidos por Wharton y NYIF en esta oferta.",
|
||||
"register.page.terms.of.service.and.honor.code": "Al crear una cuenta, aceptas el {tosAndHonorCode} y reconoces que {platformName} y cada\n Miembro procesa tus datos personales de acuerdo con la {privacyPolicy}.",
|
||||
"register.page.terms.of.service": "I agree to the {platformName} {termsOfService}",
|
||||
"sign.in": "Iniciar sesión",
|
||||
"reset.password.page.title": "Restablecer contraseña | {siteName}",
|
||||
"reset.password": "Restablecer mi contraseña",
|
||||
|
||||
@@ -112,12 +112,14 @@
|
||||
"login.form.invalid.error.message": "Veuillez remplir les champs ci-dessous.",
|
||||
"login.incorrect.credentials.error.reset.link.text": "réinitialiser votre mot de passe",
|
||||
"login.incorrect.credentials.error.before.account.blocked.text": "cliquez ici pour le réinitialiser.",
|
||||
"password.security.nudge.title": "Password security",
|
||||
"password.security.block.title": "Password change required",
|
||||
"password.security.nudge.body": "Our system detected that your password is vulnerable. We recommend you change it so that your account stays secure.",
|
||||
"password.security.block.body": "Our system detected that your password is vulnerable. Change your password so that your account stays secure.",
|
||||
"password.security.close.button": "Close",
|
||||
"password.security.redirect.to.reset.password.button": "Reset your password",
|
||||
"password.security.nudge.title": "Sécurité du mot de passe",
|
||||
"password.security.block.title": "Changement de mot de passe requis",
|
||||
"password.security.nudge.body": "Notre système a détecté que votre mot de passe est vulnérable. Nous vous recommandons de le modifier afin que votre compte reste sécurisé.",
|
||||
"password.security.block.body": "Notre système a détecté que votre mot de passe est vulnérable. Changez votre mot de passe afin que votre compte reste sécurisé.",
|
||||
"password.security.close.button": "Fermer",
|
||||
"password.security.redirect.to.reset.password.button": "Réinitialiser votre mot de passe",
|
||||
"register.page.terms.of.service.and.honor.code": "En créant un compte, vous acceptez le {tosAndHonorCode} et vous reconnaissez que {platformName} et chaque\n membre peut traiter vos données personnelles conformément à la {privacyPolicy}.",
|
||||
"register.page.honor.code": "I agree to the {platformName} {tosAndHonorCode}",
|
||||
"register.page.title": "S'inscrire | {siteName}",
|
||||
"registration.fullname.label": "Nom complet",
|
||||
"registration.email.label": "Email",
|
||||
@@ -129,7 +131,6 @@
|
||||
"help.text.username.1": "Le nom qui vous identifiera dans vos cours.",
|
||||
"help.text.username.2": "Cela ne peut pas être modifié ultérieurement.",
|
||||
"help.text.email": "Pour l'activation du compte et les mises à jour importantes",
|
||||
"create.account.button": "Créer un compte",
|
||||
"create.account.for.free.button": "Créer un compte gratuitement",
|
||||
"create.an.account.btn.pending.state": "Chargement en cours",
|
||||
"registration.other.options.heading": "Ou inscrivez-vous avec :",
|
||||
@@ -153,6 +154,8 @@
|
||||
"registration.tpa.session.expired": "L'inscription avec {provider} a échouée.",
|
||||
"terms.of.service.and.honor.code": "Conditions d'utilisation et Code d'honneur",
|
||||
"privacy.policy": "Politique de confidentialité",
|
||||
"honor.code": "Honor Code",
|
||||
"terms.of.service": "Terms of Service",
|
||||
"registration.year.of.birth.label": "Année de naissance (facultatif)",
|
||||
"registration.field.gender.options.label": "Sexe (facultatif)",
|
||||
"registration.goals.label": "Dites-nous pourquoi vous êtes intéressé par edX (facultatif)",
|
||||
@@ -173,7 +176,7 @@
|
||||
"registration.using.tpa.form.heading": "Terminer la création de votre compte",
|
||||
"did.you.mean.alert.text": "Vouliez-vous dire",
|
||||
"certificate.msg": "*L'offre n'est pas éligible au programme GTx Analytics: Essential Tools and Methods MicroMasters, au programme de certificat professionnel en finance d'entreprise de ColumbiaX, ni aux cours ou programmes proposés par Wharton et NYIF.",
|
||||
"register.page.terms.of.service.and.honor.code": "En créant un compte, vous acceptez le {tosAndHonorCode} et vous reconnaissez que {platformName} et chaque\n membre peut traiter vos données personnelles conformément à la {privacyPolicy}.",
|
||||
"register.page.terms.of.service": "I agree to the {platformName} {termsOfService}",
|
||||
"sign.in": "Connectez-vous",
|
||||
"reset.password.page.title": "Réinitialiser le mot de passe | {siteName}",
|
||||
"reset.password": "Réinitialiser le mot de passe",
|
||||
|
||||
@@ -118,6 +118,8 @@
|
||||
"password.security.block.body": "Our system detected that your password is vulnerable. Change your password so that your account stays secure.",
|
||||
"password.security.close.button": "Close",
|
||||
"password.security.redirect.to.reset.password.button": "Reset your password",
|
||||
"register.page.terms.of.service.and.honor.code": "By creating an account, you agree to the {tosAndHonorCode} and you acknowledge that {platformName} and each\n Member process your personal data in accordance with the {privacyPolicy}.",
|
||||
"register.page.honor.code": "I agree to the {platformName} {tosAndHonorCode}",
|
||||
"register.page.title": "Register | {siteName}",
|
||||
"registration.fullname.label": "Full name",
|
||||
"registration.email.label": "Email",
|
||||
@@ -129,7 +131,6 @@
|
||||
"help.text.username.1": "The name that will identify you in your courses.",
|
||||
"help.text.username.2": "This can not be changed later.",
|
||||
"help.text.email": "For account activation and important updates",
|
||||
"create.account.button": "Create an account",
|
||||
"create.account.for.free.button": "Create an account for free",
|
||||
"create.an.account.btn.pending.state": "Loading",
|
||||
"registration.other.options.heading": "Or register with:",
|
||||
@@ -153,6 +154,8 @@
|
||||
"registration.tpa.session.expired": "Registration using {provider} has timed out.",
|
||||
"terms.of.service.and.honor.code": "Terms of Service and Honor Code",
|
||||
"privacy.policy": "Privacy Policy",
|
||||
"honor.code": "Honor Code",
|
||||
"terms.of.service": "Terms of Service",
|
||||
"registration.year.of.birth.label": "Year of birth (optional)",
|
||||
"registration.field.gender.options.label": "Gender (optional)",
|
||||
"registration.goals.label": "Tell us why you're interested in edX (optional)",
|
||||
@@ -173,7 +176,7 @@
|
||||
"registration.using.tpa.form.heading": "Finish creating your account",
|
||||
"did.you.mean.alert.text": "Did you mean",
|
||||
"certificate.msg": "*Offer not eligible for GTx’s Analytics: Essential Tools and Methods MicroMasters Program, ColumbiaX’s Corporate Finance Professional Certificate Program, or courses or programs offered by Wharton, and NYIF.",
|
||||
"register.page.terms.of.service.and.honor.code": "By creating an account, you agree to the {tosAndHonorCode} and you acknowledge that {platformName} and each\n Member process your personal data in accordance with the {privacyPolicy}.",
|
||||
"register.page.terms.of.service": "I agree to the {platformName} {termsOfService}",
|
||||
"sign.in": "Sign in",
|
||||
"reset.password.page.title": "Reset Password | {siteName}",
|
||||
"reset.password": "Reset password",
|
||||
|
||||
@@ -118,6 +118,8 @@
|
||||
"password.security.block.body": "Our system detected that your password is vulnerable. Change your password so that your account stays secure.",
|
||||
"password.security.close.button": "Close",
|
||||
"password.security.redirect.to.reset.password.button": "Reset your password",
|
||||
"register.page.terms.of.service.and.honor.code": "By creating an account, you agree to the {tosAndHonorCode} and you acknowledge that {platformName} and each\n Member process your personal data in accordance with the {privacyPolicy}.",
|
||||
"register.page.honor.code": "I agree to the {platformName} {tosAndHonorCode}",
|
||||
"register.page.title": "Registrazione | {siteName}",
|
||||
"registration.fullname.label": "Full name",
|
||||
"registration.email.label": "Email",
|
||||
@@ -129,7 +131,6 @@
|
||||
"help.text.username.1": "The name that will identify you in your courses.",
|
||||
"help.text.username.2": "This can not be changed later.",
|
||||
"help.text.email": "For account activation and important updates",
|
||||
"create.account.button": "Create an account",
|
||||
"create.account.for.free.button": "Create an account for free",
|
||||
"create.an.account.btn.pending.state": "Loading",
|
||||
"registration.other.options.heading": "Or register with:",
|
||||
@@ -153,6 +154,8 @@
|
||||
"registration.tpa.session.expired": "La registrazione mediante {provider} è andata in timeout.",
|
||||
"terms.of.service.and.honor.code": "Terms of Service and Honor Code",
|
||||
"privacy.policy": "Privacy Policy",
|
||||
"honor.code": "Honor Code",
|
||||
"terms.of.service": "Terms of Service",
|
||||
"registration.year.of.birth.label": "Anno di nascita (facoltativo)",
|
||||
"registration.field.gender.options.label": "Genere (facoltativo)",
|
||||
"registration.goals.label": "Dicci perché sei interessato a edX (facoltativo)",
|
||||
@@ -173,7 +176,7 @@
|
||||
"registration.using.tpa.form.heading": "Finish creating your account",
|
||||
"did.you.mean.alert.text": "Did you mean",
|
||||
"certificate.msg": "*Offer not eligible for GTx’s Analytics: Essential Tools and Methods MicroMasters Program, ColumbiaX’s Corporate Finance Professional Certificate Program, or courses or programs offered by Wharton, and NYIF.",
|
||||
"register.page.terms.of.service.and.honor.code": "By creating an account, you agree to the {tosAndHonorCode} and you acknowledge that {platformName} and each\n Member process your personal data in accordance with the {privacyPolicy}.",
|
||||
"register.page.terms.of.service": "I agree to the {platformName} {termsOfService}",
|
||||
"sign.in": "Sign in",
|
||||
"reset.password.page.title": "Ripristina password | {siteName}",
|
||||
"reset.password": "Reset password",
|
||||
|
||||
@@ -118,6 +118,8 @@
|
||||
"password.security.block.body": "Our system detected that your password is vulnerable. Change your password so that your account stays secure.",
|
||||
"password.security.close.button": "Close",
|
||||
"password.security.redirect.to.reset.password.button": "Reset your password",
|
||||
"register.page.terms.of.service.and.honor.code": "By creating an account, you agree to the {tosAndHonorCode} and you acknowledge that {platformName} and each\n Member process your personal data in accordance with the {privacyPolicy}.",
|
||||
"register.page.honor.code": "I agree to the {platformName} {tosAndHonorCode}",
|
||||
"register.page.title": "Registar | {siteName}",
|
||||
"registration.fullname.label": "Full name",
|
||||
"registration.email.label": "Email",
|
||||
@@ -129,7 +131,6 @@
|
||||
"help.text.username.1": "The name that will identify you in your courses.",
|
||||
"help.text.username.2": "This can not be changed later.",
|
||||
"help.text.email": "For account activation and important updates",
|
||||
"create.account.button": "Create an account",
|
||||
"create.account.for.free.button": "Create an account for free",
|
||||
"create.an.account.btn.pending.state": "Loading",
|
||||
"registration.other.options.heading": "Or register with:",
|
||||
@@ -153,6 +154,8 @@
|
||||
"registration.tpa.session.expired": "Registration using {provider} has timed out.",
|
||||
"terms.of.service.and.honor.code": "Termos de Serviço e Código de Honra",
|
||||
"privacy.policy": "Política de Privacidade",
|
||||
"honor.code": "Honor Code",
|
||||
"terms.of.service": "Terms of Service",
|
||||
"registration.year.of.birth.label": "Ano de Nascimento (opcional)",
|
||||
"registration.field.gender.options.label": "Género (opcional)",
|
||||
"registration.goals.label": "Diga-nos porque está interessado no edX (opcional)",
|
||||
@@ -173,7 +176,7 @@
|
||||
"registration.using.tpa.form.heading": "Finish creating your account",
|
||||
"did.you.mean.alert.text": "Did you mean",
|
||||
"certificate.msg": "*Offer not eligible for GTx’s Analytics: Essential Tools and Methods MicroMasters Program, ColumbiaX’s Corporate Finance Professional Certificate Program, or courses or programs offered by Wharton, and NYIF.",
|
||||
"register.page.terms.of.service.and.honor.code": "By creating an account, you agree to the {tosAndHonorCode} and you acknowledge that {platformName} and each\n Member process your personal data in accordance with the {privacyPolicy}.",
|
||||
"register.page.terms.of.service": "I agree to the {platformName} {termsOfService}",
|
||||
"sign.in": "Sign in",
|
||||
"reset.password.page.title": "Redefinir Palavra-passe | {siteName}",
|
||||
"reset.password": "Reset password",
|
||||
|
||||
@@ -118,6 +118,8 @@
|
||||
"password.security.block.body": "Our system detected that your password is vulnerable. Change your password so that your account stays secure.",
|
||||
"password.security.close.button": "Close",
|
||||
"password.security.redirect.to.reset.password.button": "Reset your password",
|
||||
"register.page.terms.of.service.and.honor.code": "By creating an account, you agree to the {tosAndHonorCode} and you acknowledge that {platformName} and each\n Member process your personal data in accordance with the {privacyPolicy}.",
|
||||
"register.page.honor.code": "I agree to the {platformName} {tosAndHonorCode}",
|
||||
"register.page.title": "Register | {siteName}",
|
||||
"registration.fullname.label": "Full name",
|
||||
"registration.email.label": "Email",
|
||||
@@ -129,7 +131,6 @@
|
||||
"help.text.username.1": "The name that will identify you in your courses.",
|
||||
"help.text.username.2": "This can not be changed later.",
|
||||
"help.text.email": "For account activation and important updates",
|
||||
"create.account.button": "Create an account",
|
||||
"create.account.for.free.button": "Create an account for free",
|
||||
"create.an.account.btn.pending.state": "Loading",
|
||||
"registration.other.options.heading": "Or register with:",
|
||||
@@ -153,6 +154,8 @@
|
||||
"registration.tpa.session.expired": "Registration using {provider} has timed out.",
|
||||
"terms.of.service.and.honor.code": "Terms of Service and Honor Code",
|
||||
"privacy.policy": "Privacy Policy",
|
||||
"honor.code": "Honor Code",
|
||||
"terms.of.service": "Terms of Service",
|
||||
"registration.year.of.birth.label": "Year of birth (optional)",
|
||||
"registration.field.gender.options.label": "Gender (optional)",
|
||||
"registration.goals.label": "Tell us why you're interested in edX (optional)",
|
||||
@@ -173,7 +176,7 @@
|
||||
"registration.using.tpa.form.heading": "Finish creating your account",
|
||||
"did.you.mean.alert.text": "Did you mean",
|
||||
"certificate.msg": "*Offer not eligible for GTx’s Analytics: Essential Tools and Methods MicroMasters Program, ColumbiaX’s Corporate Finance Professional Certificate Program, or courses or programs offered by Wharton, and NYIF.",
|
||||
"register.page.terms.of.service.and.honor.code": "By creating an account, you agree to the {tosAndHonorCode} and you acknowledge that {platformName} and each\n Member process your personal data in accordance with the {privacyPolicy}.",
|
||||
"register.page.terms.of.service": "I agree to the {platformName} {termsOfService}",
|
||||
"sign.in": "Sign in",
|
||||
"reset.password.page.title": "Reset Password | {siteName}",
|
||||
"reset.password": "Reset password",
|
||||
|
||||
@@ -118,6 +118,8 @@
|
||||
"password.security.block.body": "Our system detected that your password is vulnerable. Change your password so that your account stays secure.",
|
||||
"password.security.close.button": "Close",
|
||||
"password.security.redirect.to.reset.password.button": "Reset your password",
|
||||
"register.page.terms.of.service.and.honor.code": "By creating an account, you agree to the {tosAndHonorCode} and you acknowledge that {platformName} and each\n Member process your personal data in accordance with the {privacyPolicy}.",
|
||||
"register.page.honor.code": "I agree to the {platformName} {tosAndHonorCode}",
|
||||
"register.page.title": "Register | {siteName}",
|
||||
"registration.fullname.label": "Full name",
|
||||
"registration.email.label": "Email",
|
||||
@@ -129,7 +131,6 @@
|
||||
"help.text.username.1": "The name that will identify you in your courses.",
|
||||
"help.text.username.2": "This can not be changed later.",
|
||||
"help.text.email": "For account activation and important updates",
|
||||
"create.account.button": "Create an account",
|
||||
"create.account.for.free.button": "Create an account for free",
|
||||
"create.an.account.btn.pending.state": "Loading",
|
||||
"registration.other.options.heading": "Or register with:",
|
||||
@@ -153,6 +154,8 @@
|
||||
"registration.tpa.session.expired": "Registration using {provider} has timed out.",
|
||||
"terms.of.service.and.honor.code": "Terms of Service and Honor Code",
|
||||
"privacy.policy": "Privacy Policy",
|
||||
"honor.code": "Honor Code",
|
||||
"terms.of.service": "Terms of Service",
|
||||
"registration.year.of.birth.label": "Year of birth (optional)",
|
||||
"registration.field.gender.options.label": "Gender (optional)",
|
||||
"registration.goals.label": "Tell us why you're interested in edX (optional)",
|
||||
@@ -173,7 +176,7 @@
|
||||
"registration.using.tpa.form.heading": "Finish creating your account",
|
||||
"did.you.mean.alert.text": "Did you mean",
|
||||
"certificate.msg": "*Offer not eligible for GTx’s Analytics: Essential Tools and Methods MicroMasters Program, ColumbiaX’s Corporate Finance Professional Certificate Program, or courses or programs offered by Wharton, and NYIF.",
|
||||
"register.page.terms.of.service.and.honor.code": "By creating an account, you agree to the {tosAndHonorCode} and you acknowledge that {platformName} and each\n Member process your personal data in accordance with the {privacyPolicy}.",
|
||||
"register.page.terms.of.service": "I agree to the {platformName} {termsOfService}",
|
||||
"sign.in": "Sign in",
|
||||
"reset.password.page.title": "Reset Password | {siteName}",
|
||||
"reset.password": "Reset password",
|
||||
|
||||
@@ -118,6 +118,8 @@
|
||||
"password.security.block.body": "Our system detected that your password is vulnerable. Change your password so that your account stays secure.",
|
||||
"password.security.close.button": "Close",
|
||||
"password.security.redirect.to.reset.password.button": "Reset your password",
|
||||
"register.page.terms.of.service.and.honor.code": "By creating an account, you agree to the {tosAndHonorCode} and you acknowledge that {platformName} and each\n Member process your personal data in accordance with the {privacyPolicy}.",
|
||||
"register.page.honor.code": "I agree to the {platformName} {tosAndHonorCode}",
|
||||
"register.page.title": "Register | {siteName}",
|
||||
"registration.fullname.label": "Full name",
|
||||
"registration.email.label": "Email",
|
||||
@@ -129,7 +131,6 @@
|
||||
"help.text.username.1": "The name that will identify you in your courses.",
|
||||
"help.text.username.2": "This can not be changed later.",
|
||||
"help.text.email": "For account activation and important updates",
|
||||
"create.account.button": "Create an account",
|
||||
"create.account.for.free.button": "Create an account for free",
|
||||
"create.an.account.btn.pending.state": "Loading",
|
||||
"registration.other.options.heading": "Or register with:",
|
||||
@@ -153,6 +154,8 @@
|
||||
"registration.tpa.session.expired": "Registration using {provider} has timed out.",
|
||||
"terms.of.service.and.honor.code": "Terms of Service and Honor Code",
|
||||
"privacy.policy": "Privacy Policy",
|
||||
"honor.code": "Honor Code",
|
||||
"terms.of.service": "Terms of Service",
|
||||
"registration.year.of.birth.label": "Year of birth (optional)",
|
||||
"registration.field.gender.options.label": "Gender (optional)",
|
||||
"registration.goals.label": "Tell us why you're interested in edX (optional)",
|
||||
@@ -173,7 +176,7 @@
|
||||
"registration.using.tpa.form.heading": "Finish creating your account",
|
||||
"did.you.mean.alert.text": "Did you mean",
|
||||
"certificate.msg": "*Offer not eligible for GTx’s Analytics: Essential Tools and Methods MicroMasters Program, ColumbiaX’s Corporate Finance Professional Certificate Program, or courses or programs offered by Wharton, and NYIF.",
|
||||
"register.page.terms.of.service.and.honor.code": "By creating an account, you agree to the {tosAndHonorCode} and you acknowledge that {platformName} and each\n Member process your personal data in accordance with the {privacyPolicy}.",
|
||||
"register.page.terms.of.service": "I agree to the {platformName} {termsOfService}",
|
||||
"sign.in": "Sign in",
|
||||
"reset.password.page.title": "Reset Password | {siteName}",
|
||||
"reset.password": "Reset password",
|
||||
|
||||
@@ -41,6 +41,7 @@ initialize({
|
||||
MARKETING_EMAILS_OPT_IN: process.env.MARKETING_EMAILS_OPT_IN || '',
|
||||
ENABLE_COPPA_COMPLIANCE: process.env.ENABLE_COPPA_COMPLIANCE || '',
|
||||
SHOW_DYNAMIC_PROFILING_PAGE: process.env.SHOW_DYNAMIC_PROFILING_PAGE || false,
|
||||
ENABLE_DYNAMIC_REGISTRATION_FIELDS: process.env.ENABLE_DYNAMIC_REGISTRATION_FIELDS || false,
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import { FormattedMessage, injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { getAuthService } from '@edx/frontend-platform/auth';
|
||||
import { Alert, Hyperlink } from '@edx/paragon';
|
||||
import { Error } from '@edx/paragon/icons';
|
||||
import PropTypes from 'prop-types';
|
||||
@@ -24,6 +25,7 @@ import ChangePasswordPrompt from './ChangePasswordPrompt';
|
||||
const LoginFailureMessage = (props) => {
|
||||
const { intl } = props;
|
||||
const { context, errorCode, value } = props.loginError;
|
||||
const authService = getAuthService();
|
||||
let errorList;
|
||||
let link;
|
||||
let resetLink = (
|
||||
@@ -135,6 +137,10 @@ const LoginFailureMessage = (props) => {
|
||||
}
|
||||
break;
|
||||
case NUDGE_PASSWORD_CHANGE:
|
||||
// Need to clear the CSRF token here to fetch a new one because token is already rotated after successful login.
|
||||
if (authService) {
|
||||
authService.getCsrfTokenService().clearCsrfTokenCache();
|
||||
}
|
||||
return (
|
||||
<ChangePasswordPrompt
|
||||
redirectUrl={props.loginError.redirectUrl}
|
||||
|
||||
@@ -224,7 +224,7 @@ class LoginPage extends React.Component {
|
||||
{submitState === DEFAULT_STATE && this.state.isSubmitted ? windowScrollTo({ left: 0, top: 0, behavior: 'smooth' }) : null}
|
||||
{activationMsgType && <AccountActivationMessage messageType={activationMsgType} />}
|
||||
{this.props.resetPassword && !this.props.loginError ? <ResetPasswordSuccess /> : null}
|
||||
<Form>
|
||||
<Form name="sign-in-form" id="sign-in-form">
|
||||
<FormGroup
|
||||
name="emailOrUsername"
|
||||
value={this.state.emailOrUsername}
|
||||
@@ -243,6 +243,8 @@ class LoginPage extends React.Component {
|
||||
floatingLabel={intl.formatMessage(messages['login.password.label'])}
|
||||
/>
|
||||
<StatefulButton
|
||||
name="sign-in"
|
||||
id="sign-in"
|
||||
type="submit"
|
||||
variant="brand"
|
||||
className="login-button-width"
|
||||
@@ -256,6 +258,7 @@ class LoginPage extends React.Component {
|
||||
/>
|
||||
<Link
|
||||
id="forgot-password"
|
||||
name="forgot-password"
|
||||
className="btn btn-link font-weight-500 text-body"
|
||||
to={updatePathWithQueryParams(RESET_PAGE)}
|
||||
onClick={this.handleForgotPasswordLinkClickEvent}
|
||||
|
||||
@@ -3,6 +3,7 @@ import { mount } from 'enzyme';
|
||||
import { MemoryRouter } from 'react-router-dom';
|
||||
|
||||
import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import * as auth from '@edx/frontend-platform/auth';
|
||||
|
||||
import LoginFailureMessage from '../LoginFailure';
|
||||
import {
|
||||
@@ -17,6 +18,9 @@ import {
|
||||
REQUIRE_PASSWORD_CHANGE,
|
||||
} from '../data/constants';
|
||||
|
||||
jest.mock('@edx/frontend-platform/auth');
|
||||
auth.getAuthService = jest.fn();
|
||||
|
||||
const IntlLoginFailureMessage = injectIntl(LoginFailureMessage);
|
||||
|
||||
describe('LoginFailureMessage', () => {
|
||||
|
||||
@@ -9,6 +9,7 @@ import renderer from 'react-test-renderer';
|
||||
import CookiePolicyBanner from '@edx/frontend-component-cookie-policy-banner';
|
||||
import { getConfig, mergeConfig } from '@edx/frontend-platform';
|
||||
import * as analytics from '@edx/frontend-platform/analytics';
|
||||
import * as auth from '@edx/frontend-platform/auth';
|
||||
import { IntlProvider, injectIntl } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { loginRequest, loginRequestFailure, loginRequestReset } from '../data/actions';
|
||||
@@ -18,9 +19,11 @@ import LoginPage from '../LoginPage';
|
||||
import { COMPLETE_STATE, PENDING_STATE } from '../../data/constants';
|
||||
|
||||
jest.mock('@edx/frontend-platform/analytics');
|
||||
jest.mock('@edx/frontend-platform/auth');
|
||||
|
||||
analytics.sendTrackEvent = jest.fn();
|
||||
analytics.sendPageEvent = jest.fn();
|
||||
auth.getAuthService = jest.fn();
|
||||
|
||||
const IntlLoginFailureMessage = injectIntl(LoginFailureMessage);
|
||||
const IntlLoginPage = injectIntl(LoginPage);
|
||||
|
||||
@@ -176,7 +176,7 @@ class CountryDropdown extends React.Component {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<div className="mb-4">
|
||||
<FormGroup
|
||||
as="input"
|
||||
name={this.props.name}
|
||||
|
||||
88
src/register/HonorCode.jsx
Normal file
88
src/register/HonorCode.jsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
import { Hyperlink, Form } from '@edx/paragon';
|
||||
import messages from './messages';
|
||||
|
||||
const HonorCode = (props) => {
|
||||
const {
|
||||
intl, errorMessage, onChangeHandler, fieldType, value,
|
||||
} = props;
|
||||
|
||||
if (fieldType === 'tos_and_honor_code') {
|
||||
return (
|
||||
<div id="honor-code" className="micro text-muted mt-4">
|
||||
<FormattedMessage
|
||||
id="register.page.terms.of.service.and.honor.code"
|
||||
defaultMessage="By creating an account, you agree to the {tosAndHonorCode} and you acknowledge that {platformName} and each
|
||||
Member process your personal data in accordance with the {privacyPolicy}."
|
||||
description="Text that appears on registration form stating honor code and privacy policy"
|
||||
values={{
|
||||
platformName: getConfig().SITE_NAME,
|
||||
tosAndHonorCode: (
|
||||
<Hyperlink variant="muted" destination={getConfig().TOS_AND_HONOR_CODE || '#'} target="_blank">
|
||||
{intl.formatMessage(messages['terms.of.service.and.honor.code'])}
|
||||
</Hyperlink>
|
||||
),
|
||||
privacyPolicy: (
|
||||
<Hyperlink variant="muted" destination={getConfig().PRIVACY_POLICY || '#'} target="_blank">
|
||||
{intl.formatMessage(messages['privacy.policy'])}
|
||||
</Hyperlink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div id="honor-code" className="micro text-muted">
|
||||
<Form.Checkbox
|
||||
className="opt-checkbox mt-1"
|
||||
id="honor-code"
|
||||
checked={value}
|
||||
name="honor_code"
|
||||
value={value}
|
||||
onChange={onChangeHandler}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="register.page.honor.code"
|
||||
defaultMessage="I agree to the {platformName} {tosAndHonorCode}"
|
||||
description="Text that appears on registration form stating honor code"
|
||||
values={{
|
||||
platformName: getConfig().SITE_NAME,
|
||||
tosAndHonorCode: (
|
||||
<Hyperlink variant="muted" destination={getConfig().TOS_AND_HONOR_CODE || '#'} target="_blank">
|
||||
{intl.formatMessage(messages['honor.code'])}
|
||||
</Hyperlink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Form.Checkbox>
|
||||
{errorMessage && (
|
||||
<Form.Control.Feedback type="invalid" className="form-text-size" hasIcon={false}>
|
||||
{errorMessage}
|
||||
</Form.Control.Feedback>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
HonorCode.defaultProps = {
|
||||
errorMessage: '',
|
||||
onChangeHandler: null,
|
||||
fieldType: 'honor_code',
|
||||
value: false,
|
||||
};
|
||||
|
||||
HonorCode.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
errorMessage: PropTypes.string,
|
||||
onChangeHandler: PropTypes.func,
|
||||
fieldType: PropTypes.string,
|
||||
value: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default injectIntl(HonorCode);
|
||||
@@ -4,37 +4,44 @@ import { connect } from 'react-redux';
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import PropTypes, { string } from 'prop-types';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { getConfig, snakeCaseObject } from '@edx/frontend-platform';
|
||||
import { sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||
import {
|
||||
injectIntl, intlShape, getCountryList, getLocale, FormattedMessage,
|
||||
injectIntl, intlShape, getCountryList, getLocale,
|
||||
} from '@edx/frontend-platform/i18n';
|
||||
import {
|
||||
Alert, Form, Hyperlink, StatefulButton, Icon,
|
||||
Alert, Form, StatefulButton, Icon,
|
||||
} from '@edx/paragon';
|
||||
import { Error, Close } from '@edx/paragon/icons';
|
||||
|
||||
import FormFieldRenderer from '../field-renderer';
|
||||
import {
|
||||
clearUsernameSuggestions, registerNewUser, resetRegistrationForm, fetchRealtimeValidations,
|
||||
} from './data/actions';
|
||||
import {
|
||||
FORM_SUBMISSION_ERROR, DEFAULT_SERVICE_PROVIDER_DOMAINS, DEFAULT_TOP_LEVEL_DOMAINS, COMMON_EMAIL_PROVIDERS,
|
||||
FIELDS, FORM_SUBMISSION_ERROR, DEFAULT_SERVICE_PROVIDER_DOMAINS, DEFAULT_TOP_LEVEL_DOMAINS, COMMON_EMAIL_PROVIDERS,
|
||||
} from './data/constants';
|
||||
import {
|
||||
registrationErrorSelector, registrationRequestSelector, validationsSelector, usernameSuggestionsSelector,
|
||||
registrationErrorSelector,
|
||||
registrationRequestSelector,
|
||||
validationsSelector,
|
||||
usernameSuggestionsSelector,
|
||||
} from './data/selectors';
|
||||
import messages from './messages';
|
||||
import RegistrationFailure from './RegistrationFailure';
|
||||
import UsernameField from './UsernameField';
|
||||
import HonorCode from './HonorCode';
|
||||
|
||||
import {
|
||||
RedirectLogistration, SocialAuthProviders, ThirdPartyAuthAlert, RenderInstitutionButton,
|
||||
InstitutionLogistration, FormGroup, PasswordField,
|
||||
} from '../common-components';
|
||||
import { getThirdPartyAuthContext } from '../common-components/data/actions';
|
||||
import { thirdPartyAuthContextSelector } from '../common-components/data/selectors';
|
||||
import {
|
||||
extendedProfileSelector,
|
||||
fieldDescriptionSelector,
|
||||
thirdPartyAuthContextSelector,
|
||||
} from '../common-components/data/selectors';
|
||||
import EnterpriseSSO from '../common-components/EnterpriseSSO';
|
||||
import {
|
||||
DEFAULT_STATE, PENDING_STATE, REGISTER_PAGE, VALID_EMAIL_REGEX, LETTER_REGEX, NUMBER_REGEX, VALID_NAME_REGEX,
|
||||
@@ -44,6 +51,7 @@ import {
|
||||
} from '../data/utils';
|
||||
import CountryDropdown from './CountryDropdown';
|
||||
import { getLevenshteinSuggestion, getSuggestionForInvalidEmail } from './utils';
|
||||
import TermsOfService from './TermsOfService';
|
||||
|
||||
class RegistrationPage extends React.Component {
|
||||
constructor(props, context) {
|
||||
@@ -52,6 +60,9 @@ class RegistrationPage extends React.Component {
|
||||
this.handleOnClose = this.handleOnClose.bind(this);
|
||||
|
||||
this.queryParams = getAllPossibleQueryParam();
|
||||
// TODO: Once we have tested it and ready for openedX we can remove this flag and make the code
|
||||
// permanent part of Authn and remove extra code
|
||||
this.showDynamicRegistrationFields = getConfig().ENABLE_DYNAMIC_REGISTRATION_FIELDS;
|
||||
this.tpaHint = getTpaHint();
|
||||
this.state = {
|
||||
country: '',
|
||||
@@ -76,8 +87,7 @@ class RegistrationPage extends React.Component {
|
||||
optimizelyExperimentName: '',
|
||||
readOnly: true,
|
||||
validatePassword: false,
|
||||
// TODO: Remove after VAN-876 experimentation is complete.
|
||||
registerRenameExpVariation: '',
|
||||
values: {},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -90,13 +100,6 @@ class RegistrationPage extends React.Component {
|
||||
isActive: true,
|
||||
});
|
||||
|
||||
if (payload.register_for_free === 'true') {
|
||||
window.optimizely.push({
|
||||
type: 'event',
|
||||
eventName: 'van-876-authn-registration-page',
|
||||
});
|
||||
}
|
||||
|
||||
if (payload.save_for_later === 'true') {
|
||||
sendTrackEvent('edx.bi.user.saveforlater.course.enroll.clicked', { category: 'save-for-later' });
|
||||
}
|
||||
@@ -174,27 +177,35 @@ class RegistrationPage extends React.Component {
|
||||
}
|
||||
|
||||
getExperiments = () => {
|
||||
const { experimentName, renameRegisterExperiment } = window;
|
||||
const { experimentName } = window;
|
||||
|
||||
if (experimentName) {
|
||||
this.setState({ optimizelyExperimentName: experimentName });
|
||||
}
|
||||
};
|
||||
|
||||
if (renameRegisterExperiment) {
|
||||
this.setState({ registerRenameExpVariation: renameRegisterExperiment });
|
||||
onChangeHandler = (e) => {
|
||||
const { name, value, checked } = e.target;
|
||||
const { errors, values } = this.state;
|
||||
if (e.target.type === 'checkbox') {
|
||||
errors[name] = '';
|
||||
values[name] = checked;
|
||||
} else {
|
||||
values[name] = value;
|
||||
}
|
||||
const state = { errors, values };
|
||||
this.setState({ ...state });
|
||||
};
|
||||
|
||||
handleSubmit = (e) => {
|
||||
e.preventDefault();
|
||||
const { startTime } = this.state;
|
||||
const totalRegistrationTime = (Date.now() - startTime) / 1000;
|
||||
const payload = {
|
||||
const dynamicFieldErrorMessages = {};
|
||||
let payload = {
|
||||
name: this.state.name,
|
||||
username: this.state.username,
|
||||
email: this.state.email,
|
||||
country: this.state.country,
|
||||
honor_code: true,
|
||||
is_authn_mfe: true,
|
||||
};
|
||||
|
||||
@@ -204,7 +215,28 @@ class RegistrationPage extends React.Component {
|
||||
payload.password = this.state.password;
|
||||
}
|
||||
|
||||
if (!this.isFormValid(payload)) {
|
||||
if (this.showDynamicRegistrationFields) {
|
||||
payload.extendedProfile = [];
|
||||
Object.keys(this.props.fieldDescriptions).forEach((fieldName) => {
|
||||
if (this.props.extendedProfile.includes(fieldName)) {
|
||||
payload.extendedProfile.push({ fieldName, fieldValue: this.state.values[fieldName] });
|
||||
} else {
|
||||
payload[fieldName] = this.state.values[fieldName];
|
||||
}
|
||||
dynamicFieldErrorMessages[fieldName] = this.props.fieldDescriptions[fieldName].error_message;
|
||||
});
|
||||
if (
|
||||
this.props.fieldDescriptions[FIELDS.HONOR_CODE]
|
||||
&& this.props.fieldDescriptions[FIELDS.HONOR_CODE].type === 'tos_and_honor_code'
|
||||
) {
|
||||
payload[FIELDS.HONOR_CODE] = true;
|
||||
}
|
||||
} else {
|
||||
payload.country = this.state.country;
|
||||
payload.honor_code = true;
|
||||
}
|
||||
|
||||
if (!this.isFormValid(payload, dynamicFieldErrorMessages)) {
|
||||
this.setState(prevState => ({
|
||||
errorCode: FORM_SUBMISSION_ERROR,
|
||||
failureCount: prevState.failureCount + 1,
|
||||
@@ -216,6 +248,7 @@ class RegistrationPage extends React.Component {
|
||||
payload.marketing_emails_opt_in = this.state.marketingOptIn;
|
||||
}
|
||||
|
||||
payload = snakeCaseObject(payload);
|
||||
payload.totalRegistrationTime = totalRegistrationTime;
|
||||
this.setState({
|
||||
totalRegistrationTime,
|
||||
@@ -296,13 +329,22 @@ class RegistrationPage extends React.Component {
|
||||
this.props.clearUsernameSuggestions();
|
||||
}
|
||||
|
||||
isFormValid(payload) {
|
||||
validateDynamicFields = (e) => {
|
||||
const { errors } = this.state;
|
||||
const { name, value } = e.target;
|
||||
if (!value) {
|
||||
errors[name] = this.props.fieldDescriptions[name].error_message;
|
||||
}
|
||||
this.setState({ errors });
|
||||
}
|
||||
|
||||
isFormValid(payload, dynamicFieldError) {
|
||||
const { errors } = this.state;
|
||||
let isValid = true;
|
||||
|
||||
Object.keys(payload).forEach(key => {
|
||||
if (!payload[key]) {
|
||||
errors[key] = this.props.intl.formatMessage(messages[`empty.${key}.field.error`]);
|
||||
errors[key] = (key in dynamicFieldError) ? dynamicFieldError[key] : this.props.intl.formatMessage(messages[`empty.${key}.field.error`]);
|
||||
}
|
||||
// Mark form invalid, if there was already a validation error for this key or we added empty field error
|
||||
if (errors[key]) {
|
||||
@@ -523,7 +565,6 @@ class RegistrationPage extends React.Component {
|
||||
setSurveyCookie('register');
|
||||
setCookie(getConfig().REGISTER_CONVERSION_COOKIE_NAME, true);
|
||||
setCookie('authn-returning-user');
|
||||
const payload = { ...this.queryParams };
|
||||
|
||||
// Fire optimizely events
|
||||
window.optimizely = window.optimizely || [];
|
||||
@@ -534,15 +575,75 @@ class RegistrationPage extends React.Component {
|
||||
value: this.state.totalRegistrationTime,
|
||||
},
|
||||
});
|
||||
|
||||
if (payload.register_for_free === 'true') {
|
||||
window.optimizely.push({
|
||||
type: 'event',
|
||||
eventName: 'van-876-authn-register-for-free-conversion',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const honorCode = [];
|
||||
const formFields = this.showDynamicRegistrationFields ? (
|
||||
Object.keys(this.props.fieldDescriptions).map((fieldName) => {
|
||||
const fieldData = this.props.fieldDescriptions[fieldName];
|
||||
switch (fieldData.name) {
|
||||
case FIELDS.COUNTRY:
|
||||
return (
|
||||
<span key={fieldData.name}>
|
||||
<CountryDropdown
|
||||
name="country"
|
||||
floatingLabel={intl.formatMessage(messages['registration.country.label'])}
|
||||
options={getCountryList(getLocale())}
|
||||
valueKey="code"
|
||||
displayValueKey="name"
|
||||
value={this.state.values[fieldData.name]}
|
||||
handleBlur={this.handleOnBlur}
|
||||
handleFocus={this.handleOnFocus}
|
||||
errorMessage={intl.formatMessage(messages['empty.country.field.error'])}
|
||||
handleChange={
|
||||
(value) => this.setState(prevState => ({ values: { ...prevState.values, country: value } }))
|
||||
}
|
||||
errorCode={this.state.errorCode}
|
||||
readOnly={this.state.readOnly}
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
case FIELDS.HONOR_CODE:
|
||||
honorCode.push(
|
||||
<span key={fieldData.name}>
|
||||
<HonorCode
|
||||
fieldType={fieldData.type}
|
||||
value={this.state.values[fieldData.name]}
|
||||
onChangeHandler={this.onChangeHandler}
|
||||
errorMessage={this.state.errors[fieldData.name]}
|
||||
/>
|
||||
</span>,
|
||||
);
|
||||
return null;
|
||||
case FIELDS.TERMS_OF_SERVICE:
|
||||
honorCode.push(
|
||||
<span key={fieldData.name}>
|
||||
<TermsOfService
|
||||
value={this.state.values[fieldData.name]}
|
||||
onChangeHandler={this.onChangeHandler}
|
||||
errorMessage={this.state.errors[fieldData.name]}
|
||||
/>
|
||||
</span>,
|
||||
);
|
||||
return null;
|
||||
default:
|
||||
return (
|
||||
<span key={fieldData.name}>
|
||||
<FormFieldRenderer
|
||||
fieldData={fieldData}
|
||||
value={this.state.values[fieldData.name]}
|
||||
onChangeHandler={this.onChangeHandler}
|
||||
handleBlur={this.validateDynamicFields}
|
||||
handleFocus={this.handleOnFocus}
|
||||
errorMessage={this.state.errors[fieldData.name]}
|
||||
isRequired
|
||||
/>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
})
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
@@ -574,7 +675,7 @@ class RegistrationPage extends React.Component {
|
||||
<h4 className="mt-4 mb-4">{intl.formatMessage(messages['registration.using.tpa.form.heading'])}</h4>
|
||||
</>
|
||||
)}
|
||||
<Form>
|
||||
<Form id="registration-form" name="registration-form">
|
||||
<FormGroup
|
||||
name="name"
|
||||
value={this.state.name}
|
||||
@@ -624,20 +725,24 @@ class RegistrationPage extends React.Component {
|
||||
floatingLabel={intl.formatMessage(messages['registration.password.label'])}
|
||||
/>
|
||||
)}
|
||||
<CountryDropdown
|
||||
name="country"
|
||||
floatingLabel={intl.formatMessage(messages['registration.country.label'])}
|
||||
options={getCountryList(getLocale())}
|
||||
valueKey="code"
|
||||
displayValueKey="name"
|
||||
value={this.state.country}
|
||||
handleBlur={this.handleOnBlur}
|
||||
handleFocus={this.handleOnFocus}
|
||||
errorMessage={intl.formatMessage(messages['empty.country.field.error'])}
|
||||
handleChange={(value) => this.setState({ country: value })}
|
||||
errorCode={this.state.errorCode}
|
||||
readOnly={this.state.readOnly}
|
||||
/>
|
||||
{!(this.showDynamicRegistrationFields)
|
||||
&& (
|
||||
<CountryDropdown
|
||||
name="country"
|
||||
floatingLabel={intl.formatMessage(messages['registration.country.label'])}
|
||||
options={getCountryList(getLocale())}
|
||||
valueKey="code"
|
||||
displayValueKey="name"
|
||||
value={this.state.country}
|
||||
handleBlur={this.handleOnBlur}
|
||||
handleFocus={this.handleOnFocus}
|
||||
errorMessage={intl.formatMessage(messages['empty.country.field.error'])}
|
||||
handleChange={(value) => this.setState({ country: value })}
|
||||
errorCode={this.state.errorCode}
|
||||
readOnly={this.state.readOnly}
|
||||
/>
|
||||
)}
|
||||
{formFields}
|
||||
{(getConfig().MARKETING_EMAILS_OPT_IN)
|
||||
&& (
|
||||
<Form.Checkbox
|
||||
@@ -649,40 +754,20 @@ class RegistrationPage extends React.Component {
|
||||
{intl.formatMessage(messages['registration.opt.in.label'], { siteName: getConfig().SITE_NAME })}
|
||||
</Form.Checkbox>
|
||||
)}
|
||||
<div id="honor-code" className="micro text-muted mt-4">
|
||||
<FormattedMessage
|
||||
id="register.page.terms.of.service.and.honor.code"
|
||||
defaultMessage="By creating an account, you agree to the {tosAndHonorCode} and you acknowledge that {platformName} and each
|
||||
Member process your personal data in accordance with the {privacyPolicy}."
|
||||
description="Text that appears on registration form stating honor code and privacy policy"
|
||||
values={{
|
||||
platformName: getConfig().SITE_NAME,
|
||||
tosAndHonorCode: (
|
||||
<Hyperlink variant="muted" destination={getConfig().TOS_AND_HONOR_CODE || '#'} target="_blank">
|
||||
{intl.formatMessage(messages['terms.of.service.and.honor.code'])}
|
||||
</Hyperlink>
|
||||
),
|
||||
privacyPolicy: (
|
||||
<Hyperlink variant="muted" destination={getConfig().PRIVACY_POLICY || '#'} target="_blank">
|
||||
{intl.formatMessage(messages['privacy.policy'])}
|
||||
</Hyperlink>
|
||||
),
|
||||
}}
|
||||
{!(this.showDynamicRegistrationFields) ? (
|
||||
<HonorCode
|
||||
fieldType="tos_and_honor_code"
|
||||
/>
|
||||
</div>
|
||||
) : <div className="mt-4">{honorCode}</div>}
|
||||
<StatefulButton
|
||||
name="register-user"
|
||||
id="register-user"
|
||||
type="submit"
|
||||
variant="brand"
|
||||
className={classNames(
|
||||
'mt-4 mb-4',
|
||||
{ 'stateful-button-variation1-width': this.state.registerRenameExpVariation === 'variation1' },
|
||||
{ 'stateful-button-width': this.state.registerRenameExpVariation !== 'variation1' },
|
||||
)}
|
||||
className="register-stateful-button-width mt-4 mb-4"
|
||||
state={submitState}
|
||||
labels={{
|
||||
default: this.state.registerRenameExpVariation === 'variation1' ? (
|
||||
intl.formatMessage(messages['create.account.for.free.button'])
|
||||
) : intl.formatMessage(messages['create.account.button']),
|
||||
default: intl.formatMessage(messages['create.account.for.free.button']),
|
||||
pending: '',
|
||||
}}
|
||||
onClick={this.handleSubmit}
|
||||
@@ -745,6 +830,8 @@ class RegistrationPage extends React.Component {
|
||||
}
|
||||
|
||||
RegistrationPage.defaultProps = {
|
||||
extendedProfile: [],
|
||||
fieldDescriptions: {},
|
||||
registrationResult: null,
|
||||
registerNewUser: null,
|
||||
registrationErrorCode: null,
|
||||
@@ -764,6 +851,8 @@ RegistrationPage.defaultProps = {
|
||||
};
|
||||
|
||||
RegistrationPage.propTypes = {
|
||||
extendedProfile: PropTypes.arrayOf(PropTypes.string),
|
||||
fieldDescriptions: PropTypes.shape({}),
|
||||
intl: intlShape.isRequired,
|
||||
getThirdPartyAuthContext: PropTypes.func.isRequired,
|
||||
registerNewUser: PropTypes.func,
|
||||
@@ -817,6 +906,8 @@ const mapStateToProps = state => {
|
||||
validationDecisions: validationsSelector(state),
|
||||
statusCode: state.register.statusCode,
|
||||
usernameSuggestions: usernameSuggestionsSelector(state),
|
||||
fieldDescriptions: fieldDescriptionSelector(state),
|
||||
extendedProfile: extendedProfileSelector(state),
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
60
src/register/TermsOfService.jsx
Normal file
60
src/register/TermsOfService.jsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n';
|
||||
import { Hyperlink, Form } from '@edx/paragon';
|
||||
import messages from './messages';
|
||||
|
||||
const TermsOfService = (props) => {
|
||||
const {
|
||||
intl, errorMessage, onChangeHandler, value,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<div id="terms-of-service" className="micro text-muted">
|
||||
<Form.Checkbox
|
||||
className="opt-checkbox mt-1"
|
||||
id="tos"
|
||||
checked={value}
|
||||
name="terms_of_service"
|
||||
value={value}
|
||||
onChange={onChangeHandler}
|
||||
>
|
||||
<FormattedMessage
|
||||
id="register.page.terms.of.service"
|
||||
defaultMessage="I agree to the {platformName} {termsOfService}"
|
||||
description="Text that appears on registration form stating terms of service.
|
||||
It is a legal document that users must agree to."
|
||||
values={{
|
||||
platformName: getConfig().SITE_NAME,
|
||||
termsOfService: (
|
||||
<Hyperlink variant="muted" destination={getConfig().TOS_LINK || '#'} target="_blank">
|
||||
{intl.formatMessage(messages['terms.of.service'])}
|
||||
</Hyperlink>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
</Form.Checkbox>
|
||||
{errorMessage && (
|
||||
<Form.Control.Feedback type="invalid" className="form-text-size" hasIcon={false}>
|
||||
{errorMessage}
|
||||
</Form.Control.Feedback>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
TermsOfService.defaultProps = {
|
||||
errorMessage: '',
|
||||
value: false,
|
||||
};
|
||||
|
||||
TermsOfService.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
errorMessage: PropTypes.string,
|
||||
onChangeHandler: PropTypes.func.isRequired,
|
||||
value: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default injectIntl(TermsOfService);
|
||||
@@ -1,3 +1,10 @@
|
||||
// Registration Fields
|
||||
export const FIELDS = {
|
||||
COUNTRY: 'country',
|
||||
HONOR_CODE: 'honor_code',
|
||||
TERMS_OF_SERVICE: 'terms_of_service',
|
||||
};
|
||||
|
||||
// Registration Error Codes
|
||||
export const FORBIDDEN_REQUEST = 'forbidden-request';
|
||||
export const FORM_SUBMISSION_ERROR = 'form-submission-error';
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import {
|
||||
REGISTRATION_FORM, REGISTER_NEW_USER, REGISTER_FORM_VALIDATIONS, REGISTER_CLEAR_USERNAME_SUGGESTIONS,
|
||||
REGISTRATION_FORM,
|
||||
REGISTER_NEW_USER,
|
||||
REGISTER_FORM_VALIDATIONS,
|
||||
REGISTER_CLEAR_USERNAME_SUGGESTIONS,
|
||||
} from './actions';
|
||||
|
||||
import { DEFAULT_STATE, PENDING_STATE } from '../../data/constants';
|
||||
import {
|
||||
DEFAULT_STATE,
|
||||
PENDING_STATE,
|
||||
} from '../../data/constants';
|
||||
|
||||
export const defaultState = {
|
||||
registrationError: {},
|
||||
@@ -11,6 +17,9 @@ export const defaultState = {
|
||||
validations: null,
|
||||
statusCode: null,
|
||||
usernameSuggestions: [],
|
||||
extendedProfile: [],
|
||||
fieldDescriptions: {},
|
||||
formRenderState: DEFAULT_STATE,
|
||||
};
|
||||
|
||||
const reducer = (state = defaultState, action) => {
|
||||
|
||||
@@ -55,7 +55,6 @@ export function* fetchRealtimeValidations(action) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default function* saga() {
|
||||
yield takeEvery(REGISTER_NEW_USER.BASE, handleNewUserRegistration);
|
||||
yield takeEvery(REGISTER_FORM_VALIDATIONS.BASE, fetchRealtimeValidations);
|
||||
|
||||
@@ -14,6 +14,9 @@ describe('register reducer', () => {
|
||||
validations: null,
|
||||
statusCode: null,
|
||||
usernameSuggestions: [],
|
||||
extendedProfile: [],
|
||||
fieldDescriptions: {},
|
||||
formRenderState: DEFAULT_STATE,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -59,11 +59,6 @@ const messages = defineMessages({
|
||||
description: 'Help text for email field on registration page',
|
||||
},
|
||||
// Form buttons
|
||||
'create.account.button': {
|
||||
id: 'create.account.button',
|
||||
defaultMessage: 'Create an account',
|
||||
description: 'Button label that appears on register page',
|
||||
},
|
||||
'create.account.for.free.button': {
|
||||
id: 'create.account.for.free.button',
|
||||
defaultMessage: 'Create an account for free',
|
||||
@@ -193,6 +188,16 @@ const messages = defineMessages({
|
||||
defaultMessage: 'Privacy Policy',
|
||||
description: 'Text for the hyperlink that redirects user to privacy policy',
|
||||
},
|
||||
'honor.code': {
|
||||
id: 'honor.code',
|
||||
defaultMessage: 'Honor Code',
|
||||
description: 'Text for the hyperlink that redirects user to the honor code',
|
||||
},
|
||||
'terms.of.service': {
|
||||
id: 'terms.of.service',
|
||||
defaultMessage: 'Terms of Service',
|
||||
description: 'Text for the hyperlink that redirects user to the terms of service',
|
||||
},
|
||||
// Optional fields
|
||||
'registration.year.of.birth.label': {
|
||||
id: 'registration.year.of.birth.label',
|
||||
|
||||
59
src/register/tests/HonorCode.test.jsx
Normal file
59
src/register/tests/HonorCode.test.jsx
Normal file
@@ -0,0 +1,59 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import { mergeConfig } from '@edx/frontend-platform';
|
||||
import HonorCode from '../HonorCode';
|
||||
|
||||
const IntlHonorCode = injectIntl(HonorCode);
|
||||
|
||||
describe('HonorCodeTest', () => {
|
||||
mergeConfig({
|
||||
PRIVACY_POLICY: 'http://privacy-policy.com',
|
||||
TOS_AND_HONOR_CODE: 'http://tos-and-honot-code.com',
|
||||
});
|
||||
let value = false;
|
||||
|
||||
const changeHandler = (e) => {
|
||||
value = e.target.checked;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
value = false;
|
||||
});
|
||||
|
||||
it('should render error msg if honor code is not checked', () => {
|
||||
const honorCode = mount(
|
||||
<IntlProvider locale="en">
|
||||
<IntlHonorCode errorMessage="You must agree to the edx Honor Code" onChangeHandler={changeHandler} />
|
||||
</IntlProvider>,
|
||||
);
|
||||
expect(honorCode.find('.form-text-size').last().text()).toEqual('You must agree to the edx Honor Code');
|
||||
});
|
||||
|
||||
it('should render Honor code field', () => {
|
||||
const expectedMsg = 'I agree to the Your Platform Name Here Honor Codein a new tab';
|
||||
const honorCode = mount(
|
||||
<IntlProvider locale="en">
|
||||
<IntlHonorCode onChangeHandler={changeHandler} />
|
||||
</IntlProvider>,
|
||||
);
|
||||
|
||||
honorCode.find('#honor-code').last().simulate('change', { target: { checked: true, type: 'checkbox' } });
|
||||
expect(honorCode.find('#honor-code').find('label').text()).toEqual(expectedMsg);
|
||||
expect(value).toEqual(true);
|
||||
});
|
||||
|
||||
it('should render Terms of Service and Honor code field', () => {
|
||||
const HonorCodeProps = mount(
|
||||
<IntlProvider locale="en">
|
||||
<IntlHonorCode fieldType="tos_and_honor_code" onChangeHandler={changeHandler} />
|
||||
</IntlProvider>,
|
||||
);
|
||||
const expectedMsg = 'By creating an account, you agree to the Terms of Service and Honor Codein a new tab and you '
|
||||
+ 'acknowledge that Your Platform Name Here and each Member process your personal data in '
|
||||
+ 'accordance with the Privacy Policyin a new tab.';
|
||||
const field = HonorCodeProps.find('#honor-code');
|
||||
expect(field.text()).toEqual(expectedMsg);
|
||||
});
|
||||
});
|
||||
@@ -16,7 +16,9 @@ import {
|
||||
registerNewUser,
|
||||
resetRegistrationForm,
|
||||
} from '../data/actions';
|
||||
import { FORBIDDEN_REQUEST, INTERNAL_SERVER_ERROR, TPA_SESSION_EXPIRED } from '../data/constants';
|
||||
import {
|
||||
FIELDS, FORBIDDEN_REQUEST, INTERNAL_SERVER_ERROR, TPA_SESSION_EXPIRED,
|
||||
} from '../data/constants';
|
||||
import RegistrationFailureMessage from '../RegistrationFailure';
|
||||
import RegistrationPage from '../RegistrationPage';
|
||||
|
||||
@@ -404,7 +406,7 @@ describe('RegistrationPage', () => {
|
||||
|
||||
it('should match default button state', () => {
|
||||
const registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||
expect(registrationPage.find('button[type="submit"] span').first().text()).toEqual('Create an account');
|
||||
expect(registrationPage.find('button[type="submit"] span').first().text()).toEqual('Create an account for free');
|
||||
});
|
||||
|
||||
it('should match pending button state', () => {
|
||||
@@ -773,4 +775,87 @@ describe('RegistrationPage', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('TestDynamicFields', () => {
|
||||
it('should render fields returned by backend', () => {
|
||||
mergeConfig({
|
||||
ENABLE_DYNAMIC_REGISTRATION_FIELDS: true,
|
||||
});
|
||||
store = mockStore({
|
||||
...initialState,
|
||||
commonComponents: {
|
||||
...initialState.commonComponents,
|
||||
fieldDescriptions: {
|
||||
country: { name: 'country', error_message: true },
|
||||
profession: { name: 'profession', type: 'text', label: 'Profession' },
|
||||
honor_code: { name: FIELDS.HONOR_CODE, error_message: 'You must agree to Honor Code of our site' },
|
||||
terms_of_service: {
|
||||
name: FIELDS.TERMS_OF_SERVICE,
|
||||
error_message: 'You must agree to the Terms and Service agreement of our site',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
const registerPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||
expect(registerPage.find('#country').exists()).toBeTruthy();
|
||||
expect(registerPage.find('#profession').exists()).toBeTruthy();
|
||||
expect(registerPage.find('#honor-code').exists()).toBeTruthy();
|
||||
expect(registerPage.find('#tos').exists()).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should submit form with fields returned by backend in payload', () => {
|
||||
jest.spyOn(global.Date, 'now').mockImplementation(() => 0);
|
||||
store = mockStore({
|
||||
...initialState,
|
||||
commonComponents: {
|
||||
...initialState.commonComponents,
|
||||
fieldDescriptions: {
|
||||
country: { name: 'country', error_message: true },
|
||||
profession: { name: 'profession', type: 'text', label: 'Profession' },
|
||||
honor_code: { name: 'honor_code', type: 'tos_and_honor_code' },
|
||||
},
|
||||
extendedProfile: ['profession'],
|
||||
},
|
||||
});
|
||||
|
||||
const payload = {
|
||||
name: 'John Doe',
|
||||
username: 'john_doe',
|
||||
email: 'john.doe@example.com',
|
||||
password: 'password1',
|
||||
country: 'Pakistan',
|
||||
totalRegistrationTime: 0,
|
||||
is_authn_mfe: true,
|
||||
honor_code: true,
|
||||
extended_profile: [{ field_name: 'profession', field_value: 'Engineer' }],
|
||||
};
|
||||
|
||||
store.dispatch = jest.fn(store.dispatch);
|
||||
const registerPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||
|
||||
populateRequiredFields(registerPage, payload);
|
||||
registerPage.find('input#profession').simulate('change', { target: { value: 'Engineer', name: 'profession' } });
|
||||
registerPage.find('button.btn-brand').simulate('click');
|
||||
expect(store.dispatch).toHaveBeenCalledWith(registerNewUser({ ...payload, country: 'PK' }));
|
||||
});
|
||||
|
||||
it('should show error message for fields returned by backend', () => {
|
||||
store = mockStore({
|
||||
...initialState,
|
||||
commonComponents: {
|
||||
...initialState.commonComponents,
|
||||
fieldDescriptions: {
|
||||
profession: {
|
||||
name: 'profession', type: 'text', label: 'Profession', error_message: 'Enter profession',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const registrationPage = mount(reduxWrapper(<IntlRegistrationPage {...props} />));
|
||||
registrationPage.find('button.btn-brand').simulate('click');
|
||||
|
||||
expect(registrationPage.find('#profession-error').last().text()).toEqual('Enter profession');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
55
src/register/tests/TermsOfService.test.jsx
Normal file
55
src/register/tests/TermsOfService.test.jsx
Normal file
@@ -0,0 +1,55 @@
|
||||
import React from 'react';
|
||||
import { mount } from 'enzyme';
|
||||
|
||||
import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n';
|
||||
import { mergeConfig } from '@edx/frontend-platform';
|
||||
import TermsOfService from '../TermsOfService';
|
||||
|
||||
const IntlTermsOfService = injectIntl(TermsOfService);
|
||||
|
||||
describe('TermsOfServiceTest', () => {
|
||||
mergeConfig({
|
||||
TOS_LINK: 'http://tos-and-honot-code.com',
|
||||
});
|
||||
let value = false;
|
||||
|
||||
const changeHandler = (e) => {
|
||||
value = e.target.checked;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
value = false;
|
||||
});
|
||||
|
||||
it('should render error msg if Terms of Service checkbox is not checked', () => {
|
||||
const errorMessage = 'You must agree to the edx Terms of Service';
|
||||
const termsOfService = mount(
|
||||
<IntlProvider locale="en">
|
||||
<IntlTermsOfService errorMessage={errorMessage} onChangeHandler={changeHandler} />
|
||||
</IntlProvider>,
|
||||
);
|
||||
expect(termsOfService.find('.form-text-size').last().text()).toEqual(errorMessage);
|
||||
});
|
||||
|
||||
it('should render Terms of Service field', () => {
|
||||
const termsOfService = mount(
|
||||
<IntlProvider locale="en">
|
||||
<IntlTermsOfService onChangeHandler={changeHandler} />
|
||||
</IntlProvider>,
|
||||
);
|
||||
const expectedMsg = 'I agree to the Your Platform Name Here Terms of Servicein a new tab';
|
||||
expect(termsOfService.find('#terms-of-service').find('label').text()).toEqual(expectedMsg);
|
||||
expect(value).toEqual(false);
|
||||
});
|
||||
|
||||
it('should change value when Terms of Service field is checked', () => {
|
||||
const termsOfService = mount(
|
||||
<IntlProvider locale="en">
|
||||
<IntlTermsOfService onChangeHandler={changeHandler} />
|
||||
</IntlProvider>,
|
||||
);
|
||||
const field = termsOfService.find('input#tos');
|
||||
field.simulate('change', { target: { checked: true, type: 'checkbox' } });
|
||||
expect(value).toEqual(true);
|
||||
});
|
||||
});
|
||||
@@ -174,7 +174,7 @@ const ResetPasswordPage = (props) => {
|
||||
<ResetPasswordFailure errorCode={errorCode} errorMsg={props.errorMsg} />
|
||||
<h4>{intl.formatMessage(messages['reset.password'])}</h4>
|
||||
<p className="mb-4">{intl.formatMessage(messages['reset.password.page.instructions'])}</p>
|
||||
<Form>
|
||||
<Form id="set-reset-password-form" name="set-reset-password-form">
|
||||
<PasswordField
|
||||
name="newPassword"
|
||||
value={newPassword}
|
||||
@@ -194,6 +194,8 @@ const ResetPasswordPage = (props) => {
|
||||
floatingLabel={intl.formatMessage(messages['confirm.password.label'])}
|
||||
/>
|
||||
<StatefulButton
|
||||
id="submit-new-password"
|
||||
name="submit-new-password"
|
||||
type="submit"
|
||||
variant="brand"
|
||||
className="stateful-button-width"
|
||||
|
||||
@@ -162,7 +162,7 @@ const WelcomePage = (props) => {
|
||||
<p>{intl.formatMessage(messages['welcome.page.error.message'])}</p>
|
||||
</Alert>
|
||||
) : null}
|
||||
<Form>
|
||||
<Form id="welcome-page-profile-form" name="welcome-page-profile-form">
|
||||
<Form.Group controlId="levelOfEducation">
|
||||
<Form.Control
|
||||
as="select"
|
||||
@@ -217,6 +217,8 @@ const WelcomePage = (props) => {
|
||||
</span>
|
||||
<div className="d-flex mt-4">
|
||||
<StatefulButton
|
||||
name="submit-profile"
|
||||
id="submit-profile"
|
||||
type="submit"
|
||||
variant="brand"
|
||||
className="login-button-width"
|
||||
@@ -229,6 +231,8 @@ const WelcomePage = (props) => {
|
||||
onMouseDown={(e) => e.preventDefault()}
|
||||
/>
|
||||
<StatefulButton
|
||||
id="skip-profile"
|
||||
name="skip-profile"
|
||||
className="text-gray-700 font-weight-500"
|
||||
type="submit"
|
||||
variant="link"
|
||||
|
||||
Reference in New Issue
Block a user