Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: organize plugin slots as components, add footer slot #1381

Merged
merged 1 commit into from
May 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ The Learning MFE is similar to all the other Open edX MFEs. Read the Open
edX Developer Guide's section on
`MFE applications <https://edx.readthedocs.io/projects/edx-developer-docs/en/latest/developers_guide/micro_frontends_in_open_edx.html>`_.

Plugins
=======
This MFE can be customized using `Frontend Plugin Framework <https://github.com/openedx/frontend-plugin-framework>`_.

The parts of this MFE that can be customized in that manner are documented `here </src/plugin-slots>`_.

Environment Variables
======================

Expand Down
14,574 changes: 8,296 additions & 6,278 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
},
"dependencies": {
"@edx/brand": "npm:@openedx/brand-openedx@^1.2.2",
"@edx/frontend-component-footer": "^13.0.4",
"@edx/frontend-component-header": "^5.0.2",
"@edx/frontend-lib-learning-assistant": "^2.0.0",
"@edx/frontend-lib-special-exams": "^3.0.1",
Expand All @@ -43,7 +42,8 @@
"@fortawesome/free-regular-svg-icons": "5.15.4",
"@fortawesome/free-solid-svg-icons": "5.15.4",
"@fortawesome/react-fontawesome": "^0.1.4",
"@openedx/frontend-plugin-framework": "^1.0.2",
"@openedx/frontend-plugin-framework": "^1.1.2",
"@openedx/frontend-slot-footer": "^1.0.2",
"@openedx/paragon": "^22.3.0",
"@popperjs/core": "2.11.8",
"@reduxjs/toolkit": "1.8.1",
Expand Down
10 changes: 2 additions & 8 deletions src/courseware/course/sequence/Sequence.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import {
import { useIntl } from '@edx/frontend-platform/i18n';
import { useSelector } from 'react-redux';
import SequenceExamWrapper from '@edx/frontend-lib-special-exams';
import { PluginSlot } from '@openedx/frontend-plugin-framework';

import PageLoading from '@src/generic/PageLoading';
import { useModel } from '@src/generic/model-store';
import { useSequenceBannerTextAlert, useSequenceEntranceExamAlert } from '@src/alerts/sequence-alerts/hooks';
import SequenceContainerSlot from '../../../plugin-slots/SequenceContainerSlot';

import CourseLicense from '../course-license';
import Sidebar from '../sidebar/Sidebar';
Expand Down Expand Up @@ -196,13 +196,7 @@ const Sequence = ({
</div>
{isNewDiscussionSidebarViewEnabled ? <NewSidebar /> : <Sidebar />}
</div>
<PluginSlot
id="sequence_container_plugin"
pluginProps={{
courseId,
unitId,
}}
/>
<SequenceContainerSlot courseId={courseId} unitId={unitId} />
</>
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,9 @@ exports[`Unit component output snapshot: not bookmarked, do not show content 1`]
>
unit-title
</h3>
<PluginSlot
id="unit_title_plugin"
pluginProps={
Object {
"courseId": "test-course-id",
"unitId": "test-props-id",
}
}
<UnitTitleSlot
courseId="test-course-id"
unitId="test-props-id"
/>
</div>
<h2
Expand Down
10 changes: 2 additions & 8 deletions src/courseware/course/sequence/Unit/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@ import { useIntl } from '@edx/frontend-platform/i18n';
import { useModel } from '@src/generic/model-store';
import { usePluginsCallback } from '@src/generic/plugin-store';

import { PluginSlot } from '@openedx/frontend-plugin-framework';
import BookmarkButton from '../../bookmark/BookmarkButton';
import messages from '../messages';
import ContentIFrame from './ContentIFrame';
import UnitSuspense from './UnitSuspense';
import { modelKeys, views } from './constants';
import { useExamAccess, useShouldDisplayHonorCode } from './hooks';
import { getIFrameUrl } from './urls';
import UnitTitleSlot from '../../../../plugin-slots/UnitTitleSlot';

const Unit = ({
courseId,
Expand Down Expand Up @@ -43,13 +43,7 @@ const Unit = ({
<div className="unit">
<div className="mb-0">
<h3 className="h3">{unit.title}</h3>
<PluginSlot
id="unit_title_plugin"
pluginProps={{
courseId,
unitId: id,
}}
/>
<UnitTitleSlot courseId={courseId} unitId={id} />
</div>
<h2 className="sr-only">{formatMessage(messages.headerPlaceholder)}</h2>
<BookmarkButton
Expand Down
6 changes: 3 additions & 3 deletions src/generic/CourseAccessErrorPage.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React, { useEffect } from 'react';
import { LearningHeader as Header } from '@edx/frontend-component-header';
import Footer from '@edx/frontend-component-footer';
import { useParams, Navigate } from 'react-router-dom';
import { useDispatch, useSelector } from 'react-redux';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import FooterSlot from '@openedx/frontend-slot-footer';
import useActiveEnterpriseAlert from '../alerts/active-enteprise-alert';
import { AlertList } from './user-messages';
import { fetchDiscussionTab } from '../course-home/data/thunks';
Expand Down Expand Up @@ -32,7 +32,7 @@ const CourseAccessErrorPage = ({ intl }) => {
<PageLoading
srMessage={intl.formatMessage(messages.loading)}
/>
<Footer />
<FooterSlot />
</>
);
}
Expand All @@ -51,7 +51,7 @@ const CourseAccessErrorPage = ({ intl }) => {
}}
/>
</main>
<Footer />
<FooterSlot />
</>
);
};
Expand Down
50 changes: 50 additions & 0 deletions src/plugin-slots/FooterSlot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Footer Slot

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a note for the purpose of transparency: we've decided to attempt to move the Footer Slot to frontend-component-footer itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


### Slot ID: `footer_slot`

## Description

This slot is used to replace/modify/hide the footer.

The implementation of the `FooterSlot` component lives in [the `frontend-slot-footer` repository](https://github.com/openedx/frontend-slot-footer/).

## Example

The following `env.config.jsx` will replace the default footer.

![Screenshot of Default Footer](./images/default_footer.png)

with a simple custom footer

![Screenshot of Custom Footer](./images/custom_footer.png)

```jsx
import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';

const config = {
pluginSlots: {
footer_slot: {
plugins: [
{
// Hide the default footer
op: PLUGIN_OPERATIONS.Hide,
widgetId: 'default_contents',
},
{
// Insert a custom footer
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'custom_footer',
type: DIRECT_PLUGIN,
RenderWidget: () => (
<h1 style={{textAlign: 'center'}}>🦶</h1>
),
},
},
]
}
},
}

export default config;
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions src/plugin-slots/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# `frontend-app-learning` Plugin Slots

* [`footer_slot`](./FooterSlot/)
* [`sequence_container_slot`](./SequenceContainerSlot/)
* [`unit_title_slot`](./UnitTitleSlot/)
45 changes: 45 additions & 0 deletions src/plugin-slots/SequenceContainerSlot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Sequence Container Slot

### Slot ID: `sequence_container_slot`
### Props:
* `courseId`
* `unitId`

## Description

This slot is used for adding content after the Sequence content section.
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MaxFrank13 I'm not 💯 on the wording I have here, if you have any suggestions that'd be wonderful!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As someone who doesn't develop much in this MFE, this makes sense to me — which I think is an indication that it's written well 👍


## Example

The following `env.config.jsx` will render the `course_id` and `unit_id` of the course as `<p>` elements in a `<div>`.

![Screenshot of Content added after the Sequence Container](./images/post_sequence_container.png)

```js
import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';

const config = {
pluginSlots: {
sequence_container_slot: {
plugins: [
{
// Insert custom content after sequence content
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'custom_sequence_container_content',
type: DIRECT_PLUGIN,
RenderWidget: ({courseId, unitId}) => (
<div>
<p>📚: {courseId}</p>
<p>📙: {unitId}</p>
</div>
),
},
},
]
}
},
}

export default config;
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions src/plugin-slots/SequenceContainerSlot/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import PropTypes from 'prop-types';
import { PluginSlot } from '@openedx/frontend-plugin-framework';

const SequenceContainerSlot = ({ courseId, unitId }) => (
<PluginSlot
id="sequence_container_slot"
pluginProps={{
courseId,
unitId,
}}
/>
);

SequenceContainerSlot.propTypes = {
courseId: PropTypes.string.isRequired,
unitId: PropTypes.string,
};

SequenceContainerSlot.defaultProps = {
unitId: null,
};

export default SequenceContainerSlot;
45 changes: 45 additions & 0 deletions src/plugin-slots/UnitTitleSlot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Unit Title Slot

### Slot ID: `unit_title_slot`
### Props:
* `courseId`
* `unitId`

## Description

This slot is used for adding content after the Unit title.

## Example

The following `env.config.jsx` will render the `course_id` and `unit_id` of the course as `<p>` elements.

![Screenshot of Content added after the Unit Title](./images/post_unit_title.png)

```js
import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';

const config = {
pluginSlots: {
unit_title_slot: {
plugins: [
{
// Insert custom content after unit title
op: PLUGIN_OPERATIONS.Insert,
widget: {
id: 'custom_unit_title_content',
type: DIRECT_PLUGIN,
RenderWidget: ({courseId, unitId}) => (
<>
<p>📚: {courseId}</p>
<p>📙: {unitId}</p>
</>
),
},
},
]
}
},
}

export default config;
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions src/plugin-slots/UnitTitleSlot/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import PropTypes from 'prop-types';
import { PluginSlot } from '@openedx/frontend-plugin-framework';

const UnitTitleSlot = ({ courseId, unitId }) => (
<PluginSlot
id="unit_title_slot"
pluginProps={{
courseId,
unitId,
}}
/>
);

UnitTitleSlot.propTypes = {
courseId: PropTypes.string.isRequired,
unitId: PropTypes.string.isRequired,
};

export default UnitTitleSlot;
4 changes: 2 additions & 2 deletions src/tab-page/TabPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { useDispatch, useSelector } from 'react-redux';
import { Navigate } from 'react-router-dom';

import Footer from '@edx/frontend-component-footer';
import { Toast } from '@openedx/paragon';
import { LearningHeader as Header } from '@edx/frontend-component-header';
import FooterSlot from '@openedx/frontend-slot-footer';
import PageLoading from '../generic/PageLoading';
import { getAccessDeniedRedirectUrl } from '../shared/access';
import { useModel } from '../generic/model-store';
Expand Down Expand Up @@ -80,7 +80,7 @@ const TabPage = ({ intl, ...props }) => {
{intl.formatMessage(messages.failure)}
</p>
)}
<Footer />
<FooterSlot />
</>
);
};
Expand Down
Loading