Skip to content

Commit

Permalink
feat!: organize plugin slots as components, add footer slot (openedx#…
Browse files Browse the repository at this point in the history
…1408)

BREAKING CHANGE: slot ids have been changed for consistency
* `sequence_container_plugin` -> `sequence_container_slot`
* `unit_title_plugin` -> `unit_title_slot`

Co-authored-by: Brian Smith <[email protected]>
  • Loading branch information
2 people authored and bra-i-am committed Jul 23, 2024
1 parent 258a53c commit 004a9ef
Show file tree
Hide file tree
Showing 18 changed files with 664 additions and 49 deletions.
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
478 changes: 458 additions & 20 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"@fortawesome/free-solid-svg-icons": "5.15.4",
"@fortawesome/react-fontawesome": "0.2.0",
"@openedx/frontend-plugin-framework": "1.0.2",
"@openedx/frontend-slot-footer": "^1.0.2",
"@openedx/paragon": "23.0.0-alpha.1",
"@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 { getCoursewareOutlineSidebarSettings } from '../../data/selectors';
import CourseLicense from '../course-license';
Expand Down Expand Up @@ -202,13 +202,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

### 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.

## 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

0 comments on commit 004a9ef

Please sign in to comment.