From d22a1652fcd9b2055b7465d1f99f4d1a1af520b2 Mon Sep 17 00:00:00 2001 From: Diana Olarte Date: Mon, 17 Oct 2022 11:13:59 -0500 Subject: [PATCH] feat: allow runtieme configuration (#586) Allows frontend-app-profile to be configured at runtime using the LMS's new MFE Configuration API. Part of https://github.com/openedx/frontend-wg/issues/103 --- .env | 2 + .env.development | 2 + .env.test | 2 + package-lock.json | 613 ++++++++++++++---- package.json | 7 +- src/head/Head.jsx | 23 + src/head/Head.test.jsx | 17 + src/head/messages.js | 11 + src/index.jsx | 2 + .../__snapshots__/ProfilePage.test.jsx.snap | 220 ++----- .../__snapshots__/SocialLinks.test.jsx.snap | 8 +- 11 files changed, 614 insertions(+), 293 deletions(-) create mode 100644 src/head/Head.jsx create mode 100644 src/head/Head.test.jsx create mode 100644 src/head/messages.js diff --git a/.env b/.env index 2c93ffdf3..bc72c9274 100644 --- a/.env +++ b/.env @@ -25,3 +25,5 @@ FAVICON_URL='' ENABLE_LEARNER_RECORD_MFE='' LEARNER_RECORD_MFE_BASE_URL='' COLLECT_YEAR_OF_BIRTH=true +APP_ID='' +MFE_CONFIG_API_URL='' diff --git a/.env.development b/.env.development index c7e9346f0..4d6fd3733 100644 --- a/.env.development +++ b/.env.development @@ -26,3 +26,5 @@ FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico ENABLE_LEARNER_RECORD_MFE='' LEARNER_RECORD_MFE_BASE_URL='http://localhost:1990' COLLECT_YEAR_OF_BIRTH=true +APP_ID='' +MFE_CONFIG_API_URL='' diff --git a/.env.test b/.env.test index c737b021b..4e926bca6 100644 --- a/.env.test +++ b/.env.test @@ -20,3 +20,5 @@ FAVICON_URL=https://edx-cdn.org/v3/default/favicon.ico ENABLE_LEARNER_RECORD_MFE='' LEARNER_RECORD_MFE_BASE_URL='http://localhost:1990' COLLECT_YEAR_OF_BIRTH=true +APP_ID='' +MFE_CONFIG_API_URL='' diff --git a/package-lock.json b/package-lock.json index 3b4a4ca40..21b2ba75d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,9 +10,9 @@ "license": "AGPL-3.0", "dependencies": { "@edx/brand": "npm:@edx/brand-openedx@1.1.0", - "@edx/frontend-component-footer": "10.3.0", - "@edx/frontend-component-header": "2.6.1", - "@edx/frontend-platform": "1.15.6", + "@edx/frontend-component-footer": "11.2.1", + "@edx/frontend-component-header": "3.2.1", + "@edx/frontend-platform": "2.6.2", "@edx/paragon": "19.25.3", "@fortawesome/fontawesome-svg-core": "1.2.36", "@fortawesome/free-brands-svg-icons": "5.15.4", @@ -28,6 +28,7 @@ "prop-types": "15.8.1", "react": "16.14.0", "react-dom": "16.14.0", + "react-helmet": "^6.1.0", "react-redux": "7.2.9", "react-router": "5.3.4", "react-router-dom": "5.3.4", @@ -2280,72 +2281,53 @@ } }, "node_modules/@edx/frontend-component-footer": { - "version": "10.3.0", - "license": "AGPL-3.0", + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@edx/frontend-component-footer/-/frontend-component-footer-11.2.1.tgz", + "integrity": "sha512-W/lLQUsi5lTvq0ReCzRSswPoAqa85r/u7CHcTdxK33faAdzIUlKfJO+EAgwdooUw9DuHuWBIapwhxjJX1ZWYmQ==", "dependencies": { "@fortawesome/fontawesome-svg-core": "1.2.36", "@fortawesome/free-brands-svg-icons": "5.15.4", "@fortawesome/free-regular-svg-icons": "5.15.4", "@fortawesome/free-solid-svg-icons": "5.15.4", - "@fortawesome/react-fontawesome": "0.1.18" + "@fortawesome/react-fontawesome": "0.2.0" }, "peerDependencies": { - "@edx/frontend-platform": "^1.8.0", + "@edx/frontend-platform": "^2.3.0", "prop-types": "^15.5.10", "react": "^16.9.0", "react-dom": "^16.9.0" } }, - "node_modules/@edx/frontend-component-footer/node_modules/@fortawesome/react-fontawesome": { - "version": "0.1.18", - "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.18.tgz", - "integrity": "sha512-RwLIB4TZw0M9gvy5u+TusAA0afbwM4JQIimNH/j3ygd6aIvYPQLqXMhC9ErY26J23rDPyDZldIfPq/HpTTJ/tQ==", - "dependencies": { - "prop-types": "^15.8.1" - }, - "peerDependencies": { - "@fortawesome/fontawesome-svg-core": "~1 || ~6", - "react": ">=16.x" - } - }, "node_modules/@edx/frontend-component-header": { - "version": "2.6.1", - "license": "AGPL-3.0", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@edx/frontend-component-header/-/frontend-component-header-3.2.1.tgz", + "integrity": "sha512-zlXnAX9wVpDAVOiNjoIdNGxIANS1XveI4vJE+x0/aSexE0qNkMlcOSqDYwSVq6YeZ5nJhXnAXYKVLPPKpREgrQ==", "dependencies": { "@fortawesome/fontawesome-svg-core": "1.2.36", "@fortawesome/free-brands-svg-icons": "5.15.4", "@fortawesome/free-regular-svg-icons": "5.15.4", "@fortawesome/free-solid-svg-icons": "5.15.4", - "@fortawesome/react-fontawesome": "^0.1.14", + "@fortawesome/react-fontawesome": "^0.2.0", "babel-polyfill": "6.26.0", "react-responsive": "8.2.0", - "react-transition-group": "4.4.2" + "react-transition-group": "4.4.5" }, "peerDependencies": { - "@edx/frontend-platform": "^1.8.0", - "@edx/paragon": ">= 7.0.0 < 20.0.0", + "@edx/frontend-platform": "^2.0.0", + "@edx/paragon": ">= 7.0.0 < 21.0.0", "prop-types": "^15.5.10", "react": "^16.9.0", "react-dom": "^16.9.0" } }, - "node_modules/@edx/frontend-component-header/node_modules/@fortawesome/react-fontawesome": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.19.tgz", - "integrity": "sha512-Hyb+lB8T18cvLNX0S3llz7PcSOAJMLwiVKBuuzwM/nI5uoBw+gQjnf9il0fR1C3DKOI5Kc79pkJ4/xB0Uw9aFQ==", - "dependencies": { - "prop-types": "^15.8.1" - }, - "peerDependencies": { - "@fortawesome/fontawesome-svg-core": "~1 || ~6", - "react": ">=16.x" - } - }, "node_modules/@edx/frontend-platform": { - "version": "1.15.6", - "license": "AGPL-3.0", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-2.6.2.tgz", + "integrity": "sha512-h+gYLkPYw41krGiSGs59o2jaq/g3Yk6ay/3rBq0y1/KM6eeaq/F7o14YOhfTRLTpld9Hg+MPKzfOuHyqQN2TEw==", "dependencies": { "@cospired/i18n-iso-languages": "2.2.0", + "@formatjs/intl-pluralrules": "4.3.3", + "@formatjs/intl-relativetimeformat": "10.0.1", "axios": "0.26.1", "axios-cache-adapter": "2.7.3", "form-urlencoded": "4.1.4", @@ -2360,15 +2342,14 @@ "lodash.merge": "4.6.2", "lodash.snakecase": "4.1.1", "pubsub-js": "1.9.4", - "react-intl": "2.9.0", + "react-intl": "^5.25.0", "universal-cookie": "4.0.4" }, "bin": { - "transifex-Makefile": "i18n/scripts/Makefile", "transifex-utils.js": "i18n/scripts/transifex-utils.js" }, "peerDependencies": { - "@edx/paragon": ">= 10.0.0 < 20.0.0", + "@edx/paragon": ">= 10.0.0 < 21.0.0", "prop-types": "^15.7.2", "react": "^16.9.0", "react-dom": "^16.9.0", @@ -2547,6 +2528,119 @@ "tslib": "^2.0.1" } }, + "node_modules/@formatjs/fast-memoize": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz", + "integrity": "sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.0.tgz", + "integrity": "sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/icu-skeleton-parser": "1.3.6", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/icu-messageformat-parser/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz", + "integrity": "sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/icu-skeleton-parser/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.2.1.tgz", + "integrity": "sha512-vgvyUOOrzqVaOFYzTf2d3+ToSkH2JpR7x/4U1RyoHQLmvEaTQvXJ7A2qm1Iy3brGNXC/+/7bUlc3lpH+h/LOJA==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/fast-memoize": "1.2.1", + "@formatjs/icu-messageformat-parser": "2.1.0", + "@formatjs/intl-displaynames": "5.4.3", + "@formatjs/intl-listformat": "6.5.3", + "intl-messageformat": "9.13.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "typescript": "^4.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@formatjs/intl-displaynames": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-5.4.3.tgz", + "integrity": "sha512-4r12A3mS5dp5hnSaQCWBuBNfi9Amgx2dzhU4lTFfhSxgb5DOAiAbMpg6+7gpWZgl4ahsj3l2r/iHIjdmdXOE2Q==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-displaynames/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-listformat": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-6.5.3.tgz", + "integrity": "sha512-ozpz515F/+3CU+HnLi5DYPsLa6JoCfBggBSSg/8nOB5LYSFW9+ZgNQJxJ8tdhKYeODT+4qVHX27EeJLoxLGLNg==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-listformat/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.2.25", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz", + "integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/@formatjs/intl-numberformat": { "version": "5.7.6", "dev": true, @@ -2556,6 +2650,53 @@ "tslib": "^2.0.1" } }, + "node_modules/@formatjs/intl-pluralrules": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-pluralrules/-/intl-pluralrules-4.3.3.tgz", + "integrity": "sha512-NLZN8gf2qLpCuc0m565IbKLNUarEGOzk0mkdTkE4XTuNCofzoQTurW6lL3fmDlneAoYl2FiTdHa5q4o2vZF50g==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-pluralrules/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-relativetimeformat": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-10.0.1.tgz", + "integrity": "sha512-AABPQtPjFilXegQsnmVHrSlzjFNUffAEk5DgowY6b7WSwDI7g2W6QgW903/lbZ58emhphAbgHdtKeUBXqTiLpw==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl-relativetimeformat/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, + "node_modules/@formatjs/intl/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + }, "node_modules/@formatjs/ts-transformer": { "version": "2.13.0", "dev": true, @@ -11666,15 +11807,15 @@ "node": ">= 0.10" } }, - "node_modules/intl-format-cache": { - "version": "2.2.9", - "license": "BSD-3-Clause" - }, "node_modules/intl-messageformat": { - "version": "2.2.0", - "license": "BSD-3-Clause", + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz", + "integrity": "sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==", "dependencies": { - "intl-messageformat-parser": "1.4.0" + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/fast-memoize": "1.2.1", + "@formatjs/icu-messageformat-parser": "2.1.0", + "tslib": "^2.1.0" } }, "node_modules/intl-messageformat-parser": { @@ -11685,15 +11826,13 @@ "@formatjs/intl-numberformat": "^5.5.2" } }, - "node_modules/intl-messageformat/node_modules/intl-messageformat-parser": { - "version": "1.4.0", - "license": "BSD-3-Clause" - }, - "node_modules/intl-relativeformat": { - "version": "2.2.0", - "license": "BSD-3-Clause", + "node_modules/intl-messageformat/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", "dependencies": { - "intl-messageformat": "^2.0.0" + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" } }, "node_modules/into-stream": { @@ -16324,19 +16463,53 @@ } } }, - "node_modules/react-intl": { - "version": "2.9.0", - "license": "BSD-3-Clause", + "node_modules/react-helmet": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz", + "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==", "dependencies": { - "hoist-non-react-statics": "^3.3.0", - "intl-format-cache": "^2.0.5", - "intl-messageformat": "^2.1.0", - "intl-relativeformat": "^2.1.0", - "invariant": "^2.1.1" + "object-assign": "^4.1.1", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.1.1", + "react-side-effect": "^2.1.0" }, "peerDependencies": { - "prop-types": "^15.5.4", - "react": "^0.14.9 || ^15.0.0 || ^16.0.0" + "react": ">=16.3.0" + } + }, + "node_modules/react-intl": { + "version": "5.25.1", + "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-5.25.1.tgz", + "integrity": "sha512-pkjdQDvpJROoXLMltkP/5mZb0/XqrqLoPGKUCfbdkP8m6U9xbK40K51Wu+a4aQqTEvEK5lHBk0fWzUV72SJ3Hg==", + "dependencies": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/icu-messageformat-parser": "2.1.0", + "@formatjs/intl": "2.2.1", + "@formatjs/intl-displaynames": "5.4.3", + "@formatjs/intl-listformat": "6.5.3", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/react": "16 || 17 || 18", + "hoist-non-react-statics": "^3.3.2", + "intl-messageformat": "9.13.0", + "tslib": "^2.1.0" + }, + "peerDependencies": { + "react": "^16.3.0 || 17 || 18", + "typescript": "^4.5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/react-intl/node_modules/@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "dependencies": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" } }, "node_modules/react-is": { @@ -16514,6 +16687,14 @@ "react": ">=15" } }, + "node_modules/react-side-effect": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz", + "integrity": "sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==", + "peerDependencies": { + "react": "^16.3.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-style-singleton": { "version": "2.2.1", "license": "MIT", @@ -16561,8 +16742,9 @@ } }, "node_modules/react-transition-group": { - "version": "4.4.2", - "license": "BSD-3-Clause", + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", @@ -19785,7 +19967,7 @@ }, "node_modules/typescript": { "version": "4.7.4", - "dev": true, + "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", @@ -22564,52 +22746,40 @@ } }, "@edx/frontend-component-footer": { - "version": "10.3.0", + "version": "11.2.1", + "resolved": "https://registry.npmjs.org/@edx/frontend-component-footer/-/frontend-component-footer-11.2.1.tgz", + "integrity": "sha512-W/lLQUsi5lTvq0ReCzRSswPoAqa85r/u7CHcTdxK33faAdzIUlKfJO+EAgwdooUw9DuHuWBIapwhxjJX1ZWYmQ==", "requires": { "@fortawesome/fontawesome-svg-core": "1.2.36", "@fortawesome/free-brands-svg-icons": "5.15.4", "@fortawesome/free-regular-svg-icons": "5.15.4", "@fortawesome/free-solid-svg-icons": "5.15.4", - "@fortawesome/react-fontawesome": "0.1.18" - }, - "dependencies": { - "@fortawesome/react-fontawesome": { - "version": "0.1.18", - "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.18.tgz", - "integrity": "sha512-RwLIB4TZw0M9gvy5u+TusAA0afbwM4JQIimNH/j3ygd6aIvYPQLqXMhC9ErY26J23rDPyDZldIfPq/HpTTJ/tQ==", - "requires": { - "prop-types": "^15.8.1" - } - } + "@fortawesome/react-fontawesome": "0.2.0" } }, "@edx/frontend-component-header": { - "version": "2.6.1", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@edx/frontend-component-header/-/frontend-component-header-3.2.1.tgz", + "integrity": "sha512-zlXnAX9wVpDAVOiNjoIdNGxIANS1XveI4vJE+x0/aSexE0qNkMlcOSqDYwSVq6YeZ5nJhXnAXYKVLPPKpREgrQ==", "requires": { "@fortawesome/fontawesome-svg-core": "1.2.36", "@fortawesome/free-brands-svg-icons": "5.15.4", "@fortawesome/free-regular-svg-icons": "5.15.4", "@fortawesome/free-solid-svg-icons": "5.15.4", - "@fortawesome/react-fontawesome": "^0.1.14", + "@fortawesome/react-fontawesome": "^0.2.0", "babel-polyfill": "6.26.0", "react-responsive": "8.2.0", - "react-transition-group": "4.4.2" - }, - "dependencies": { - "@fortawesome/react-fontawesome": { - "version": "0.1.19", - "resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.1.19.tgz", - "integrity": "sha512-Hyb+lB8T18cvLNX0S3llz7PcSOAJMLwiVKBuuzwM/nI5uoBw+gQjnf9il0fR1C3DKOI5Kc79pkJ4/xB0Uw9aFQ==", - "requires": { - "prop-types": "^15.8.1" - } - } + "react-transition-group": "4.4.5" } }, "@edx/frontend-platform": { - "version": "1.15.6", + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/@edx/frontend-platform/-/frontend-platform-2.6.2.tgz", + "integrity": "sha512-h+gYLkPYw41krGiSGs59o2jaq/g3Yk6ay/3rBq0y1/KM6eeaq/F7o14YOhfTRLTpld9Hg+MPKzfOuHyqQN2TEw==", "requires": { "@cospired/i18n-iso-languages": "2.2.0", + "@formatjs/intl-pluralrules": "4.3.3", + "@formatjs/intl-relativetimeformat": "10.0.1", "axios": "0.26.1", "axios-cache-adapter": "2.7.3", "form-urlencoded": "4.1.4", @@ -22624,7 +22794,7 @@ "lodash.merge": "4.6.2", "lodash.snakecase": "4.1.1", "pubsub-js": "1.9.4", - "react-intl": "2.9.0", + "react-intl": "^5.25.0", "universal-cookie": "4.0.4" }, "dependencies": { @@ -22754,6 +22924,130 @@ "tslib": "^2.0.1" } }, + "@formatjs/fast-memoize": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-1.2.1.tgz", + "integrity": "sha512-Rg0e76nomkz3vF9IPlKeV+Qynok0r7YZjL6syLz4/urSg0IbjPZCB/iYUMNsYA643gh4mgrX3T7KEIFIxJBQeg==", + "requires": { + "tslib": "^2.1.0" + } + }, + "@formatjs/icu-messageformat-parser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.1.0.tgz", + "integrity": "sha512-Qxv/lmCN6hKpBSss2uQ8IROVnta2r9jd3ymUEIjm2UyIkUCHVcbUVRGL/KS/wv7876edvsPe+hjHVJ4z8YuVaw==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/icu-skeleton-parser": "1.3.6", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + } + } + }, + "@formatjs/icu-skeleton-parser": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.3.6.tgz", + "integrity": "sha512-I96mOxvml/YLrwU2Txnd4klA7V8fRhb6JG/4hm3VMNmeJo1F03IpV2L3wWt7EweqNLES59SZ4d6hVOPCSf80Bg==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + } + } + }, + "@formatjs/intl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl/-/intl-2.2.1.tgz", + "integrity": "sha512-vgvyUOOrzqVaOFYzTf2d3+ToSkH2JpR7x/4U1RyoHQLmvEaTQvXJ7A2qm1Iy3brGNXC/+/7bUlc3lpH+h/LOJA==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/fast-memoize": "1.2.1", + "@formatjs/icu-messageformat-parser": "2.1.0", + "@formatjs/intl-displaynames": "5.4.3", + "@formatjs/intl-listformat": "6.5.3", + "intl-messageformat": "9.13.0", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + } + } + }, + "@formatjs/intl-displaynames": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-displaynames/-/intl-displaynames-5.4.3.tgz", + "integrity": "sha512-4r12A3mS5dp5hnSaQCWBuBNfi9Amgx2dzhU4lTFfhSxgb5DOAiAbMpg6+7gpWZgl4ahsj3l2r/iHIjdmdXOE2Q==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + } + } + }, + "@formatjs/intl-listformat": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-listformat/-/intl-listformat-6.5.3.tgz", + "integrity": "sha512-ozpz515F/+3CU+HnLi5DYPsLa6JoCfBggBSSg/8nOB5LYSFW9+ZgNQJxJ8tdhKYeODT+4qVHX27EeJLoxLGLNg==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + } + } + }, + "@formatjs/intl-localematcher": { + "version": "0.2.25", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.2.25.tgz", + "integrity": "sha512-YmLcX70BxoSopLFdLr1Ds99NdlTI2oWoLbaUW2M406lxOIPzE1KQhRz2fPUkq34xVZQaihCoU29h0KK7An3bhA==", + "requires": { + "tslib": "^2.1.0" + } + }, "@formatjs/intl-numberformat": { "version": "5.7.6", "dev": true, @@ -22762,6 +23056,48 @@ "tslib": "^2.0.1" } }, + "@formatjs/intl-pluralrules": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@formatjs/intl-pluralrules/-/intl-pluralrules-4.3.3.tgz", + "integrity": "sha512-NLZN8gf2qLpCuc0m565IbKLNUarEGOzk0mkdTkE4XTuNCofzoQTurW6lL3fmDlneAoYl2FiTdHa5q4o2vZF50g==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + } + } + }, + "@formatjs/intl-relativetimeformat": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@formatjs/intl-relativetimeformat/-/intl-relativetimeformat-10.0.1.tgz", + "integrity": "sha512-AABPQtPjFilXegQsnmVHrSlzjFNUffAEk5DgowY6b7WSwDI7g2W6QgW903/lbZ58emhphAbgHdtKeUBXqTiLpw==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + } + } + }, "@formatjs/ts-transformer": { "version": "2.13.0", "dev": true, @@ -28889,17 +29225,25 @@ "version": "2.2.0", "dev": true }, - "intl-format-cache": { - "version": "2.2.9" - }, "intl-messageformat": { - "version": "2.2.0", + "version": "9.13.0", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-9.13.0.tgz", + "integrity": "sha512-7sGC7QnSQGa5LZP7bXLDhVDtQOeKGeBFGHF2Y8LVBwYZoQZCgWeKoPGTa5GMG8g/TzDgeXuYJQis7Ggiw2xTOw==", "requires": { - "intl-messageformat-parser": "1.4.0" + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/fast-memoize": "1.2.1", + "@formatjs/icu-messageformat-parser": "2.1.0", + "tslib": "^2.1.0" }, "dependencies": { - "intl-messageformat-parser": { - "version": "1.4.0" + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } } } }, @@ -28910,12 +29254,6 @@ "@formatjs/intl-numberformat": "^5.5.2" } }, - "intl-relativeformat": { - "version": "2.2.0", - "requires": { - "intl-messageformat": "^2.0.0" - } - }, "into-stream": { "version": "3.1.0", "dev": true, @@ -31872,14 +32210,43 @@ "use-sidecar": "^1.1.2" } }, - "react-intl": { - "version": "2.9.0", + "react-helmet": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/react-helmet/-/react-helmet-6.1.0.tgz", + "integrity": "sha512-4uMzEY9nlDlgxr61NL3XbKRy1hEkXmKNXhjbAIOVw5vcFrsdYbH2FEwcNyWvWinl103nXgzYNlns9ca+8kFiWw==", "requires": { - "hoist-non-react-statics": "^3.3.0", - "intl-format-cache": "^2.0.5", - "intl-messageformat": "^2.1.0", - "intl-relativeformat": "^2.1.0", - "invariant": "^2.1.1" + "object-assign": "^4.1.1", + "prop-types": "^15.7.2", + "react-fast-compare": "^3.1.1", + "react-side-effect": "^2.1.0" + } + }, + "react-intl": { + "version": "5.25.1", + "resolved": "https://registry.npmjs.org/react-intl/-/react-intl-5.25.1.tgz", + "integrity": "sha512-pkjdQDvpJROoXLMltkP/5mZb0/XqrqLoPGKUCfbdkP8m6U9xbK40K51Wu+a4aQqTEvEK5lHBk0fWzUV72SJ3Hg==", + "requires": { + "@formatjs/ecma402-abstract": "1.11.4", + "@formatjs/icu-messageformat-parser": "2.1.0", + "@formatjs/intl": "2.2.1", + "@formatjs/intl-displaynames": "5.4.3", + "@formatjs/intl-listformat": "6.5.3", + "@types/hoist-non-react-statics": "^3.3.1", + "@types/react": "16 || 17 || 18", + "hoist-non-react-statics": "^3.3.2", + "intl-messageformat": "9.13.0", + "tslib": "^2.1.0" + }, + "dependencies": { + "@formatjs/ecma402-abstract": { + "version": "1.11.4", + "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.11.4.tgz", + "integrity": "sha512-EBikYFp2JCdIfGEb5G9dyCkTGDmC57KSHhRQOC3aYxoPWVZvfWCDjZwkGYHN7Lis/fmuWl906bnNTJifDQ3sXw==", + "requires": { + "@formatjs/intl-localematcher": "0.2.25", + "tslib": "^2.1.0" + } + } } }, "react-is": { @@ -31991,6 +32358,12 @@ "tiny-warning": "^1.0.0" } }, + "react-side-effect": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz", + "integrity": "sha512-PVjOcvVOyIILrYoyGEpDN3vmYNLdy1CajSFNt4TDsVQC5KpTijDvWVoR+/7Rz2xT978D8/ZtFceXxzsPwZEDvw==", + "requires": {} + }, "react-style-singleton": { "version": "2.2.1", "requires": { @@ -32014,7 +32387,9 @@ } }, "react-transition-group": { - "version": "4.4.2", + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", "requires": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", @@ -34203,7 +34578,7 @@ }, "typescript": { "version": "4.7.4", - "dev": true + "devOptional": true }, "typescript-compare": { "version": "0.0.2", diff --git a/package.json b/package.json index 11e12c059..e7647b879 100644 --- a/package.json +++ b/package.json @@ -30,9 +30,9 @@ ], "dependencies": { "@edx/brand": "npm:@edx/brand-openedx@1.1.0", - "@edx/frontend-component-footer": "10.3.0", - "@edx/frontend-component-header": "2.6.1", - "@edx/frontend-platform": "1.15.6", + "@edx/frontend-component-footer": "11.2.1", + "@edx/frontend-component-header": "3.2.1", + "@edx/frontend-platform": "2.6.2", "@edx/paragon": "19.25.3", "@fortawesome/fontawesome-svg-core": "1.2.36", "@fortawesome/free-brands-svg-icons": "5.15.4", @@ -51,6 +51,7 @@ "react-redux": "7.2.9", "react-router": "5.3.4", "react-router-dom": "5.3.4", + "react-helmet": "^6.1.0", "redux": "4.2.0", "redux-devtools-extension": "2.13.9", "redux-logger": "3.0.6", diff --git a/src/head/Head.jsx b/src/head/Head.jsx new file mode 100644 index 000000000..088d030ef --- /dev/null +++ b/src/head/Head.jsx @@ -0,0 +1,23 @@ +import React from 'react'; +import { Helmet } from 'react-helmet'; +import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; +import { getConfig } from '@edx/frontend-platform'; + +import messages from './messages'; + +function Head({ intl }) { + return ( + + + {intl.formatMessage(messages['profile.page.title'], { siteName: getConfig().SITE_NAME })} + + + + ); +} + +Head.propTypes = { + intl: intlShape.isRequired, +}; + +export default injectIntl(Head); diff --git a/src/head/Head.test.jsx b/src/head/Head.test.jsx new file mode 100644 index 000000000..91eab5eb5 --- /dev/null +++ b/src/head/Head.test.jsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { IntlProvider } from '@edx/frontend-platform/i18n'; +import { Helmet } from 'react-helmet'; +import { mount } from 'enzyme'; +import { getConfig } from '@edx/frontend-platform'; +import Head from './Head'; + +describe('Head', () => { + const props = {}; + it('should match render title tag and favicon with the site configuration values', () => { + mount(); + const helmet = Helmet.peek(); + expect(helmet.title).toEqual(`Profile | ${getConfig().SITE_NAME}`); + expect(helmet.linkTags[0].rel).toEqual('shortcut icon'); + expect(helmet.linkTags[0].href).toEqual(getConfig().FAVICON_URL); + }); +}); diff --git a/src/head/messages.js b/src/head/messages.js new file mode 100644 index 000000000..3e3e106bc --- /dev/null +++ b/src/head/messages.js @@ -0,0 +1,11 @@ +import { defineMessages } from '@edx/frontend-platform/i18n'; + +const messages = defineMessages({ + 'profile.page.title': { + id: 'profile.page.title', + defaultMessage: 'Profile | {siteName}', + description: 'Title tag', + }, +}); + +export default messages; diff --git a/src/index.jsx b/src/index.jsx index 3a69778c2..0a1f9b599 100755 --- a/src/index.jsx +++ b/src/index.jsx @@ -25,10 +25,12 @@ import { ProfilePage, NotFoundPage } from './profile'; import configureStore from './data/configureStore'; import './index.scss'; +import Head from './head/Head'; subscribe(APP_READY, () => { ReactDOM.render( +
diff --git a/src/profile/__snapshots__/ProfilePage.test.jsx.snap b/src/profile/__snapshots__/ProfilePage.test.jsx.snap index b5682dad8..ce78fdfb6 100644 --- a/src/profile/__snapshots__/ProfilePage.test.jsx.snap +++ b/src/profile/__snapshots__/ProfilePage.test.jsx.snap @@ -120,12 +120,8 @@ exports[` Renders correctly in various states test country edit w

- - Member since - - 2017 - - + Member since + 2017


Renders correctly in various states test country edit w

- - Member since - - 2017 - - + Member since + 2017


Renders correctly in various states test country edit w

- - From - + From

Renders correctly in various states test country edit w

- - Completed on - - 3/4/2019 - - + Completed on + 3/4/2019

Renders correctly in various states test education edit

- - Member since - - 2017 - - + Member since + 2017


Renders correctly in various states test education edit

- - Member since - - 2017 - - + Member since + 2017


Renders correctly in various states test education edit

- - From - + From

Renders correctly in various states test education edit

- - Completed on - - 3/4/2019 - - + Completed on + 3/4/2019

Renders correctly in various states test preferreded la

- - Member since - - 2017 - - + Member since + 2017


Renders correctly in various states test preferreded la

- - Member since - - 2017 - - + Member since + 2017


Renders correctly in various states test preferreded la

- - From - + From

Renders correctly in various states test preferreded la

- - Completed on - - 3/4/2019 - - + Completed on + 3/4/2019

Renders correctly in various states viewing other profi

- - Member since - - 2017 - - + Member since + 2017

Renders correctly in various states viewing other profi
- - Your profile information is only visible to you. Only your username is visible to others on localhost. - + Your profile information is only visible to you. Only your username is visible to others on localhost.

Renders correctly in various states viewing other profi

- - Member since - - 2017 - - + Member since + 2017

Renders correctly in various states viewing other profi
- - Your profile information is only visible to you. Only your username is visible to others on localhost. - + Your profile information is only visible to you. Only your username is visible to others on localhost.

Renders correctly in various states viewing own profile

- - Member since - - 2017 - - + Member since + 2017


Renders correctly in various states viewing own profile

- - Member since - - 2017 - - + Member since + 2017


Renders correctly in various states viewing own profile

- - From - + From

Renders correctly in various states viewing own profile

- - Completed on - - 3/4/2019 - - + Completed on + 3/4/2019

Renders correctly in various states while saving an edi

- - Member since - - 2017 - - + Member since + 2017


Renders correctly in various states while saving an edi

- - Member since - - 2017 - - + Member since + 2017


Renders correctly in various states while saving an edi

- - From - + From

Renders correctly in various states while saving an edi

- - Completed on - - 3/4/2019 - - + Completed on + 3/4/2019

Renders correctly in various states while saving an edi

- - Member since - - 2017 - - + Member since + 2017


Renders correctly in various states while saving an edi

- - Member since - - 2017 - - + Member since + 2017


Renders correctly in various states while saving an edi

- - From - + From

Renders correctly in various states while saving an edi

- - Completed on - - 3/4/2019 - - + Completed on + 3/4/2019