From e0249722102040006feecc60a9eaa7a5da7564a8 Mon Sep 17 00:00:00 2001 From: Felipe B Date: Thu, 22 Dec 2022 12:45:48 +0100 Subject: [PATCH 1/8] feat: add Outline Navigation Sidebar. --- .env | 1 + .env.development | 1 + .env.test | 1 + .eslintrc.js | 10 +++++ .github/workflows/validate.yml | 8 +--- docs/decisions/0010-outline-sidebar.md | 13 ++++++ jest.config.js | 1 + src/courseware/course/Course.jsx | 10 ++++- src/courseware/course/sequence/Sequence.jsx | 12 ++++- .../course/sidebar/SidebarOutlineTrigger.jsx | 15 +++++++ .../course/sidebar/SidebarTriggers.jsx | 31 ++++--------- .../course/sidebar/TriggerButton.jsx | 31 +++++++++++++ .../course/sidebar/sidebars/index.js | 10 ++++- .../sidebars/outline/OutlineSidebar.jsx | 43 ++++++++++++++++++ .../sidebars/outline/OutlineSidebar.test.jsx | 43 ++++++++++++++++++ .../sidebars/outline/OutlineTrigger.jsx | 29 ++++++++++++ .../sidebars/outline/OutlineTrigger.test.jsx | 45 +++++++++++++++++++ .../course/sidebar/sidebars/outline/index.js | 2 + .../sidebar/sidebars/outline/messages.js | 16 +++++++ src/index.jsx | 1 + src/setupTest.js | 1 + webpack.dev.config.js | 11 +++++ webpack.prod.config.js | 11 +++++ 23 files changed, 311 insertions(+), 35 deletions(-) create mode 100644 docs/decisions/0010-outline-sidebar.md create mode 100644 src/courseware/course/sidebar/SidebarOutlineTrigger.jsx create mode 100644 src/courseware/course/sidebar/TriggerButton.jsx create mode 100644 src/courseware/course/sidebar/sidebars/outline/OutlineSidebar.jsx create mode 100644 src/courseware/course/sidebar/sidebars/outline/OutlineSidebar.test.jsx create mode 100644 src/courseware/course/sidebar/sidebars/outline/OutlineTrigger.jsx create mode 100644 src/courseware/course/sidebar/sidebars/outline/OutlineTrigger.test.jsx create mode 100644 src/courseware/course/sidebar/sidebars/outline/index.js create mode 100644 src/courseware/course/sidebar/sidebars/outline/messages.js create mode 100644 webpack.dev.config.js create mode 100644 webpack.prod.config.js diff --git a/.env b/.env index c882fde382..0208b01f5b 100644 --- a/.env +++ b/.env @@ -11,6 +11,7 @@ CREDIT_HELP_LINK_URL='' CSRF_TOKEN_API_PATH='' DISCOVERY_API_BASE_URL='' DISCUSSIONS_MFE_BASE_URL='' +SIDEBAR_MFE_BASE_URL='' ECOMMERCE_BASE_URL='' ENABLE_JUMPNAV='true' ENABLE_NOTICES='' diff --git a/.env.development b/.env.development index f19135140b..499b947c78 100644 --- a/.env.development +++ b/.env.development @@ -11,6 +11,7 @@ CREDIT_HELP_LINK_URL='https://edx.readthedocs.io/projects/edx-guide-for-students CSRF_TOKEN_API_PATH='/csrf/api/v1/token' DISCOVERY_API_BASE_URL='http://localhost:18381' DISCUSSIONS_MFE_BASE_URL='http://localhost:2002' +SIDEBAR_MFE_BASE_URL='http://localhost:9090' ECOMMERCE_BASE_URL='http://localhost:18130' ENABLE_JUMPNAV='true' ENABLE_NOTICES='' diff --git a/.env.test b/.env.test index 147c12c182..390ba92704 100644 --- a/.env.test +++ b/.env.test @@ -11,6 +11,7 @@ CREDIT_HELP_LINK_URL='https://edx.readthedocs.io/projects/edx-guide-for-students CSRF_TOKEN_API_PATH='/csrf/api/v1/token' DISCOVERY_API_BASE_URL='http://localhost:18381' DISCUSSIONS_MFE_BASE_URL='http://localhost:2002' +SIDEBAR_MFE_BASE_URL='http://localhost:9090' ECOMMERCE_BASE_URL='http://localhost:18130' ENABLE_JUMPNAV='true' ENABLE_NOTICES='' diff --git a/.eslintrc.js b/.eslintrc.js index 9ed6c9fe81..b47abab542 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,6 +1,16 @@ const { createConfig } = require('@edx/frontend-build'); module.exports = createConfig('eslint', { + rules: { + 'import/no-unresolved': 'off', + }, + 'settings': { + 'import/resolver': { + 'node': { + 'paths': ['src'], + }, + }, + }, overrides: [{ files: ["**/__tests__/**/*.[jt]s?(x)", "**/?(*.)+(spec|test).[jt]s?(x)", "setupTest.js"], rules: { diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index d38a3991c9..622999c209 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -1,11 +1,9 @@ name: validate on: push: - branches: - - master + branches: [pearson-release/olive.master] pull_request: - branches: - - '**' + branches: [pearson-release/olive.master] jobs: tests: runs-on: ubuntu-latest @@ -20,5 +18,3 @@ jobs: - run: make validate.ci - name: Upload coverage uses: codecov/codecov-action@v3 - with: - fail_ci_if_error: true diff --git a/docs/decisions/0010-outline-sidebar.md b/docs/decisions/0010-outline-sidebar.md new file mode 100644 index 0000000000..0468f3fc98 --- /dev/null +++ b/docs/decisions/0010-outline-sidebar.md @@ -0,0 +1,13 @@ +# Add Outline Sidebar + +Following the [DISCOVERY](https://agile-jira.pearson.com/browse/PADV-213) made and the proposed [DESIGN](https://lucid.app/lucidchart/d52cc785-409f-4964-af29-ff277baa5bc5/edit?invitationId=inv_e569412e-e9f0-44aa-a9fa-54123d272f1a&referringApp=slack&page=l2M~LaFs47mo#), the MFE sidebar navigation is added in Frontend-App-Learning. + +Using currently logic in Frontend-App-Learning, **Outline Sidebar** is integrated following Discussion Sidebar. Using the already created **SidebarBase** function, the integration of the Outline sidebar is done through an iframe, which allows the integration of a Microfrontend located in a certain path, in this case using the port :9090, called **SIDEBAR_MFE_BASE_URL**. + +Then, using **SidebarTriggerBase**, the respective integration of the trigger is done, where to locate it in a different div from the one that already exists in src/courseware/course/Course.jsx module, the **SidebarOutlineTrigger** module is created. + +To show the Outline Sidebar it is necessary to take the context through the **SidebarContext** function to src/courseware/course/sequence/Sequence.jsx where if the Outline Sidebar is active it will be shown on the left, otherwise another sidebar will be shown in the default from the platform. + +## Next Step: Add Navigation Functionality + +The next step for the Outline Sidebar integration is to make the navigation more user friendly where it goes to the subsection the user needs without the need to open another tab. diff --git a/jest.config.js b/jest.config.js index 7020176f69..066b235bf6 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,6 +1,7 @@ const { createConfig } = require('@edx/frontend-build'); module.exports = createConfig('jest', { + modulePaths: ['src'], setupFilesAfterEnv: [ '/src/setupTest.js', ], diff --git a/src/courseware/course/Course.jsx b/src/courseware/course/Course.jsx index e6bf5b7072..b599a6bdb6 100644 --- a/src/courseware/course/Course.jsx +++ b/src/courseware/course/Course.jsx @@ -14,6 +14,7 @@ import ContentTools from './content-tools'; import CourseBreadcrumbs from './CourseBreadcrumbs'; import SidebarProvider from './sidebar/SidebarContextProvider'; import SidebarTriggers from './sidebar/SidebarTriggers'; +import SidebarOutlineTrigger from './sidebar/SidebarOutlineTrigger'; import { useModel } from '../../generic/model-store'; import { getSessionStorage, setSessionStorage } from '../../data/sessionStorage'; @@ -88,7 +89,7 @@ function Course({ {`${pageTitleBreadCrumbs.join(' | ')} | ${getConfig().SITE_NAME}`} -
+
)} -
+ +
+ {shouldDisplayTriggers && ( + + )} +
+ {isOutlineActive ? : null}
- + {isOutlineActive ? null : } {/** [MM-P2P] Experiment */} {(mmp2p.state.isEnabled && mmp2p.flyover.isVisible) && ( diff --git a/src/courseware/course/sidebar/SidebarOutlineTrigger.jsx b/src/courseware/course/sidebar/SidebarOutlineTrigger.jsx new file mode 100644 index 0000000000..cf82a3b8b6 --- /dev/null +++ b/src/courseware/course/sidebar/SidebarOutlineTrigger.jsx @@ -0,0 +1,15 @@ +import React from 'react'; +import TriggerButton from 'courseware/course/sidebar/TriggerButton'; +import { ID } from 'courseware/course/sidebar/sidebars/outline/OutlineTrigger'; + +function SidebarOutlineTrigger() { + return ( +
+ +
+ ); +} + +SidebarOutlineTrigger.propTypes = {}; + +export default SidebarOutlineTrigger; diff --git a/src/courseware/course/sidebar/SidebarTriggers.jsx b/src/courseware/course/sidebar/SidebarTriggers.jsx index b578f41bb4..45e42230b4 100644 --- a/src/courseware/course/sidebar/SidebarTriggers.jsx +++ b/src/courseware/course/sidebar/SidebarTriggers.jsx @@ -1,29 +1,14 @@ -import classNames from 'classnames'; -import React, { useContext } from 'react'; -import SidebarContext from './SidebarContext'; -import { SIDEBAR_ORDER, SIDEBARS } from './sidebars'; +import React from 'react'; +import { SIDEBAR_ORDER } from 'courseware/course/sidebar/sidebars'; +import TriggerButton from 'courseware/course/sidebar/TriggerButton'; function SidebarTriggers() { - const { - toggleSidebar, - currentSidebar, - } = useContext(SidebarContext); return ( -
- {SIDEBAR_ORDER.map((sidebarId) => { - const { Trigger } = SIDEBARS[sidebarId]; - const isActive = sidebarId === currentSidebar; - return ( -
- toggleSidebar(sidebarId)} key={sidebarId} /> -
- ); - })} -
+
+ {SIDEBAR_ORDER.map((sidebarId) => ( + + ))} +
); } diff --git a/src/courseware/course/sidebar/TriggerButton.jsx b/src/courseware/course/sidebar/TriggerButton.jsx new file mode 100644 index 0000000000..4d5b91bc1c --- /dev/null +++ b/src/courseware/course/sidebar/TriggerButton.jsx @@ -0,0 +1,31 @@ +import classNames from 'classnames'; +import PropTypes from 'prop-types'; +import React, { useContext } from 'react'; +import SidebarContext from './SidebarContext'; +import { SIDEBARS } from './sidebars'; + +function TriggerButton({ + sidebarId, +}) { + const { + toggleSidebar, + currentSidebar, + } = useContext(SidebarContext); + const { Trigger } = SIDEBARS[sidebarId]; + const isActive = sidebarId === currentSidebar; + return ( +
+ toggleSidebar(sidebarId)} key={sidebarId} /> +
+ ); +} + +TriggerButton.propTypes = { + sidebarId: PropTypes.string.isRequired, +}; + +export default TriggerButton; diff --git a/src/courseware/course/sidebar/sidebars/index.js b/src/courseware/course/sidebar/sidebars/index.js index 9128fd0e48..79d58edde1 100644 --- a/src/courseware/course/sidebar/sidebars/index.js +++ b/src/courseware/course/sidebar/sidebars/index.js @@ -1,7 +1,13 @@ -import * as notifications from './notifications'; -import * as discusssions from './discussions'; +import * as outline from 'courseware/course/sidebar/sidebars/outline'; +import * as notifications from 'courseware/course/sidebar/sidebars/notifications'; +import * as discusssions from 'courseware/course/sidebar/sidebars/discussions'; export const SIDEBARS = { + [outline.ID]: { + ID: outline.ID, + Sidebar: outline.Sidebar, + Trigger: outline.Trigger, + }, [notifications.ID]: { ID: notifications.ID, Sidebar: notifications.Sidebar, diff --git a/src/courseware/course/sidebar/sidebars/outline/OutlineSidebar.jsx b/src/courseware/course/sidebar/sidebars/outline/OutlineSidebar.jsx new file mode 100644 index 0000000000..b06f9a1dcb --- /dev/null +++ b/src/courseware/course/sidebar/sidebars/outline/OutlineSidebar.jsx @@ -0,0 +1,43 @@ +import { ensureConfig, getConfig } from '@edx/frontend-platform'; +import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; +import React, { useContext } from 'react'; +import SidebarBase from 'courseware/course/sidebar/common/SidebarBase'; +import SidebarContext from 'courseware/course/sidebar/SidebarContext'; +import { ID } from 'courseware/course/sidebar/sidebars/outline/OutlineTrigger'; + +import messages from './messages'; + +ensureConfig(['SIDEBAR_MFE_BASE_URL']); + +function OutlineSidebar({ intl }) { + const { + courseId, + } = useContext(SidebarContext); + + const outlineUrl = `${getConfig().SIDEBAR_MFE_BASE_URL}/${courseId}`; + return ( + +