diff --git a/src/course-home/dates-tab/DatesTab.jsx b/src/course-home/dates-tab/DatesTab.jsx
index 65fed3dfdd..c7d3fcbe55 100644
--- a/src/course-home/dates-tab/DatesTab.jsx
+++ b/src/course-home/dates-tab/DatesTab.jsx
@@ -11,7 +11,7 @@ import { useModel } from '../../generic/model-store';
import SuggestedScheduleHeader from '../suggested-schedule-messaging/SuggestedScheduleHeader';
import ShiftDatesAlert from '../suggested-schedule-messaging/ShiftDatesAlert';
-import UpgradeToCompleteAlert from '../suggested-schedule-messaging/UpgradeToCompleteAlert';
+import { BannerDatesUpgradeSlot } from '../../plugin-slots/BannerDatesUpgradeSlot';
import UpgradeToShiftDatesAlert from '../suggested-schedule-messaging/UpgradeToShiftDatesAlert';
const DatesTab = () => {
@@ -51,7 +51,7 @@ const DatesTab = () => {
<>
-
+
>
)}
diff --git a/src/course-home/dates-tab/DatesTab.test.jsx b/src/course-home/dates-tab/DatesTab.test.jsx
index 17dcd34ad4..d56c4c0d10 100644
--- a/src/course-home/dates-tab/DatesTab.test.jsx
+++ b/src/course-home/dates-tab/DatesTab.test.jsx
@@ -171,9 +171,9 @@ describe('DatesTab', () => {
await waitFor(() => expect(screen.getByText('We’ve built a suggested schedule to help you stay on track. But don’t worry—it’s flexible so you can learn at your own pace.')).toBeInTheDocument());
});
- it('renders UpgradeToCompleteAlert', async () => {
+ it('does not render UpgradeToCompleteAlert when contentTypeGatingEnabled is false', async () => {
datesTabData.datesBannerInfo = {
- contentTypeGatingEnabled: true,
+ contentTypeGatingEnabled: false,
missedDeadlines: false,
missedGatedContent: false,
verifiedUpgradeLink: 'http://localhost:18130/basket/add/?sku=8CF08E5',
@@ -182,8 +182,8 @@ describe('DatesTab', () => {
axiosMock.onGet(datesUrl).reply(200, datesTabData);
render(component);
- await waitFor(() => expect(screen.getByText('You are auditing this course, which means that you are unable to participate in graded assignments. To complete graded assignments as part of this course, you can upgrade today.')).toBeInTheDocument());
- expect(screen.getByRole('button', { name: 'Upgrade now' })).toBeInTheDocument();
+ expect(screen.queryByText('You are auditing this course, which means that you are unable to participate in graded assignments. To complete graded assignments as part of this course, you can upgrade today.')).not.toBeInTheDocument();
+ expect(screen.queryByRole('button', { name: 'Upgrade now' })).not.toBeInTheDocument();
});
it('renders UpgradeToShiftDatesAlert', async () => {
@@ -254,32 +254,6 @@ describe('DatesTab', () => {
expect(screen.queryByRole('button', { name: 'Shift due dates' })).not.toBeInTheDocument();
});
- it('sends analytics event onClick of upgrade button in UpgradeToCompleteAlert', async () => {
- sendTrackEvent.mockClear();
- datesTabData.datesBannerInfo = {
- contentTypeGatingEnabled: true,
- missedDeadlines: false,
- missedGatedContent: false,
- verifiedUpgradeLink: 'http://localhost:18130/basket/add/?sku=8CF08E5',
- };
-
- axiosMock.onGet(datesUrl).reply(200, datesTabData);
- render(component);
-
- const upgradeButton = await waitFor(() => screen.getByRole('button', { name: 'Upgrade now' }));
- fireEvent.click(upgradeButton);
-
- expect(sendTrackEvent).toHaveBeenCalledTimes(1);
- expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.ecommerce.upsell_links_clicked', {
- org_key: 'edX',
- courserun_key: courseId,
- linkCategory: 'personalized_learner_schedules',
- linkName: 'dates_upgrade',
- linkType: 'button',
- pageName: 'dates_tab',
- });
- });
-
it('sends analytics event onClick of upgrade button in UpgradeToShiftDatesAlert', async () => {
sendTrackEvent.mockClear();
datesTabData.datesBannerInfo = {
diff --git a/src/plugin-slots/BannerDatesUpgradeSlot/README.md b/src/plugin-slots/BannerDatesUpgradeSlot/README.md
new file mode 100644
index 0000000000..75e553ac59
--- /dev/null
+++ b/src/plugin-slots/BannerDatesUpgradeSlot/README.md
@@ -0,0 +1,59 @@
+# Banner Dates Upgrade Slot
+
+### Slot ID: `org.openedx.frontend.learning.banner_dates_upgrade.v1`
+
+## Description
+
+This slot is used for rendering upgrade messaging in the dates tab banner area.
+
+By default, the slot renders `UpgradeToCompleteAlert`. You can disable the default and fully replace it with a custom plugin experience using `keepDefault: false`.
+
+## Props
+
+- `courseId` - The course ID (string)
+- `logUpgradeLinkClick` - Callback used for upgrade-click analytics
+
+## Screenshots
+
+Default banner screenshot:
+
+
+
+
+
+## Example
+
+The following `env.config.jsx` example replaces the default banner experience with a custom plugin widget:
+
+```js
+import {
+ DIRECT_PLUGIN,
+ PLUGIN_OPERATIONS,
+} from '@openedx/frontend-plugin-framework';
+
+const config = {
+ pluginSlots: {
+ 'org.openedx.frontend.learning.banner_dates_upgrade.v1': {
+ keepDefault: false,
+ plugins: [
+ {
+ op: PLUGIN_OPERATIONS.Insert,
+ widget: {
+ id: 'org.openedx.frontend.learning.custom_banner_dates_upgrade_widget.v1',
+ type: DIRECT_PLUGIN,
+ priority: 50,
+ RenderWidget: (pluginProps) => (
+
+ Replacement Banner
+
+
+ ),
+ },
+ },
+ ],
+ },
+ },
+};
+
+export default config;
+```
diff --git a/src/plugin-slots/BannerDatesUpgradeSlot/images/update_banner_complete_alert.png b/src/plugin-slots/BannerDatesUpgradeSlot/images/update_banner_complete_alert.png
new file mode 100644
index 0000000000..870ecc9132
Binary files /dev/null and b/src/plugin-slots/BannerDatesUpgradeSlot/images/update_banner_complete_alert.png differ
diff --git a/src/plugin-slots/BannerDatesUpgradeSlot/index.tsx b/src/plugin-slots/BannerDatesUpgradeSlot/index.tsx
new file mode 100644
index 0000000000..43ac9724ef
--- /dev/null
+++ b/src/plugin-slots/BannerDatesUpgradeSlot/index.tsx
@@ -0,0 +1,24 @@
+import React from 'react';
+
+import { PluginSlot } from '@openedx/frontend-plugin-framework';
+import UpgradeToCompleteAlert from '../../course-home/suggested-schedule-messaging/UpgradeToCompleteAlert';
+
+export const BannerDatesUpgradeSlot = ({
+ courseId,
+ logUpgradeLinkClick,
+}: BannerDatesUpgradeSlotProps) => (
+
+
+
+);
+
+interface BannerDatesUpgradeSlotProps {
+ courseId: string;
+ logUpgradeLinkClick: () => void;
+}
diff --git a/src/plugin-slots/README.md b/src/plugin-slots/README.md
index 9bd3b3dd3b..4519860652 100644
--- a/src/plugin-slots/README.md
+++ b/src/plugin-slots/README.md
@@ -2,6 +2,7 @@
* [`org.openedx.frontend.layout.footer.v1`](./FooterSlot/)
* [`org.openedx.frontend.layout.header_learning.v1`](./HeaderSlot/)
+* [`org.openedx.frontend.learning.banner_dates_upgrade.v1`](./BannerDatesUpgradeSlot/)
* [`org.openedx.frontend.learning.content_iframe_loader.v1`](./ContentIFrameLoaderSlot/)
* [`org.openedx.frontend.learning.course_breadcrumbs.v1`](./CourseBreadcrumbsSlot/)
* [`org.openedx.frontend.learning.course_home_section_outline.v1`](./CourseHomeSectionOutlineSlot/)