Skip to content

Commit 8a6fa93

Browse files
authored
feat: add progress certificate status plugin slot (#1551)
1 parent dafdcad commit 8a6fa93

File tree

5 files changed

+100
-30
lines changed

5 files changed

+100
-30
lines changed

src/course-home/progress-tab/certificate-status/CertificateStatus.jsx

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
1-
import React, { useEffect } from 'react';
1+
import { useEffect } from 'react';
22
import { useDispatch, useSelector } from 'react-redux';
33
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
44
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
5-
import {
6-
FormattedDate, FormattedMessage, injectIntl, intlShape,
7-
} from '@edx/frontend-platform/i18n';
5+
import { FormattedDate, FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
86

97
import { Button, Card } from '@openedx/paragon';
108
import { getConfig } from '@edx/frontend-platform';
@@ -13,8 +11,10 @@ import { COURSE_EXIT_MODES, getCourseExitMode } from '../../../courseware/course
1311
import { DashboardLink, IdVerificationSupportLink, ProfileLink } from '../../../shared/links';
1412
import { requestCert } from '../../data/thunks';
1513
import messages from './messages';
14+
import ProgressCertificateStatusSlot from '../../../plugin-slots/ProgressCertificateStatusSlot';
1615

17-
const CertificateStatus = ({ intl }) => {
16+
const CertificateStatus = () => {
17+
const intl = useIntl();
1818
const {
1919
courseId,
2020
} = useSelector(state => state.courseHome);
@@ -215,7 +215,6 @@ const CertificateStatus = ({ intl }) => {
215215
});
216216
// eslint-disable-next-line react-hooks/exhaustive-deps
217217
}, []);
218-
219218
if (!certCase) {
220219
return null;
221220
}
@@ -243,32 +242,32 @@ const CertificateStatus = ({ intl }) => {
243242
return (
244243
<section data-testid="certificate-status-component" className="text-dark-700 mb-4">
245244
<Card className="bg-light-200 raised-card">
246-
<Card.Header title={header} />
247-
<Card.Section className="small text-gray-700">
248-
{body}
249-
</Card.Section>
250-
<Card.Footer>
251-
{buttonText && (buttonLocation || buttonAction) && (
252-
<Button
253-
variant="outline-brand"
254-
onClick={() => {
255-
logCertificateStatusButtonClicked(certStatus);
256-
if (buttonAction) { buttonAction(); }
257-
}}
258-
href={buttonLocation}
259-
block
260-
>
261-
{buttonText}
262-
</Button>
263-
)}
264-
</Card.Footer>
245+
<ProgressCertificateStatusSlot courseId={courseId}>
246+
<div id={`${certCase}_certificate_status`}>
247+
<Card.Header title={header} />
248+
<Card.Section className="small text-gray-700">
249+
{body}
250+
</Card.Section>
251+
<Card.Footer>
252+
{buttonText && (buttonLocation || buttonAction) && (
253+
<Button
254+
variant="outline-brand"
255+
onClick={() => {
256+
logCertificateStatusButtonClicked(certStatus);
257+
if (buttonAction) { buttonAction(); }
258+
}}
259+
href={buttonLocation}
260+
block
261+
>
262+
{buttonText}
263+
</Button>
264+
)}
265+
</Card.Footer>
266+
</div>
267+
</ProgressCertificateStatusSlot>
265268
</Card>
266269
</section>
267270
);
268271
};
269272

270-
CertificateStatus.propTypes = {
271-
intl: intlShape.isRequired,
272-
};
273-
274-
export default injectIntl(CertificateStatus);
273+
export default CertificateStatus;
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Unit Title Slot
2+
3+
### Slot ID: `progress_certificate_status_slot`
4+
### Props:
5+
* `courseId`
6+
7+
## Description
8+
9+
This slot is used for modify the content in the CertificateStatus in the progress page for specific enrollment tracks.
10+
11+
## Example
12+
13+
The following `env.config.jsx` will render the `RenderWidget.props.id` of the course as `<p>` element.
14+
15+
### Default content
16+
![Certificate Status slot with default content](./screenshot_default.png)
17+
18+
### Replaced with custom component
19+
![Default content id showing in trigger slot](./screenshot_custom.png)
20+
21+
```js
22+
import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework';
23+
24+
const modifyWidget = (widget) => {
25+
const { RenderWidget } = widget;
26+
if (RenderWidget.props.id.includes('upgrade')) {
27+
widget.RenderWidget = (
28+
<div className='m-3'>
29+
<h3>Upgrade certificate</h3>
30+
<p>{RenderWidget.props.id}</p>
31+
</div>
32+
)
33+
}
34+
35+
return widget;
36+
}
37+
38+
const config = {
39+
pluginSlots: {
40+
progress_certificate_status_slot: {
41+
keepDefault: true,
42+
plugins: [{
43+
op: PLUGIN_OPERATIONS.Modify,
44+
widgetId: 'default_contents',
45+
// Insert custom content top modify certificate status
46+
fn: modifyWidget,
47+
}],
48+
}
49+
},
50+
}
51+
52+
export default config;
53+
```
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import PropTypes from 'prop-types';
2+
import { PluginSlot } from '@openedx/frontend-plugin-framework';
3+
4+
const ProgressCertificateStatusSlot = ({ courseId, children }) => (
5+
<PluginSlot
6+
id="progress_certificate_status_slot"
7+
pluginProps={{ courseId }}
8+
>
9+
{children}
10+
</PluginSlot>
11+
);
12+
13+
ProgressCertificateStatusSlot.propTypes = {
14+
courseId: PropTypes.string.isRequired,
15+
children: PropTypes.node.isRequired,
16+
};
17+
18+
export default ProgressCertificateStatusSlot;
26.4 KB
Loading
58.2 KB
Loading

0 commit comments

Comments
 (0)