From 15409efe98a4cd8365ca3c3c5799865ab1585493 Mon Sep 17 00:00:00 2001 From: javier ontiveros Date: Wed, 8 May 2024 16:37:27 -0600 Subject: [PATCH 01/10] chore: updated oex components library --- package-lock.json | 207 ++++++++++++++++++++++++++++++++++++++-------- package.json | 2 +- 2 files changed, 173 insertions(+), 36 deletions(-) diff --git a/package-lock.json b/package-lock.json index d616c1f..7a9cbcd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "license": "AGPL-3.0", "dependencies": { "@edx/brand": "npm:@edx/brand-openedx@1.2.0", - "@edx/frontend-app-learning": "github:wgu-opensource/frontend-app-learning#0.5.5", + "@edx/frontend-app-learning": "github:wgu-opensource/frontend-app-learning#0.6.0", "@edx/frontend-component-footer": "12.1.2", "@edx/frontend-component-header": "4.2.3", "@edx/frontend-platform": "4.2.0", @@ -2012,36 +2012,43 @@ }, "node_modules/@edx/frontend-app-learning": { "version": "1.0.0-semantically-released", - "resolved": "git+ssh://git@github.com/wgu-opensource/frontend-app-learning.git#4e4f41abd188d0b1a164972756e8ab6f9b4b3a95", + "resolved": "git+ssh://git@github.com/wgu-opensource/frontend-app-learning.git#bed47cd9f52121a51fc5865d4048fe49b5fc74e1", "hasInstallScript": true, "license": "AGPL-3.0", "dependencies": { - "@edx/brand": "npm:@edx/brand-openedx@1.1.0", + "@edx/brand": "npm:@edx/brand-openedx@1.2.0", "@edx/frontend-component-footer": "^12.0.0", "@edx/frontend-component-header": "^4.0.0", "@edx/frontend-lib-special-exams": "^2.16.1", "@edx/frontend-platform": "^4.2.0", + "@edx/paragon": "20.28.4", "@fortawesome/fontawesome-svg-core": "1.3.0", "@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", - "@popperjs/core": "2.11.5", + "@popperjs/core": "2.11.6", "@reduxjs/toolkit": "1.8.1", - "classnames": "2.3.1", + "classnames": "2.3.2", "core-js": "3.22.2", - "history": "^5.3.0", + "history": "5.3.0", "js-cookie": "3.0.1", "lodash.camelcase": "4.3.0", "patch-package": "^8.0.0", - "query-string": "^7.1.1", + "prop-types": "15.8.1", + "query-string": "7.1.3", + "react": "16.14.0", + "react-dom": "16.14.0", "react-helmet": "6.1.0", + "react-redux": "7.2.9", "react-router": "5.2.1", - "react-share": "4.4.0", - "regenerator-runtime": "0.13.9", - "reselect": "4.1.5", + "react-router-dom": "5.3.0", + "react-share": "4.4.1", + "redux": "4.1.2", + "regenerator-runtime": "0.13.11", + "reselect": "4.1.7", "truncate-html": "1.0.4", - "util": "0.12.4" + "util": "0.12.5" }, "peerDependencies": { "@edx/frontend-build": ">= 8.1.0 || ^12.9.0-alpha.1", @@ -2054,11 +2061,62 @@ "redux": "^4.0.4" } }, - "node_modules/@edx/frontend-app-learning/node_modules/@edx/brand": { - "name": "@edx/brand-openedx", - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@edx/brand-openedx/-/brand-openedx-1.1.0.tgz", - "integrity": "sha512-ne2ZKF1r0akkt0rEzCAQAk4cTDTI2GiWCpc+T7ldQpw9X57OnUB16dKsFNe40C9uEjL5h3Ps/ZsFM5dm4cIkEQ==" + "node_modules/@edx/frontend-app-learning/node_modules/@edx/paragon": { + "version": "20.28.4", + "resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-20.28.4.tgz", + "integrity": "sha512-JiEAUaEuOnHB/zC8h9d5f3AwchREiHFKbXFm7JpXNcvXpkTj0TTKTCS6zcfwZeDl77q/+Rx6Js7SpSE2EzAtDg==", + "dependencies": { + "@fortawesome/fontawesome-svg-core": "^6.1.1", + "@fortawesome/react-fontawesome": "^0.1.18", + "@popperjs/core": "^2.11.4", + "bootstrap": "^4.6.2", + "classnames": "^2.3.1", + "email-prop-type": "^3.0.0", + "file-selector": "^0.6.0", + "font-awesome": "^4.7.0", + "glob": "^8.0.3", + "lodash.uniqby": "^4.7.0", + "mailto-link": "^2.0.0", + "prop-types": "^15.8.1", + "react-bootstrap": "^1.6.5", + "react-dropzone": "^14.2.1", + "react-focus-on": "^3.5.4", + "react-loading-skeleton": "^3.1.0", + "react-popper": "^2.2.5", + "react-proptype-conditional-require": "^1.0.4", + "react-responsive": "^8.2.0", + "react-table": "^7.7.0", + "react-transition-group": "^4.4.2", + "tabbable": "^5.3.3", + "uncontrollable": "^7.2.1", + "uuid": "^9.0.0" + }, + "peerDependencies": { + "react": "^16.8.6 || ^17.0.0", + "react-dom": "^16.8.6 || ^17.0.0", + "react-intl": "^5.25.1" + } + }, + "node_modules/@edx/frontend-app-learning/node_modules/@edx/paragon/node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.5.2.tgz", + "integrity": "sha512-gBxPg3aVO6J0kpfHNILc+NMhXnqHumFxOmjYCFfOiLZfwhnnfhtsdA2hfJlDnj+8PjAs6kKQPenOTKj3Rf7zHw==", + "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@edx/frontend-app-learning/node_modules/@edx/paragon/node_modules/@fortawesome/fontawesome-svg-core": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.5.2.tgz", + "integrity": "sha512-5CdaCBGl8Rh9ohNdxeeTMxIj8oc3KNBgIeLMvJosBMdslK/UnEB8rzyDRrbKdL1kDweqBPo4GT9wvnakHWucZw==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.5.2" + }, + "engines": { + "node": ">=6" + } }, "node_modules/@edx/frontend-app-learning/node_modules/@fortawesome/fontawesome-common-types": { "version": "0.3.0", @@ -2096,14 +2154,27 @@ } }, "node_modules/@edx/frontend-app-learning/node_modules/@popperjs/core": { - "version": "2.11.5", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.5.tgz", - "integrity": "sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==", + "version": "2.11.6", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", + "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==", "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" } }, + "node_modules/@edx/frontend-app-learning/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@edx/frontend-app-learning/node_modules/classnames": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz", + "integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==" + }, "node_modules/@edx/frontend-app-learning/node_modules/core-js": { "version": "3.22.2", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.22.2.tgz", @@ -2115,6 +2186,24 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/@edx/frontend-app-learning/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/@edx/frontend-app-learning/node_modules/history": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/history/-/history-5.3.0.tgz", @@ -2123,6 +2212,17 @@ "@babel/runtime": "^7.7.6" } }, + "node_modules/@edx/frontend-app-learning/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/@edx/frontend-app-learning/node_modules/query-string": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", @@ -2165,6 +2265,36 @@ "react": ">=15" } }, + "node_modules/@edx/frontend-app-learning/node_modules/react-router-dom": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.0.tgz", + "integrity": "sha512-ObVBLjUZsphUUMVycibxgMdh5jJ1e3o+KpAZBVeHcNQZ4W+uUGGWsokurzlF4YOldQYRQL4y6yFRWM4m3svmuQ==", + "dependencies": { + "@babel/runtime": "^7.12.13", + "history": "^4.9.0", + "loose-envify": "^1.3.1", + "prop-types": "^15.6.2", + "react-router": "5.2.1", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0" + }, + "peerDependencies": { + "react": ">=15" + } + }, + "node_modules/@edx/frontend-app-learning/node_modules/react-router-dom/node_modules/history": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", + "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", + "dependencies": { + "@babel/runtime": "^7.1.2", + "loose-envify": "^1.2.0", + "resolve-pathname": "^3.0.0", + "tiny-invariant": "^1.0.2", + "tiny-warning": "^1.0.0", + "value-equal": "^1.0.1" + } + }, "node_modules/@edx/frontend-app-learning/node_modules/react-router/node_modules/history": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", @@ -2178,15 +2308,18 @@ "value-equal": "^1.0.1" } }, - "node_modules/@edx/frontend-app-learning/node_modules/regenerator-runtime": { - "version": "0.13.9", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" + "node_modules/@edx/frontend-app-learning/node_modules/redux": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.1.2.tgz", + "integrity": "sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } }, "node_modules/@edx/frontend-app-learning/node_modules/reselect": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.5.tgz", - "integrity": "sha512-uVdlz8J7OO+ASpBYoz1Zypgx0KasCY20H+N8JD13oUMtPvSHQuscrHop4KbXrbsBcdB9Ds7lVK7eRkBIfO43vQ==" + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-4.1.7.tgz", + "integrity": "sha512-Zu1xbUt3/OPwsXL46hvOOoQrap2azE7ZQbokq61BQfiXvhewsKDwhMeZjTX9sX0nvw1t/U5Audyn1I9P/m9z0A==" }, "node_modules/@edx/frontend-build": { "version": "12.9.2", @@ -20203,11 +20336,11 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/react-share": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/react-share/-/react-share-4.4.0.tgz", - "integrity": "sha512-POe8Ge/JT9Ew9iyW7CiYsCCWCb8uMJWqFl9S7W0fJ/oH5gBJNzukH0bL5vSr17KKG5h15d3GfKaoviI22BKeYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/react-share/-/react-share-4.4.1.tgz", + "integrity": "sha512-AJ9m9RiJssqvYg7MoJUc9J0D7b/liWrsfQ99ndKc5vJ4oVHHd4Fy87jBlKEQPibT40oYA3AQ/a9/oQY6/yaigw==", "dependencies": { - "classnames": "^2.2.5", + "classnames": "^2.3.2", "jsonp": "^0.2.1" }, "engines": { @@ -20215,9 +20348,14 @@ "npm": ">=5.0.0" }, "peerDependencies": { - "react": "^16.3.0 || ^17" + "react": "^16.3.0 || ^17 || ^18" } }, + "node_modules/react-share/node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==" + }, "node_modules/react-side-effect": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/react-side-effect/-/react-side-effect-2.1.2.tgz", @@ -22440,15 +22578,14 @@ } }, "node_modules/util": { - "version": "0.12.4", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", - "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", - "safe-buffer": "^5.1.2", "which-typed-array": "^1.1.2" } }, diff --git a/package.json b/package.json index d189474..a584205 100644 --- a/package.json +++ b/package.json @@ -42,7 +42,7 @@ }, "dependencies": { "@edx/brand": "npm:@edx/brand-openedx@1.2.0", - "@edx/frontend-app-learning": "github:wgu-opensource/frontend-app-learning#0.5.5", + "@edx/frontend-app-learning": "github:wgu-opensource/frontend-app-learning#0.6.0", "@edx/frontend-component-footer": "12.1.2", "@edx/frontend-component-header": "4.2.3", "@edx/frontend-platform": "4.2.0", From da5d047e045e82d800f868ce93a493578063171b Mon Sep 17 00:00:00 2001 From: javier ontiveros Date: Wed, 17 Jul 2024 16:02:07 -0600 Subject: [PATCH 02/10] chore: fixed broken tests --- .../SequenceContainer.test.jsx | 35 ++++--------------- src/features/sidebar/Sidebar.test.jsx | 17 +++++---- 2 files changed, 17 insertions(+), 35 deletions(-) diff --git a/src/features/course-player/sequence-container/SequenceContainer.test.jsx b/src/features/course-player/sequence-container/SequenceContainer.test.jsx index 9744e79..ec53551 100644 --- a/src/features/course-player/sequence-container/SequenceContainer.test.jsx +++ b/src/features/course-player/sequence-container/SequenceContainer.test.jsx @@ -22,11 +22,11 @@ describe('SequenceContainer', () => { unitId: unitBlocks[0].id, sequenceId: courseware.sequenceId, courseId: courseware.courseId, - unitNavigationHandler: () => {}, - nextSequenceHandler: () => {}, - previousSequenceHandler: () => {}, - toggleNotificationTray: () => {}, - setNotificationStatus: () => {}, + unitNavigationHandler: () => { }, + nextSequenceHandler: () => { }, + previousSequenceHandler: () => { }, + toggleNotificationTray: () => { }, + setNotificationStatus: () => { }, }; }); @@ -45,8 +45,8 @@ describe('SequenceContainer', () => { it('handles loading unit', async () => { render(); expect(await screen.findByText('Loading learning sequence...')).toBeInTheDocument(); - // Renders navigation buttons plus one button for each unit. - expect(screen.getAllByRole('button')).toHaveLength(3 + unitBlocks.length); + // Renders navigation buttons (4 prev, next, bookmark, notificaitons tray) plus one button for each unit. + expect(screen.getAllByRole('button')).toHaveLength(4 + unitBlocks.length); loadUnit(); await waitFor(() => expect(screen.queryByText('Loading learning sequence...')).not.toBeInTheDocument()); @@ -70,27 +70,6 @@ describe('SequenceContainer', () => { expect(nav).not.toBeVisible(); }); - it('has sidebar triggers button hidden', async () => { - // Sidebar triggers button is part of the Sequence component, shown only on sm screens, - // but we don't want to show it - - // Set width to less than small so the component is rendered - global.innerWidth = breakpoints.extraSmall.maxWidth; - - const { container } = render(); - appendStyles(container); - - let button; - await waitFor(() => { - button = container.querySelector('[aria-label="Show notification tray"]'); - if (!button) { - throw new Error('Sidebar triggers button not found in the DOM'); - } - }); - - expect(button).not.toBeVisible(); - }); - it('has bookmark button hidden', async () => { // Bookmark button is part of the Sequence component, but we don't want to show it const { container } = render(); diff --git a/src/features/sidebar/Sidebar.test.jsx b/src/features/sidebar/Sidebar.test.jsx index 77a3f08..55e81f8 100644 --- a/src/features/sidebar/Sidebar.test.jsx +++ b/src/features/sidebar/Sidebar.test.jsx @@ -27,7 +27,7 @@ describe('', () => { it('renders without crashing', () => { const currentUnitId = Object.keys(sidebarMockStore.models.units)[0]; - const { getAllByText } = render(, { store }); + const { getAllByText } = render(, { store }); const { models: { sections, sequences, units } } = store.getState(); Object.values(sections).forEach(section => { @@ -48,7 +48,7 @@ describe('', () => { it('section and sequence expand when click on it', async () => { const currentUnitId = Object.keys(sidebarMockStore.models.units)[0]; - const { getByTestId } = render(, { store }); + const { getByTestId } = render(, { store }); const sectionCollapsable = getByTestId('section-collapsable-block-v1:edX+DemoX+Demo_Course+type@chapter+block@d8a6192ade314473a78242dfeedfbf5b'); expect(sectionCollapsable.classList.contains('collapsed')).toBeTruthy(); @@ -65,7 +65,7 @@ describe('', () => { it('collapse all sidebar items', () => { const currentUnitId = Object.keys(sidebarMockStore.models.units)[0]; - const { getByTestId } = render(, { store }); + const { getByTestId } = render(, { store }); const collapseAllButton = getByTestId('collapse-all-button'); fireEvent.click(collapseAllButton); @@ -75,7 +75,7 @@ describe('', () => { const scrollIntoViewMock = jest.fn(); HTMLElement.prototype.scrollIntoView = scrollIntoViewMock; const currentUnitId = Object.keys(sidebarMockStore.models.units)[0]; - const { getByTestId } = render(, { store }); + const { getByTestId } = render(, { store }); const expandAllButton = getByTestId('expand-all-button'); fireEvent.click(expandAllButton); @@ -83,7 +83,7 @@ describe('', () => { it('closes sidebar on unit selection in mobile', async () => { const currentUnitId = Object.keys(sidebarMockStore.models.units)[0]; - const { getByTestId } = render(, { store }); + const { getByTestId } = render(, { store }); // Sidebar is already open according to mock data in sidebarMockStore const { courseView: { isMobileSidebarOpen: isMobileSidebarOpenInitial } } = store.getState(); @@ -118,7 +118,10 @@ describe('', () => { }, }); - const { queryByTestId } = render(, { store: emptyStore }); + const { queryByTestId } = render(, { store: emptyStore }); const loader = queryByTestId('simple-loader'); expect(loader).toBeInTheDocument(); @@ -126,7 +129,7 @@ describe('', () => { it('doesnt show loading spinner when sectionSequenceUnits is not empty', async () => { const currentUnitId = Object.keys(sidebarMockStore.models.units)[0]; - const { queryByTestId } = render(, { store }); + const { queryByTestId } = render(, { store }); const loader = queryByTestId('simple-loader'); expect(loader).not.toBeInTheDocument(); From e3a42aa09ec6095a9eb0b10fd8948a489f41b99c Mon Sep 17 00:00:00 2001 From: Josh McLaughlin Date: Thu, 21 Nov 2024 11:42:17 -0800 Subject: [PATCH 03/10] Update pendo IIFE handling to evaluate configured IIFE The pendo feature now evaluates the IIFE passed in via the MFE configuration, and passes it two arguments, first is the base URL of the lms in `lmsUrl`, and the second is the username in `username`. Note that no error handling is provided for the IIFE, any exceptions within the IIFE will be passed up to the browser. This assists in debugging issues with the IIFE but risks causing errors for end users in the MFE if the IIFE does not catch exceptions. --- src/features/pendo/Pendo.jsx | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/features/pendo/Pendo.jsx b/src/features/pendo/Pendo.jsx index 8d7014e..a34cd27 100644 --- a/src/features/pendo/Pendo.jsx +++ b/src/features/pendo/Pendo.jsx @@ -26,14 +26,19 @@ function pendoHelperUser(pendoKey) { } // Use this function for a custom visitor ID -function pendoHelperCustom(pendoKey) { +async function pendoHelperCustom(pendoKey) { // eslint-disable-next-line prefer-const - let visitor = null; + const { username } = getAuthenticatedUser(); - // Iife should retreive custom visitor ID and assign to visitor - // eslint-disable-next-line no-unused-expressions - function customVisitorIife() { getConfig().PENDO_VISITOR_IIFE; } - customVisitorIife(); + // eslint-disable-next-line no-shadow + async function customVisitorIife(lmsUrl, username) { + if (getConfig().PENDO_VISITOR_IIFE) { + // eslint-disable-next-line no-new-func + return new Function('lmsUrl', 'username', `return ${getConfig().PENDO_VISITOR_IIFE};`)(lmsUrl, username); + } + return null; + } + const visitor = await customVisitorIife(getConfig().LMS_BASE_URL, username); if (localStorage.getItem(pendoKey === null)) { localStorage.setItem(pendoKey, visitor); From f6fe6eb6a84bd2c06ebe5a607866f9867a9da6d4 Mon Sep 17 00:00:00 2001 From: Josh McLaughlin Date: Thu, 21 Nov 2024 13:45:33 -0800 Subject: [PATCH 04/10] Fix username retrieval in pendoHelperCustom --- src/features/pendo/Pendo.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/features/pendo/Pendo.jsx b/src/features/pendo/Pendo.jsx index a34cd27..caeb4c0 100644 --- a/src/features/pendo/Pendo.jsx +++ b/src/features/pendo/Pendo.jsx @@ -28,7 +28,8 @@ function pendoHelperUser(pendoKey) { // Use this function for a custom visitor ID async function pendoHelperCustom(pendoKey) { // eslint-disable-next-line prefer-const - const { username } = getAuthenticatedUser(); + const authenticatedUser = getAuthenticatedUser(); + const username = authenticatedUser?.username ?? null; // eslint-disable-next-line no-shadow async function customVisitorIife(lmsUrl, username) { From 63903ba05de2da2a8ecaeeafc71c72e6823ae0c3 Mon Sep 17 00:00:00 2001 From: Josh McLaughlin Date: Wed, 30 Oct 2024 16:49:33 -0700 Subject: [PATCH 05/10] Add dashboard return link to the logo in the header This modifies the header in the logo to link back to the main frontend, and configuration options, to enable the header link and set the base URL for the main frontend (DASHBOARD_BASE_URL) --- .env | 1 + .env.acaddev | 2 ++ .env.development | 1 + src/features/header/Header.jsx | 22 +++++++++++++++++----- src/index.jsx | 2 ++ 5 files changed, 23 insertions(+), 5 deletions(-) diff --git a/.env b/.env index fd3b59f..c2fd5a2 100644 --- a/.env +++ b/.env @@ -55,6 +55,7 @@ MFE_CONFIG_API_URL='' DISABLE_DESKTOP_HEADER=false DISABLE_HEADER_LOGO=false +ENABLE_DASHBOARD_RETURN_LINK=false ACCESS_DENIED_PAGE_INSTRUCTIONS_LINK='' ACCESS_DENIED_PAGE_STUDENT_PORTAL_LINK='' ACCESS_DENIED_PAGE_STUDENT_PORTAL_LINK_TEXT='' diff --git a/.env.acaddev b/.env.acaddev index 7c7178a..4634ffe 100644 --- a/.env.acaddev +++ b/.env.acaddev @@ -61,6 +61,8 @@ MFE_CONFIG_API_URL='https://devapp.learn.academy.test/api/mfe_config/v1' DISABLE_APP_FOOTER=true DISABLE_DESKTOP_HEADER=true DISABLE_HEADER_LOGO=true +ENABLE_DASHBOARD_RETURN_LINK=true +DASHBOARD_BASE_URL='https://academy.test' ACCESS_DENIED_PAGE_INSTRUCTIONS_LINK='http://example.com' ACCESS_DENIED_PAGE_STUDENT_PORTAL_LINK='https://learn.academy.test/dashboard' ACCESS_DENIED_PAGE_STUDENT_PORTAL_LINK_TEXT='Student Dashboard' diff --git a/.env.development b/.env.development index 5c72a16..ddc1d75 100644 --- a/.env.development +++ b/.env.development @@ -61,6 +61,7 @@ MFE_CONFIG_API_URL='http://apps.local.overhang.io/api/mfe_config/v1' DISABLE_APP_FOOTER=false DISABLE_DESKTOP_HEADER=false DISABLE_HEADER_LOGO=false +ENABLE_DASHBOARD_RETURN_LINK=false ACCESS_DENIED_PAGE_INSTRUCTIONS_LINK='http://example.com' ACCESS_DENIED_PAGE_STUDENT_PORTAL_LINK='http://local.overhang.io/dashboard' ACCESS_DENIED_PAGE_STUDENT_PORTAL_LINK_TEXT='Student Portal' diff --git a/src/features/header/Header.jsx b/src/features/header/Header.jsx index bb2f9b8..fc119d2 100644 --- a/src/features/header/Header.jsx +++ b/src/features/header/Header.jsx @@ -17,6 +17,8 @@ const Header = () => { const logo = getConfig().LOGO_WHITE_URL; const logoAltText = `${getConfig().SITE_OPERATOR} logo`; const courseTitle = course?.title || ''; + const enableDashboardReturnLink = getConfig().ENABLE_DASHBOARD_RETURN_LINK === true; + const dashboardReturnLink = `${getConfig().DASHBOARD_BASE_URL}/course/overview/${course?.courseId}`; const dispatch = useDispatch(); @@ -28,11 +30,21 @@ const Header = () => {
{ !disableHeaderLogo && ( - + enableDashboardReturnLink ? ( + + + + ) : ( + + ) ) } {`${courseTitle}`} diff --git a/src/index.jsx b/src/index.jsx index a79b577..2a4efde 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -94,6 +94,8 @@ initialize({ DISABLE_APP_FOOTER: process.env.DISABLE_APP_FOOTER === 'true' || null, DISABLE_HEADER_LOGO: process.env.DISABLE_HEADER_LOGO === 'true' || null, DISABLE_DESKTOP_HEADER: process.env.DISABLE_DESKTOP_HEADER === 'true' || null, + DASHBOARD_BASE_URL: process.env.DASHBOARD_BASE_URL || null, + ENABLE_DASHBOARD_RETURN_LINK: process.env.ENABLE_DASHBOARD_RETURN_LINK === 'true' || null, ACCESS_DENIED_PAGE_INSTRUCTIONS_LINK: process.env.ACCESS_DENIED_PAGE_INSTRUCTIONS_LINK || '', ACCESS_DENIED_PAGE_STUDENT_PORTAL_LINK: process.env.ACCESS_DENIED_PAGE_STUDENT_PORTAL_LINK || '', ACCESS_DENIED_PAGE_STUDENT_PORTAL_LINK_TEXT: process.env.ACCESS_DENIED_PAGE_STUDENT_PORTAL_LINK_TEXT || 'Student Portal', From 72bf1ffa23a8b54aed9ac6c9fcd236eaf47b6490 Mon Sep 17 00:00:00 2001 From: Josh McLaughlin Date: Fri, 1 Nov 2024 16:25:04 -0700 Subject: [PATCH 06/10] Add configurable footer links with FOOTER_LINK_MAP This adds a new configuration option FOOTER_LINK_MAP which overrides the default set of links in the footer. It's set as a json blob in the same format of the existing link map. --- .env.acaddev | 1 + src/features/footer/Footer.jsx | 13 +++++++------ src/features/layout/Layout.jsx | 2 ++ src/index.jsx | 1 + 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.env.acaddev b/.env.acaddev index 4634ffe..818db14 100644 --- a/.env.acaddev +++ b/.env.acaddev @@ -61,6 +61,7 @@ MFE_CONFIG_API_URL='https://devapp.learn.academy.test/api/mfe_config/v1' DISABLE_APP_FOOTER=true DISABLE_DESKTOP_HEADER=true DISABLE_HEADER_LOGO=true +FOOTER_LINK_MAP='[{"label": "Help Center", "url": "https://academy.test/help-center", "id": 1}]' ENABLE_DASHBOARD_RETURN_LINK=true DASHBOARD_BASE_URL='https://academy.test' ACCESS_DENIED_PAGE_INSTRUCTIONS_LINK='http://example.com' diff --git a/src/features/footer/Footer.jsx b/src/features/footer/Footer.jsx index 6f59231..27a966b 100644 --- a/src/features/footer/Footer.jsx +++ b/src/features/footer/Footer.jsx @@ -17,16 +17,11 @@ ensureConfig([ 'SITE_NAME', ], 'Footer component'); -const Footer = ({ className }) => { +const Footer = ({ className, links }) => { const { config } = useContext(AppContext); const adaUrl = getConfig().ADA_URL; const copyRight = getConfig().COPYRIGHT_STRING; - const links = [ - { label: 'Privacy Policy', url: getConfig().PRIVACY_POLICY_URL, id: 1 }, - { label: 'Terms of Service', url: getConfig().TERMS_OF_SERVICE_URL, id: 2 }, - { label: 'Honor Code', url: getConfig().HONOR_CODE_URL, id: 3 }, - ]; const logo = getConfig().LOGO_TRADEMARK_URL; const logoAltText = `${getConfig().SITE_OPERATOR} logo`; @@ -98,10 +93,16 @@ const Footer = ({ className }) => { Footer.propTypes = { className: PropTypes.string, + links: PropTypes.arrayOf(PropTypes.objectOf(PropTypes.string)), }; Footer.defaultProps = { className: '', + links: [ + { label: 'Privacy Policy', url: getConfig().PRIVACY_POLICY_URL, id: 1 }, + { label: 'Terms of Service', url: getConfig().TERMS_OF_SERVICE_URL, id: 2 }, + { label: 'Honor Code', url: getConfig().HONOR_CODE_URL, id: 3 }, + ], }; export default Footer; diff --git a/src/features/layout/Layout.jsx b/src/features/layout/Layout.jsx index 7ecccfc..b8f507d 100644 --- a/src/features/layout/Layout.jsx +++ b/src/features/layout/Layout.jsx @@ -20,6 +20,7 @@ const Layout = ({ children }) => { const isMobileDevice = useMediaQuery({ query: '(max-width: 1200px)' }); const layoutHasSidebar = useSelector(layoutHasSidebarSelector); const isSidebarExtended = useSelector(isDesktopSidebarExtendedSelector); + const customLinks = getConfig().FOOTER_LINK_MAP ? JSON.parse(getConfig().FOOTER_LINK_MAP) : undefined; return ( <> @@ -33,6 +34,7 @@ const Layout = ({ children }) => { 'footer-no-sidebar': !layoutHasSidebar, 'footer-sidebar-extended': layoutHasSidebar && isSidebarExtended, })} + links={customLinks} /> )} diff --git a/src/index.jsx b/src/index.jsx index 2a4efde..e40d1bc 100644 --- a/src/index.jsx +++ b/src/index.jsx @@ -95,6 +95,7 @@ initialize({ DISABLE_HEADER_LOGO: process.env.DISABLE_HEADER_LOGO === 'true' || null, DISABLE_DESKTOP_HEADER: process.env.DISABLE_DESKTOP_HEADER === 'true' || null, DASHBOARD_BASE_URL: process.env.DASHBOARD_BASE_URL || null, + FOOTER_LINK_MAP: process.env.FOOTER_LINK_MAP || null, ENABLE_DASHBOARD_RETURN_LINK: process.env.ENABLE_DASHBOARD_RETURN_LINK === 'true' || null, ACCESS_DENIED_PAGE_INSTRUCTIONS_LINK: process.env.ACCESS_DENIED_PAGE_INSTRUCTIONS_LINK || '', ACCESS_DENIED_PAGE_STUDENT_PORTAL_LINK: process.env.ACCESS_DENIED_PAGE_STUDENT_PORTAL_LINK || '', From d8fca4aa0c33213e7ac01ee0e704b57dfaf0b7eb Mon Sep 17 00:00:00 2001 From: Josh McLaughlin Date: Fri, 1 Nov 2024 16:33:05 -0700 Subject: [PATCH 07/10] Set logo height and default options for academy development --- .env.acaddev | 6 +++--- src/features/header/Header.scss | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.env.acaddev b/.env.acaddev index 818db14..011a3aa 100644 --- a/.env.acaddev +++ b/.env.acaddev @@ -58,9 +58,9 @@ PUBLIC_PATH='/wgulearning/' APP_ID='wgulearning' MFE_CONFIG_API_URL='https://devapp.learn.academy.test/api/mfe_config/v1' -DISABLE_APP_FOOTER=true -DISABLE_DESKTOP_HEADER=true -DISABLE_HEADER_LOGO=true +DISABLE_APP_FOOTER=false +DISABLE_DESKTOP_HEADER=false +DISABLE_HEADER_LOGO=false FOOTER_LINK_MAP='[{"label": "Help Center", "url": "https://academy.test/help-center", "id": 1}]' ENABLE_DASHBOARD_RETURN_LINK=true DASHBOARD_BASE_URL='https://academy.test' diff --git a/src/features/header/Header.scss b/src/features/header/Header.scss index d92d5a1..865c5fc 100644 --- a/src/features/header/Header.scss +++ b/src/features/header/Header.scss @@ -24,7 +24,7 @@ .logo { max-height: 32px; - height: 32px; + height: 24px; width: auto; @media (max-width: map-get($grid-breakpoints, "xl")) { From 0271b9abe79690288fe1cec35e8aa4cc90416bfe Mon Sep 17 00:00:00 2001 From: Josh McLaughlin Date: Fri, 1 Nov 2024 17:19:18 -0700 Subject: [PATCH 08/10] Add tests --- src/features/footer/Footer.jsx | 2 +- src/features/footer/Footer.test.jsx | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/features/footer/Footer.jsx b/src/features/footer/Footer.jsx index 27a966b..4d0e301 100644 --- a/src/features/footer/Footer.jsx +++ b/src/features/footer/Footer.jsx @@ -93,7 +93,7 @@ const Footer = ({ className, links }) => { Footer.propTypes = { className: PropTypes.string, - links: PropTypes.arrayOf(PropTypes.objectOf(PropTypes.string)), + links: PropTypes.arrayOf(PropTypes.objectOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number]))), }; Footer.defaultProps = { diff --git a/src/features/footer/Footer.test.jsx b/src/features/footer/Footer.test.jsx index 635b5c7..9bfbd85 100644 --- a/src/features/footer/Footer.test.jsx +++ b/src/features/footer/Footer.test.jsx @@ -21,4 +21,15 @@ describe('Footer', () => { ), ).toBeInTheDocument()); }); + it('Renders Links', async () => { + render( +