Skip to content

Commit

Permalink
feat!: organize plugin slots as components, add footer slot
Browse files Browse the repository at this point in the history
BREAKING CHANGE: slot ids have been changed for consistency
* `sequence_container_plugin` -> `sequence_container_slot`
* `unit_title_plugin` -> `unit_title_slot`
  • Loading branch information
brian-smith-tcril committed May 7, 2024
1 parent 1c3610e commit 61d479b
Show file tree
Hide file tree
Showing 17 changed files with 201 additions and 29 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
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,7 +9,7 @@ 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 SequenceContainerSlot from '../../../plugin-slots/SequenceContainerSlot';

Check failure on line 12 in src/courseware/course/sequence/Sequence.jsx

View workflow job for this annotation

GitHub Actions / tests

`../../../plugin-slots/SequenceContainerSlot` import should occur after import of `@src/alerts/sequence-alerts/hooks`

import PageLoading from '@src/generic/PageLoading';
import { useModel } from '@src/generic/model-store';
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 '../plugin-slots/FooterSlot';
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
44 changes: 44 additions & 0 deletions src/plugin-slots/FooterSlot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Footer Slot

### Slot ID: `footer_slot`

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

```js
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.
10 changes: 10 additions & 0 deletions src/plugin-slots/FooterSlot/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { PluginSlot } from '@openedx/frontend-plugin-framework';
import Footer from '@edx/frontend-component-footer';

const FooterSlot = () => (
<PluginSlot id="footer_slot">
<Footer />
</PluginSlot>
);

export default FooterSlot;
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/)
41 changes: 41 additions & 0 deletions src/plugin-slots/SequenceContainerSlot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Sequence Container Slot

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

## Example

The following `env.config.jsx` will add content after the sequence container

![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;
41 changes: 41 additions & 0 deletions src/plugin-slots/UnitTitleSlot/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Unit Title Slot

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

## Example

The following `env.config.jsx` will add content after the unit title

![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 '../plugin-slots/FooterSlot';
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 61d479b

Please sign in to comment.