Merge pull request #1354 from open-craft/kshitij/plugin-slot
feat: Add plugin slot for login page
This commit is contained in:
181
package-lock.json
generated
181
package-lock.json
generated
@@ -16,6 +16,7 @@
|
||||
"@fortawesome/free-brands-svg-icons": "6.7.2",
|
||||
"@fortawesome/free-solid-svg-icons": "6.7.2",
|
||||
"@fortawesome/react-fontawesome": "0.2.6",
|
||||
"@openedx/frontend-plugin-framework": "^1.7.0",
|
||||
"@openedx/paragon": "^23.4.2",
|
||||
"@optimizely/react-sdk": "^2.9.1",
|
||||
"@redux-devtools/extension": "3.3.0",
|
||||
@@ -286,7 +287,6 @@
|
||||
"integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.2.0",
|
||||
"@babel/code-frame": "^7.24.7",
|
||||
@@ -2443,7 +2443,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^14 || ^16 || >=18"
|
||||
},
|
||||
@@ -2466,7 +2465,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": "^14 || ^16 || >=18"
|
||||
}
|
||||
@@ -3166,7 +3164,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.7.2.tgz",
|
||||
"integrity": "sha512-yxtOBWDrdi5DD5o1pmVdq3WMCvnobT0LU6R8RyyVXPvFRd2o79/0NCuQoCjNTeZz9EzA9xS3JxNWfv54RIHFEA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-common-types": "6.7.2"
|
||||
},
|
||||
@@ -5978,7 +5975,6 @@
|
||||
"integrity": "sha512-Iu4/GPq90Xr/MSWnonn2qX8VDhI89HN7KOYBZ0/sxmAQgvXXNc7OYNC7kumvzbYzKueJQTyZoUYS7UjKB/n1WA==",
|
||||
"devOptional": true,
|
||||
"license": "AGPL-3.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/cli": "7.24.8",
|
||||
"@babel/core": "7.24.9",
|
||||
@@ -6497,7 +6493,6 @@
|
||||
"integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@jest/core": "^29.7.0",
|
||||
"@jest/types": "^29.6.3",
|
||||
@@ -7060,12 +7055,88 @@
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@openedx/frontend-plugin-framework/-/frontend-plugin-framework-1.7.0.tgz",
|
||||
"integrity": "sha512-8tGkuHvtzhbqb9dU4sXUtR0K44+Hjh1uGR6DvhZAt9wSKQC1v4RBk34ef8DFzQhoNQa/Jtn6BJuta4Un6MmHmw==",
|
||||
"license": "AGPL-3.0",
|
||||
"dependencies": {
|
||||
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
|
||||
"classnames": "^2.3.2",
|
||||
"core-js": "3.37.1",
|
||||
"react-redux": "8.1.1",
|
||||
"redux": "4.2.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@edx/frontend-platform": "^7.0.0 || ^8.0.0",
|
||||
"@openedx/paragon": "^21.0.0 || ^22.0.0 || ^23.0.0",
|
||||
"prop-types": "^15.8.0",
|
||||
"react": "^17.0.0 || ^18.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0",
|
||||
"react-error-boundary": "^4.0.11"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/core-js": {
|
||||
"version": "3.37.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.37.1.tgz",
|
||||
"integrity": "sha512-Xn6qmxrQZyB0FFY8E3bgRXei3lWDJHhvI+u0q9TKIYM49G8pAr0FgnnrFRAmsbptZL1yxRADVXn+x5AGsbBfyw==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/core-js"
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/react-is": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
"integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@openedx/frontend-plugin-framework/node_modules/react-redux": {
|
||||
"version": "8.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.1.1.tgz",
|
||||
"integrity": "sha512-5W0QaKtEhj+3bC0Nj0NkqkhIv8gLADH/2kYFMTHxCVqQILiWzLv6MaLuV5wJU3BQEdHKzTfcvPN0WMS6SC1oyA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.1",
|
||||
"@types/hoist-non-react-statics": "^3.3.1",
|
||||
"@types/use-sync-external-store": "^0.0.3",
|
||||
"hoist-non-react-statics": "^3.3.2",
|
||||
"react-is": "^18.0.0",
|
||||
"use-sync-external-store": "^1.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "^16.8 || ^17.0 || ^18.0",
|
||||
"@types/react-dom": "^16.8 || ^17.0 || ^18.0",
|
||||
"react": "^16.8 || ^17.0 || ^18.0",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0",
|
||||
"react-native": ">=0.59",
|
||||
"redux": "^4 || ^5.0.0-beta.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
},
|
||||
"react-native": {
|
||||
"optional": true
|
||||
},
|
||||
"redux": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@openedx/paragon": {
|
||||
"version": "23.15.0",
|
||||
"resolved": "https://registry.npmjs.org/@openedx/paragon/-/paragon-23.15.0.tgz",
|
||||
"integrity": "sha512-wmsO0tOcLGLsinIHWi2PvGIQ0PGlfB8c789NTPCUiY1AGfkpboOEpZc+7f3m1imCdJ3OvBula5lYV9BpuQ0CSQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"workspaces": [
|
||||
"example",
|
||||
"component-generator",
|
||||
@@ -7755,7 +7826,6 @@
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/popperjs"
|
||||
@@ -8063,7 +8133,6 @@
|
||||
"integrity": "sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.21.3",
|
||||
"@svgr/babel-preset": "8.1.0",
|
||||
@@ -8274,7 +8343,8 @@
|
||||
"version": "5.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
|
||||
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
"version": "7.20.5",
|
||||
@@ -8636,7 +8706,6 @@
|
||||
"integrity": "sha512-gUuVEAK4/u6F9wRLznPUU4WGUacSEBDPoC2TrBkw3GAnOLHBL45QdfHOXp1kJ4ypBGLxTOB+t7NJLpKoC3gznQ==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~7.11.0"
|
||||
}
|
||||
@@ -8686,12 +8755,12 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/react": {
|
||||
"version": "19.1.13",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.13.tgz",
|
||||
"integrity": "sha512-hHkbU/eoO3EG5/MZkuFSKmYqPbSVk5byPFa3e7y/8TybHiLMACgI8seVYlicwk7H5K/rI2px9xrQp/C+AUDTiQ==",
|
||||
"version": "18.3.26",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.26.tgz",
|
||||
"integrity": "sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
@@ -8787,6 +8856,12 @@
|
||||
"devOptional": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/use-sync-external-store": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz",
|
||||
"integrity": "sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/warning": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz",
|
||||
@@ -8826,7 +8901,6 @@
|
||||
"integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.4.0",
|
||||
"@typescript-eslint/scope-manager": "5.62.0",
|
||||
@@ -8875,7 +8949,6 @@
|
||||
"integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==",
|
||||
"devOptional": true,
|
||||
"license": "BSD-2-Clause",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "5.62.0",
|
||||
"@typescript-eslint/types": "5.62.0",
|
||||
@@ -9613,7 +9686,6 @@
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -9711,7 +9783,6 @@
|
||||
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
@@ -9780,7 +9851,6 @@
|
||||
"resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-4.25.2.tgz",
|
||||
"integrity": "sha512-lYx98L6kb1VvXypbPI7Z54C4BJB2VT5QvOYthvPq6/POufZj+YdyeZSKjoLBKHJgGmYWQTHOKtcCTdKf98WOCA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@algolia/cache-browser-local-storage": "4.25.2",
|
||||
"@algolia/cache-common": "4.25.2",
|
||||
@@ -10256,7 +10326,6 @@
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.12.0.tgz",
|
||||
"integrity": "sha512-oXTDccv8PcfjZmPGlWsPSwtOJCZ/b6W5jAMCNcfwJbCzDckwG0jrYJFaWH1yvivfCXjVzV/SPDEhMB3Q+DSurg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"follow-redirects": "^1.15.6",
|
||||
"form-data": "^4.0.4",
|
||||
@@ -10778,7 +10847,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.8.2",
|
||||
"caniuse-lite": "^1.0.30001741",
|
||||
@@ -12986,7 +13054,8 @@
|
||||
"version": "0.5.16",
|
||||
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
|
||||
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/dom-converter": {
|
||||
"version": "0.2.0",
|
||||
@@ -13556,7 +13625,6 @@
|
||||
"deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
"@eslint-community/regexpp": "^4.4.0",
|
||||
@@ -13614,7 +13682,6 @@
|
||||
"integrity": "sha512-T75QYQVQX57jiNgpF9r1KegMICE94VYwoFQyMGhrvc+lB8YF2E/M/PYDaQe1AJcWaEgqLE+ErXV1Og/+6Vyzew==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"eslint-config-airbnb-base": "^15.0.0",
|
||||
"object.assign": "^4.1.2",
|
||||
@@ -13657,7 +13724,6 @@
|
||||
"integrity": "sha512-GPxI5URre6dDpJ0CtcthSZVBAfI+Uw7un5OYNVxP2EYi3H81Jw701yFP7AU+/vCE7xBtFmjge7kfhhk4+RAiig==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"eslint-config-airbnb-base": "^15.0.0"
|
||||
},
|
||||
@@ -14060,7 +14126,6 @@
|
||||
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@rtsao/scc": "^1.1.0",
|
||||
"array-includes": "^3.1.9",
|
||||
@@ -14118,7 +14183,6 @@
|
||||
"integrity": "sha512-63Bog4iIethyo8smBklORknVjB0T2dwB8Mr/hIC+fBS0uyHdYYpzM/Ed+YC8VxTjlXHEWFOdmgwcDn1U2L9VCA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.20.7",
|
||||
"aria-query": "^5.1.3",
|
||||
@@ -14157,7 +14221,6 @@
|
||||
"integrity": "sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"array-includes": "^3.1.6",
|
||||
"array.prototype.flatmap": "^1.3.1",
|
||||
@@ -14189,7 +14252,6 @@
|
||||
"integrity": "sha512-Ck77j8hF7l9N4S/rzSLOWEKpn994YH6iwUK8fr9mXIaQvGpQYmOnQLbiue1u5kI5T1y+gdgqosnEAO9NCz0DBg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
@@ -17822,7 +17884,6 @@
|
||||
"integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.3",
|
||||
@@ -22576,7 +22637,6 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.7",
|
||||
"picocolors": "^1.1.1",
|
||||
@@ -23361,6 +23421,7 @@
|
||||
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
|
||||
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1",
|
||||
"ansi-styles": "^5.0.0",
|
||||
@@ -23375,6 +23436,7 @@
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
|
||||
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
@@ -23386,7 +23448,8 @@
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
|
||||
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/process": {
|
||||
"version": "0.11.10",
|
||||
@@ -23423,7 +23486,6 @@
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
@@ -23681,7 +23743,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
|
||||
"integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0"
|
||||
},
|
||||
@@ -23840,7 +23901,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
|
||||
"integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"scheduler": "^0.23.2"
|
||||
@@ -23878,6 +23938,19 @@
|
||||
"node": ">= 12"
|
||||
}
|
||||
},
|
||||
"node_modules/react-error-boundary": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-4.1.2.tgz",
|
||||
"integrity": "sha512-GQDxZ5Jd+Aq/qUxbCm1UtzmL/s++V7zKgE8yMktJiCQXCCFZnMZh9ng+6/Ne6PjNSXH0L9CjeOEREfRnq6Duag==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.13.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-error-overlay": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.1.0.tgz",
|
||||
@@ -24039,16 +24112,6 @@
|
||||
"tslib": "2"
|
||||
}
|
||||
},
|
||||
"node_modules/react-intl/node_modules/@types/react": {
|
||||
"version": "18.3.24",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.24.tgz",
|
||||
"integrity": "sha512-0dLEBsA1kI3OezMBF8nSsb7Nk19ZnsyE1LLhB8r27KbgU5H4pvuqZLdtE+aUkJVoXgTVuA+iLIwmZ0TuK4tx6A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/prop-types": "*",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
@@ -24116,7 +24179,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz",
|
||||
"integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.15.4",
|
||||
"@types/react-redux": "^7.1.20",
|
||||
@@ -24149,7 +24211,6 @@
|
||||
"integrity": "sha512-FPvF2XxTSikpJxcr+bHut2H4gJ17+18Uy20D5/F+SKzFap62R3cM5wH6b8WN3LyGSYeQilLEcJcR1fjBSI2S1A==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -24239,7 +24300,6 @@
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.1.tgz",
|
||||
"integrity": "sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@remix-run/router": "1.23.0",
|
||||
"react-router": "6.30.1"
|
||||
@@ -24442,7 +24502,6 @@
|
||||
"resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
|
||||
"integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.9.2"
|
||||
}
|
||||
@@ -25102,7 +25161,6 @@
|
||||
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
@@ -26104,7 +26162,6 @@
|
||||
"integrity": "sha512-+xU0IA1StzqAqFs/QtXkK+XJa7wpS4X5H+JQccRKsRCElgeLGocFU1U/UMvMUylKFw6vwGV+Y/a2wb2pm5rFFQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@bundled-es-modules/deepmerge": "^4.3.1",
|
||||
"@bundled-es-modules/glob": "^10.4.2",
|
||||
@@ -26622,7 +26679,6 @@
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -26755,7 +26811,6 @@
|
||||
"integrity": "sha512-HO3GyiWn2qvTQA4kTgjDcXiMwYQt68a1Y8+JuLRVpdIzm+UOLSHgl/XqR4c6nzJkq5rOkjc02O2I7P7l/Yof0Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"bs-logger": "^0.2.6",
|
||||
"fast-json-stable-stringify": "^2.1.0",
|
||||
@@ -26910,8 +26965,7 @@
|
||||
"version": "2.8.1",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
|
||||
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
|
||||
"license": "0BSD",
|
||||
"peer": true
|
||||
"license": "0BSD"
|
||||
},
|
||||
"node_modules/tsutils": {
|
||||
"version": "3.21.0",
|
||||
@@ -26965,7 +27019,6 @@
|
||||
"integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
|
||||
"devOptional": true,
|
||||
"license": "(MIT OR CC0-1.0)",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
@@ -27071,7 +27124,6 @@
|
||||
"integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -27258,7 +27310,6 @@
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"napi-postinstall": "^0.3.0"
|
||||
},
|
||||
@@ -27448,6 +27499,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
|
||||
"integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/util": {
|
||||
"version": "0.12.5",
|
||||
"resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz",
|
||||
@@ -27618,7 +27678,6 @@
|
||||
"integrity": "sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.7",
|
||||
"@types/estree": "^1.0.8",
|
||||
@@ -27727,7 +27786,6 @@
|
||||
"integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@discoveryjs/json-ext": "^0.5.0",
|
||||
"@webpack-cli/configtest": "^2.1.1",
|
||||
@@ -27808,7 +27866,6 @@
|
||||
"integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@types/bonjour": "^3.5.9",
|
||||
"@types/connect-history-api-fallback": "^1.3.5",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"@fortawesome/free-brands-svg-icons": "6.7.2",
|
||||
"@fortawesome/free-solid-svg-icons": "6.7.2",
|
||||
"@fortawesome/react-fontawesome": "0.2.6",
|
||||
"@openedx/frontend-plugin-framework": "^1.7.0",
|
||||
"@openedx/paragon": "^23.4.2",
|
||||
"@optimizely/react-sdk": "^2.9.1",
|
||||
"@redux-devtools/extension": "3.3.0",
|
||||
|
||||
@@ -1,26 +1,17 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
useCallback, useEffect, useMemo, useState,
|
||||
} from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import {
|
||||
Form, StatefulButton,
|
||||
} from '@openedx/paragon';
|
||||
import { Form, StatefulButton } from '@openedx/paragon';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import AccountActivationMessage from './AccountActivationMessage';
|
||||
import {
|
||||
backupLoginFormBegin,
|
||||
dismissPasswordResetBanner,
|
||||
loginRequest,
|
||||
} from './data/actions';
|
||||
import { INVALID_FORM, TPA_AUTHENTICATION_FAILURE } from './data/constants';
|
||||
import LoginFailureMessage from './LoginFailure';
|
||||
import messages from './messages';
|
||||
import {
|
||||
FormGroup,
|
||||
InstitutionLogistration,
|
||||
@@ -28,13 +19,12 @@ import {
|
||||
RedirectLogistration,
|
||||
ThirdPartyAuthAlert,
|
||||
} from '../common-components';
|
||||
import AccountActivationMessage from './AccountActivationMessage';
|
||||
import { getThirdPartyAuthContext } from '../common-components/data/actions';
|
||||
import { thirdPartyAuthContextSelector } from '../common-components/data/selectors';
|
||||
import EnterpriseSSO from '../common-components/EnterpriseSSO';
|
||||
import ThirdPartyAuth from '../common-components/ThirdPartyAuth';
|
||||
import {
|
||||
DEFAULT_STATE, PENDING_STATE, RESET_PAGE,
|
||||
} from '../data/constants';
|
||||
import { PENDING_STATE, RESET_PAGE } from '../data/constants';
|
||||
import {
|
||||
getActivationStatus,
|
||||
getAllPossibleQueryParams,
|
||||
@@ -43,37 +33,57 @@ import {
|
||||
updatePathWithQueryParams,
|
||||
} from '../data/utils';
|
||||
import ResetPasswordSuccess from '../reset-password/ResetPasswordSuccess';
|
||||
import { backupLoginFormBegin, dismissPasswordResetBanner, loginRequest } from './data/actions';
|
||||
import { INVALID_FORM, TPA_AUTHENTICATION_FAILURE } from './data/constants';
|
||||
import LoginFailureMessage from './LoginFailure';
|
||||
import messages from './messages';
|
||||
|
||||
const LoginPage = (props) => {
|
||||
const LoginPage = ({
|
||||
institutionLogin,
|
||||
handleInstitutionLogin,
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const backupFormState = useCallback((data) => dispatch(backupLoginFormBegin(data)), [dispatch]);
|
||||
const getTPADataFromBackend = useCallback(() => dispatch(getThirdPartyAuthContext()), [dispatch]);
|
||||
const {
|
||||
backedUpFormData,
|
||||
loginErrorCode,
|
||||
loginErrorContext,
|
||||
loginResult,
|
||||
shouldBackupState,
|
||||
thirdPartyAuthContext: {
|
||||
providers,
|
||||
currentProvider,
|
||||
secondaryProviders,
|
||||
finishAuthUrl,
|
||||
platformName,
|
||||
errorMessage: thirdPartyErrorMessage,
|
||||
},
|
||||
thirdPartyAuthApiStatus,
|
||||
institutionLogin,
|
||||
showResetPasswordSuccessBanner,
|
||||
submitState,
|
||||
// Actions
|
||||
backupFormState,
|
||||
handleInstitutionLogin,
|
||||
getTPADataFromBackend,
|
||||
} = props;
|
||||
thirdPartyAuthContext,
|
||||
thirdPartyAuthApiStatus,
|
||||
} = useSelector((state) => ({
|
||||
backedUpFormData: state.login.loginFormData,
|
||||
loginErrorCode: state.login.loginErrorCode,
|
||||
loginErrorContext: state.login.loginErrorContext,
|
||||
loginResult: state.login.loginResult,
|
||||
shouldBackupState: state.login.shouldBackupState,
|
||||
showResetPasswordSuccessBanner: state.login.showResetPasswordSuccessBanner,
|
||||
submitState: state.login.submitState,
|
||||
thirdPartyAuthContext: thirdPartyAuthContextSelector(state),
|
||||
thirdPartyAuthApiStatus: state.commonComponents.thirdPartyAuthApiStatus,
|
||||
}));
|
||||
const {
|
||||
providers,
|
||||
currentProvider,
|
||||
secondaryProviders,
|
||||
finishAuthUrl,
|
||||
platformName,
|
||||
errorMessage: thirdPartyErrorMessage,
|
||||
} = thirdPartyAuthContext;
|
||||
const { formatMessage } = useIntl();
|
||||
const activationMsgType = getActivationStatus();
|
||||
const queryParams = useMemo(() => getAllPossibleQueryParams(), []);
|
||||
|
||||
const [formFields, setFormFields] = useState({ ...backedUpFormData.formFields });
|
||||
const [errorCode, setErrorCode] = useState({ type: '', count: 0, context: {} });
|
||||
const [errorCode, setErrorCode] = useState({
|
||||
type: '',
|
||||
count: 0,
|
||||
context: {},
|
||||
});
|
||||
const [errors, setErrors] = useState({ ...backedUpFormData.errors });
|
||||
const tpaHint = getTpaHint();
|
||||
|
||||
@@ -87,7 +97,7 @@ const LoginPage = (props) => {
|
||||
payload.tpa_hint = tpaHint;
|
||||
}
|
||||
getTPADataFromBackend(payload);
|
||||
}, [getTPADataFromBackend, queryParams, tpaHint]);
|
||||
}, [queryParams, tpaHint, getTPADataFromBackend]);
|
||||
/**
|
||||
* Backup the login form in redux when login page is toggled.
|
||||
*/
|
||||
@@ -98,7 +108,7 @@ const LoginPage = (props) => {
|
||||
errors: { ...errors },
|
||||
});
|
||||
}
|
||||
}, [shouldBackupState, formFields, errors, backupFormState]);
|
||||
}, [backupFormState, shouldBackupState, formFields, errors]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loginErrorCode) {
|
||||
@@ -123,7 +133,10 @@ const LoginPage = (props) => {
|
||||
}, [thirdPartyErrorMessage]);
|
||||
|
||||
const validateFormFields = (payload) => {
|
||||
const { emailOrUsername, password } = payload;
|
||||
const {
|
||||
emailOrUsername,
|
||||
password,
|
||||
} = payload;
|
||||
const fieldErrors = { ...errors };
|
||||
|
||||
if (emailOrUsername === '') {
|
||||
@@ -141,14 +154,18 @@ const LoginPage = (props) => {
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
if (showResetPasswordSuccessBanner) {
|
||||
props.dismissPasswordResetBanner();
|
||||
dispatch(dismissPasswordResetBanner());
|
||||
}
|
||||
|
||||
const formData = { ...formFields };
|
||||
const validationErrors = validateFormFields(formData);
|
||||
if (validationErrors.emailOrUsername || validationErrors.password) {
|
||||
setErrors({ ...validationErrors });
|
||||
setErrorCode(prevState => ({ type: INVALID_FORM, count: prevState.count + 1, context: {} }));
|
||||
setErrorCode(prevState => ({
|
||||
type: INVALID_FORM,
|
||||
count: prevState.count + 1,
|
||||
context: {},
|
||||
}));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -158,23 +175,35 @@ const LoginPage = (props) => {
|
||||
password: formData.password,
|
||||
...queryParams,
|
||||
};
|
||||
props.loginRequest(payload);
|
||||
dispatch(loginRequest(payload));
|
||||
};
|
||||
|
||||
const handleOnChange = (event) => {
|
||||
const { name, value } = event.target;
|
||||
setFormFields(prevState => ({ ...prevState, [name]: value }));
|
||||
const {
|
||||
name,
|
||||
value,
|
||||
} = event.target;
|
||||
setFormFields(prevState => ({
|
||||
...prevState,
|
||||
[name]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handleOnFocus = (event) => {
|
||||
const { name } = event.target;
|
||||
setErrors(prevErrors => ({ ...prevErrors, [name]: '' }));
|
||||
setErrors(prevErrors => ({
|
||||
...prevErrors,
|
||||
[name]: '',
|
||||
}));
|
||||
};
|
||||
const trackForgotPasswordLinkClick = () => {
|
||||
sendTrackEvent('edx.bi.password-reset_form.toggled', { category: 'user-engagement' });
|
||||
};
|
||||
|
||||
const { provider, skipHintedLogin } = getTpaProvider(tpaHint, providers, secondaryProviders);
|
||||
const {
|
||||
provider,
|
||||
skipHintedLogin,
|
||||
} = getTpaProvider(tpaHint, providers, secondaryProviders);
|
||||
|
||||
if (tpaHint) {
|
||||
if (thirdPartyAuthApiStatus === PENDING_STATE) {
|
||||
@@ -281,88 +310,9 @@ const LoginPage = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const loginPageState = state.login;
|
||||
return {
|
||||
backedUpFormData: loginPageState.loginFormData,
|
||||
loginErrorCode: loginPageState.loginErrorCode,
|
||||
loginErrorContext: loginPageState.loginErrorContext,
|
||||
loginResult: loginPageState.loginResult,
|
||||
shouldBackupState: loginPageState.shouldBackupState,
|
||||
showResetPasswordSuccessBanner: loginPageState.showResetPasswordSuccessBanner,
|
||||
submitState: loginPageState.submitState,
|
||||
thirdPartyAuthContext: thirdPartyAuthContextSelector(state),
|
||||
thirdPartyAuthApiStatus: state.commonComponents.thirdPartyAuthApiStatus,
|
||||
};
|
||||
};
|
||||
|
||||
LoginPage.propTypes = {
|
||||
backedUpFormData: PropTypes.shape({
|
||||
formFields: PropTypes.shape({}),
|
||||
errors: PropTypes.shape({}),
|
||||
}),
|
||||
loginErrorCode: PropTypes.string,
|
||||
loginErrorContext: PropTypes.shape({
|
||||
email: PropTypes.string,
|
||||
redirectUrl: PropTypes.string,
|
||||
context: PropTypes.shape({}),
|
||||
}),
|
||||
loginResult: PropTypes.shape({
|
||||
redirectUrl: PropTypes.string,
|
||||
success: PropTypes.bool,
|
||||
}),
|
||||
shouldBackupState: PropTypes.bool,
|
||||
showResetPasswordSuccessBanner: PropTypes.bool,
|
||||
submitState: PropTypes.string,
|
||||
thirdPartyAuthApiStatus: PropTypes.string,
|
||||
institutionLogin: PropTypes.bool.isRequired,
|
||||
thirdPartyAuthContext: PropTypes.shape({
|
||||
currentProvider: PropTypes.string,
|
||||
errorMessage: PropTypes.string,
|
||||
platformName: PropTypes.string,
|
||||
providers: PropTypes.arrayOf(PropTypes.shape({})),
|
||||
secondaryProviders: PropTypes.arrayOf(PropTypes.shape({})),
|
||||
finishAuthUrl: PropTypes.string,
|
||||
}),
|
||||
// Actions
|
||||
backupFormState: PropTypes.func.isRequired,
|
||||
dismissPasswordResetBanner: PropTypes.func.isRequired,
|
||||
loginRequest: PropTypes.func.isRequired,
|
||||
getTPADataFromBackend: PropTypes.func.isRequired,
|
||||
handleInstitutionLogin: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
LoginPage.defaultProps = {
|
||||
backedUpFormData: {
|
||||
formFields: {
|
||||
emailOrUsername: '', password: '',
|
||||
},
|
||||
errors: {
|
||||
emailOrUsername: '', password: '',
|
||||
},
|
||||
},
|
||||
loginErrorCode: null,
|
||||
loginErrorContext: {},
|
||||
loginResult: {},
|
||||
shouldBackupState: false,
|
||||
showResetPasswordSuccessBanner: false,
|
||||
submitState: DEFAULT_STATE,
|
||||
thirdPartyAuthApiStatus: PENDING_STATE,
|
||||
thirdPartyAuthContext: {
|
||||
currentProvider: null,
|
||||
errorMessage: null,
|
||||
finishAuthUrl: null,
|
||||
providers: [],
|
||||
secondaryProviders: [],
|
||||
},
|
||||
};
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
{
|
||||
backupFormState: backupLoginFormBegin,
|
||||
dismissPasswordResetBanner,
|
||||
loginRequest,
|
||||
getTPADataFromBackend: getThirdPartyAuthContext,
|
||||
},
|
||||
)(LoginPage);
|
||||
export default LoginPage;
|
||||
|
||||
@@ -41,6 +41,14 @@ describe('LoginPage', () => {
|
||||
const initialState = {
|
||||
login: {
|
||||
loginResult: { success: false, redirectUrl: '' },
|
||||
loginFormData: {
|
||||
formFields: {
|
||||
emailOrUsername: '', password: '',
|
||||
},
|
||||
errors: {
|
||||
emailOrUsername: '', password: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
commonComponents: {
|
||||
thirdPartyAuthApiStatus: null,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||
@@ -24,16 +24,20 @@ import { LOGIN_PAGE, REGISTER_PAGE } from '../data/constants';
|
||||
import {
|
||||
getTpaHint, getTpaProvider, updatePathWithQueryParams,
|
||||
} from '../data/utils';
|
||||
import { LoginPage } from '../login';
|
||||
import { backupLoginForm } from '../login/data/actions';
|
||||
import LoginComponentSlot from '../plugin-slots/LoginComponentSlot';
|
||||
import { RegistrationPage } from '../register';
|
||||
import { backupRegistrationForm } from '../register/data/actions';
|
||||
|
||||
const Logistration = (props) => {
|
||||
const { selectedPage, tpaProviders } = props;
|
||||
const Logistration = ({
|
||||
selectedPage,
|
||||
}) => {
|
||||
const tpaHint = getTpaHint();
|
||||
const tpaProviders = useSelector(tpaProvidersSelector);
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
providers, secondaryProviders,
|
||||
providers,
|
||||
secondaryProviders,
|
||||
} = tpaProviders;
|
||||
const { formatMessage } = useIntl();
|
||||
const [institutionLogin, setInstitutionLogin] = useState(false);
|
||||
@@ -45,7 +49,8 @@ const Logistration = (props) => {
|
||||
useEffect(() => {
|
||||
const authService = getAuthService();
|
||||
if (authService) {
|
||||
authService.getCsrfTokenService().getCsrfToken(getConfig().LMS_BASE_URL);
|
||||
authService.getCsrfTokenService()
|
||||
.getCsrfToken(getConfig().LMS_BASE_URL);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -71,11 +76,11 @@ const Logistration = (props) => {
|
||||
return;
|
||||
}
|
||||
sendTrackEvent(`edx.bi.${tabKey.replace('/', '')}_form.toggled`, { category: 'user-engagement' });
|
||||
props.clearThirdPartyAuthContextErrorMessage();
|
||||
dispatch(clearThirdPartyAuthContextErrorMessage());
|
||||
if (tabKey === LOGIN_PAGE) {
|
||||
props.backupRegistrationForm();
|
||||
dispatch(backupRegistrationForm());
|
||||
} else if (tabKey === REGISTER_PAGE) {
|
||||
props.backupLoginForm();
|
||||
dispatch(backupLoginForm());
|
||||
}
|
||||
setKey(tabKey);
|
||||
};
|
||||
@@ -111,7 +116,10 @@ const Logistration = (props) => {
|
||||
{!institutionLogin && (
|
||||
<h3 className="mb-4.5">{formatMessage(messages['logistration.sign.in'])}</h3>
|
||||
)}
|
||||
<LoginPage institutionLogin={institutionLogin} handleInstitutionLogin={handleInstitutionLogin} />
|
||||
<LoginComponentSlot
|
||||
institutionLogin={institutionLogin}
|
||||
handleInstitutionLogin={handleInstitutionLogin}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
@@ -124,12 +132,16 @@ const Logistration = (props) => {
|
||||
</Tabs>
|
||||
)
|
||||
: (!isValidTpaHint() && !hideRegistrationLink && (
|
||||
<Tabs defaultActiveKey={selectedPage} id="controlled-tab" onSelect={(tabKey) => handleOnSelect(tabKey, selectedPage)}>
|
||||
<Tabs
|
||||
defaultActiveKey={selectedPage}
|
||||
id="controlled-tab"
|
||||
onSelect={(tabKey) => handleOnSelect(tabKey, selectedPage)}
|
||||
>
|
||||
<Tab title={formatMessage(messages['logistration.register'])} eventKey={REGISTER_PAGE} />
|
||||
<Tab title={formatMessage(messages['logistration.sign.in'])} eventKey={LOGIN_PAGE} />
|
||||
</Tabs>
|
||||
))}
|
||||
{ key && (
|
||||
{key && (
|
||||
<Navigate to={updatePathWithQueryParams(key)} replace />
|
||||
)}
|
||||
<div id="main-content" className="main-content">
|
||||
@@ -139,7 +151,12 @@ const Logistration = (props) => {
|
||||
</h3>
|
||||
)}
|
||||
{selectedPage === LOGIN_PAGE
|
||||
? <LoginPage institutionLogin={institutionLogin} handleInstitutionLogin={handleInstitutionLogin} />
|
||||
? (
|
||||
<LoginComponentSlot
|
||||
institutionLogin={institutionLogin}
|
||||
handleInstitutionLogin={handleInstitutionLogin}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<RegistrationPage
|
||||
institutionLogin={institutionLogin}
|
||||
@@ -156,35 +173,10 @@ const Logistration = (props) => {
|
||||
|
||||
Logistration.propTypes = {
|
||||
selectedPage: PropTypes.string,
|
||||
backupLoginForm: PropTypes.func.isRequired,
|
||||
backupRegistrationForm: PropTypes.func.isRequired,
|
||||
clearThirdPartyAuthContextErrorMessage: PropTypes.func.isRequired,
|
||||
tpaProviders: PropTypes.shape({
|
||||
providers: PropTypes.arrayOf(PropTypes.shape({})),
|
||||
secondaryProviders: PropTypes.arrayOf(PropTypes.shape({})),
|
||||
}),
|
||||
};
|
||||
|
||||
Logistration.defaultProps = {
|
||||
tpaProviders: {
|
||||
providers: [],
|
||||
secondaryProviders: [],
|
||||
},
|
||||
};
|
||||
|
||||
Logistration.defaultProps = {
|
||||
selectedPage: REGISTER_PAGE,
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
tpaProviders: tpaProvidersSelector(state),
|
||||
});
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
{
|
||||
backupLoginForm,
|
||||
backupRegistrationForm,
|
||||
clearThirdPartyAuthContextErrorMessage,
|
||||
},
|
||||
)(Logistration);
|
||||
export default Logistration;
|
||||
|
||||
@@ -48,16 +48,26 @@ describe('Logistration', () => {
|
||||
marketingEmailsOptIn: true,
|
||||
},
|
||||
formFields: {
|
||||
name: '', email: '', username: '', password: '',
|
||||
name: '',
|
||||
email: '',
|
||||
username: '',
|
||||
password: '',
|
||||
},
|
||||
emailSuggestion: {
|
||||
suggestion: '', type: '',
|
||||
suggestion: '',
|
||||
type: '',
|
||||
},
|
||||
errors: {
|
||||
name: '', email: '', username: '', password: '',
|
||||
name: '',
|
||||
email: '',
|
||||
username: '',
|
||||
password: '',
|
||||
},
|
||||
},
|
||||
registrationResult: { success: false, redirectUrl: '' },
|
||||
registrationResult: {
|
||||
success: false,
|
||||
redirectUrl: '',
|
||||
},
|
||||
registrationError: {},
|
||||
usernameSuggestions: [],
|
||||
validationApiRateLimited: false,
|
||||
@@ -69,7 +79,18 @@ describe('Logistration', () => {
|
||||
},
|
||||
},
|
||||
login: {
|
||||
loginResult: { success: false, redirectUrl: '' },
|
||||
loginResult: {
|
||||
success: false,
|
||||
redirectUrl: '',
|
||||
},
|
||||
loginFormData: {
|
||||
formFields: {
|
||||
emailOrUsername: '', password: '',
|
||||
},
|
||||
errors: {
|
||||
emailOrUsername: '', password: '',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
47
src/plugin-slots/LoginComponentSlot/README.md
Normal file
47
src/plugin-slots/LoginComponentSlot/README.md
Normal file
@@ -0,0 +1,47 @@
|
||||
# Login Component Slot
|
||||
|
||||
### Slot ID: `org.openedx.frontend.authn.login_component.v1`
|
||||
|
||||
## Description
|
||||
|
||||
This slot is used to replace/modify/hide the login component.
|
||||
|
||||
## Example
|
||||
|
||||
### Default content
|
||||

|
||||
|
||||
### With a prepended message
|
||||

|
||||
|
||||
The following `env.config.jsx` will add a message before the login component.
|
||||
|
||||
```js
|
||||
import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
|
||||
|
||||
// Load environment variables from .env file
|
||||
const config = {
|
||||
...process.env,
|
||||
pluginSlots: {
|
||||
'org.openedx.frontend.authn.login_component.v1': {
|
||||
keepDefault: true,
|
||||
plugins: [
|
||||
{
|
||||
op: PLUGIN_OPERATIONS.Insert,
|
||||
widget: {
|
||||
id: 'test_plugin',
|
||||
type: DIRECT_PLUGIN,
|
||||
priority: 1,
|
||||
RenderWidget: () => (
|
||||
<h2>You're logging into TEST Instance.</h2>
|
||||
)
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
||||
```
|
||||
BIN
src/plugin-slots/LoginComponentSlot/component_with_prefix.png
Normal file
BIN
src/plugin-slots/LoginComponentSlot/component_with_prefix.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
src/plugin-slots/LoginComponentSlot/default_component.png
Normal file
BIN
src/plugin-slots/LoginComponentSlot/default_component.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.5 KiB |
31
src/plugin-slots/LoginComponentSlot/index.jsx
Normal file
31
src/plugin-slots/LoginComponentSlot/index.jsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import React from 'react';
|
||||
|
||||
import { PluginSlot } from '@openedx/frontend-plugin-framework';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import LoginPage from '../../login/LoginPage';
|
||||
|
||||
const LoginComponentSlot = ({
|
||||
institutionLogin,
|
||||
handleInstitutionLogin,
|
||||
}) => (
|
||||
<PluginSlot
|
||||
id="org.openedx.frontend.authn.login_component.v1"
|
||||
pluginProps={{
|
||||
isInstitutionLogin: institutionLogin,
|
||||
setInstitutionLogin: handleInstitutionLogin,
|
||||
}}
|
||||
>
|
||||
<LoginPage
|
||||
institutionLogin={institutionLogin}
|
||||
handleInstitutionLogin={handleInstitutionLogin}
|
||||
/>
|
||||
</PluginSlot>
|
||||
);
|
||||
|
||||
LoginComponentSlot.propTypes = {
|
||||
institutionLogin: PropTypes.bool,
|
||||
handleInstitutionLogin: PropTypes.func,
|
||||
};
|
||||
|
||||
export default LoginComponentSlot;
|
||||
@@ -1,10 +1,12 @@
|
||||
import { renderHook } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import { render } from '@testing-library/react';
|
||||
import algoliasearchHelper from 'algoliasearch-helper';
|
||||
|
||||
import mockedRecommendedProducts from './mockedData';
|
||||
import CreateAlgoliaSearchHelperMock from './test_utils/test_utils';
|
||||
import isOneTrustFunctionalCookieEnabled from '../../../data/oneTrust';
|
||||
import useAlgoliaRecommendations from '../hooks/useAlgoliaRecommendations';
|
||||
import CreateAlgoliaSearchHelperMock from './test_utils/test_utils';
|
||||
|
||||
jest.mock('algoliasearch-helper');
|
||||
|
||||
@@ -17,6 +19,23 @@ jest.mock('../../../data/algolia', () => ({
|
||||
|
||||
jest.mock('../algoliaResultsParser', () => jest.fn((course) => course));
|
||||
|
||||
const renderHook = (hookCallback) => {
|
||||
const result = {
|
||||
current: null,
|
||||
};
|
||||
|
||||
const Component = () => {
|
||||
const val = hookCallback();
|
||||
React.useEffect(() => {
|
||||
result.current = val;
|
||||
});
|
||||
return null;
|
||||
};
|
||||
|
||||
render(<Component />);
|
||||
return { result };
|
||||
};
|
||||
|
||||
describe('useAlgoliaRecommendations Tests', () => {
|
||||
const MockSearchHelperWithData = new CreateAlgoliaSearchHelperMock(mockedRecommendedProducts);
|
||||
const MockSearchHelperWithoutData = new CreateAlgoliaSearchHelperMock();
|
||||
@@ -28,8 +47,10 @@ describe('useAlgoliaRecommendations Tests', () => {
|
||||
() => useAlgoliaRecommendations('PK', 'Introductory'),
|
||||
);
|
||||
|
||||
expect(result.current.recommendations).toEqual(mockedRecommendedProducts);
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.recommendations)
|
||||
.toEqual(mockedRecommendedProducts);
|
||||
expect(result.current.isLoading)
|
||||
.toBe(false);
|
||||
});
|
||||
|
||||
it('should not fetch recommendations if functional cookies are not set', async () => {
|
||||
@@ -39,8 +60,10 @@ describe('useAlgoliaRecommendations Tests', () => {
|
||||
() => useAlgoliaRecommendations('PK', 'Introductory'),
|
||||
);
|
||||
|
||||
expect(result.current.recommendations).toEqual([]);
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.recommendations)
|
||||
.toEqual([]);
|
||||
expect(result.current.isLoading)
|
||||
.toBe(false);
|
||||
});
|
||||
|
||||
it('should return empty list if no recommendations returned from Algolia', async () => {
|
||||
@@ -50,7 +73,9 @@ describe('useAlgoliaRecommendations Tests', () => {
|
||||
() => useAlgoliaRecommendations('PK', 'Introductory'),
|
||||
);
|
||||
|
||||
expect(result.current.recommendations).toEqual([]);
|
||||
expect(result.current.isLoading).toBe(false);
|
||||
expect(result.current.recommendations)
|
||||
.toEqual([]);
|
||||
expect(result.current.isLoading)
|
||||
.toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user