diff --git a/src/containers/CourseCard/components/CourseCardActions/index.jsx b/src/containers/CourseCard/components/CourseCardActions/index.jsx index 51eeafe6..5f4a34fa 100644 --- a/src/containers/CourseCard/components/CourseCardActions/index.jsx +++ b/src/containers/CourseCard/components/CourseCardActions/index.jsx @@ -5,7 +5,7 @@ import { ActionRow } from '@openedx/paragon'; import { reduxHooks } from 'hooks'; -import UpgradeButton from './UpgradeButton'; +import CourseCardActionSlot from 'plugin-slots/CourseCardActionSlot'; import SelectSessionButton from './SelectSessionButton'; import BeginCourseButton from './BeginCourseButton'; import ResumeButton from './ResumeButton'; @@ -14,15 +14,13 @@ import ViewCourseButton from './ViewCourseButton'; export const CourseCardActions = ({ cardId }) => { const { isEntitlement, isFulfilled } = reduxHooks.useCardEntitlementData(cardId); const { - isVerified, hasStarted, - isExecEd2UCourse, } = reduxHooks.useCardEnrollmentData(cardId); const { isArchived } = reduxHooks.useCardCourseRunData(cardId); return ( - {!(isEntitlement || isVerified || isExecEd2UCourse) && } + {isEntitlement && (isFulfilled ? : diff --git a/src/containers/CourseCard/components/CourseCardActions/index.test.jsx b/src/containers/CourseCard/components/CourseCardActions/index.test.jsx index 58809da8..5819a9b6 100644 --- a/src/containers/CourseCard/components/CourseCardActions/index.test.jsx +++ b/src/containers/CourseCard/components/CourseCardActions/index.test.jsx @@ -2,6 +2,7 @@ import { shallow } from '@edx/react-unit-test-utils'; import { reduxHooks } from 'hooks'; +import CourseCardActionSlot from 'plugin-slots/CourseCardActionSlot'; import UpgradeButton from './UpgradeButton'; import SelectSessionButton from './SelectSessionButton'; import BeginCourseButton from './BeginCourseButton'; @@ -19,6 +20,7 @@ jest.mock('hooks', () => ({ }, })); +jest.mock('plugin-slots/CourseCardActionSlot', () => 'CustomActionButton'); jest.mock('./UpgradeButton', () => 'UpgradeButton'); jest.mock('./SelectSessionButton', () => 'SelectSessionButton'); jest.mock('./ViewCourseButton', () => 'ViewCourseButton'); @@ -88,18 +90,18 @@ describe('CourseCardActions', () => { expect(el.instance.findByType(UpgradeButton).length).toEqual(0); }); }); - describe('not entielement, verified, or exec ed', () => { + describe('not entitlement, verified, or exec ed', () => { it('renders UpgradeButton and ViewCourseButton for archived courses', () => { mockHooks({ isArchived: true }); render(); - expect(el.instance.findByType(UpgradeButton)[0].props.cardId).toEqual(cardId); + expect(el.instance.findByType(CourseCardActionSlot)[0].props.cardId).toEqual(cardId); expect(el.instance.findByType(ViewCourseButton)[0].props.cardId).toEqual(cardId); }); describe('unstarted courses', () => { it('renders UpgradeButton and BeginCourseButton', () => { mockHooks(); render(); - expect(el.instance.findByType(UpgradeButton)[0].props.cardId).toEqual(cardId); + expect(el.instance.findByType(CourseCardActionSlot)[0].props.cardId).toEqual(cardId); expect(el.instance.findByType(BeginCourseButton)[0].props.cardId).toEqual(cardId); }); }); @@ -107,7 +109,7 @@ describe('CourseCardActions', () => { it('renders UpgradeButton and ResumeButton', () => { mockHooks({ hasStarted: true }); render(); - expect(el.instance.findByType(UpgradeButton)[0].props.cardId).toEqual(cardId); + expect(el.instance.findByType(CourseCardActionSlot)[0].props.cardId).toEqual(cardId); expect(el.instance.findByType(ResumeButton)[0].props.cardId).toEqual(cardId); }); }); diff --git a/src/plugin-slots/CourseCardActionSlot/README.md b/src/plugin-slots/CourseCardActionSlot/README.md new file mode 100644 index 00000000..34470639 --- /dev/null +++ b/src/plugin-slots/CourseCardActionSlot/README.md @@ -0,0 +1,64 @@ +# Course Card Action Slot + +### Slot ID: `course_card_action_slot` +### Props: +* `cardId` + +## Description + +This slot is used for adding content in the Action buttons section of each Course Card. + +## Example + +The following `env.config.jsx` will render the `cardId` of the course as `

` elements in a `

`. + +![Screenshot of Content added after the Sequence Container](./images/post_course_card_action.png) + +```js +import { DIRECT_PLUGIN, PLUGIN_OPERATIONS } from '@openedx/frontend-plugin-framework'; +import ActionButton from 'containers/CourseCard/components/CourseCardActions/ActionButton'; + +const config = { + pluginSlots: { + course_card_action_slot: { + keepDefault: false, + plugins: [ + { + // Insert Custom Button in Course Card + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'custom_course_card_action', + priority: 60, + type: DIRECT_PLUGIN, + RenderWidget: ({cardId}) => ( + + Custom Button + + ), + }, + }, + { + // Insert Another Button in Course Card + op: PLUGIN_OPERATIONS.Insert, + widget: { + id: 'another_custom_course_card_action', + priority: 70, + type: DIRECT_PLUGIN, + RenderWidget: ({cardId}) => ( + + 📚: {cardId} + + ), + }, + }, + ] + } + }, +} + +export default config; +``` diff --git a/src/plugin-slots/CourseCardActionSlot/images/post_course_card_action.png b/src/plugin-slots/CourseCardActionSlot/images/post_course_card_action.png new file mode 100644 index 00000000..e21a0084 Binary files /dev/null and b/src/plugin-slots/CourseCardActionSlot/images/post_course_card_action.png differ diff --git a/src/plugin-slots/CourseCardActionSlot/index.jsx b/src/plugin-slots/CourseCardActionSlot/index.jsx new file mode 100644 index 00000000..6747ee18 --- /dev/null +++ b/src/plugin-slots/CourseCardActionSlot/index.jsx @@ -0,0 +1,31 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { PluginSlot } from '@openedx/frontend-plugin-framework'; + +import { reduxHooks } from 'hooks'; +import UpgradeButton from 'containers/CourseCard/components/CourseCardActions/UpgradeButton'; + +const CourseCardActionSlot = ({ cardId }) => { + const { isEntitlement } = reduxHooks.useCardEntitlementData(cardId); + const { + isVerified, + isExecEd2UCourse, + } = reduxHooks.useCardEnrollmentData(cardId); + + return ( + + {!(isEntitlement || isVerified || isExecEd2UCourse) && } + + ); +}; + +CourseCardActionSlot.propTypes = { + cardId: PropTypes.string.isRequired, +}; + +export default CourseCardActionSlot; diff --git a/src/plugin-slots/README.md b/src/plugin-slots/README.md index 30a6a047..b5b65570 100644 --- a/src/plugin-slots/README.md +++ b/src/plugin-slots/README.md @@ -1,3 +1,4 @@ # `frontend-app-learner-dashboard` Plugin Slots +* [`course_card_action_slot`](./CourseCardActionSlot/) * [`footer_slot`](./FooterSlot/)