![{intl.formatMessage(messages['learn.lockPaywall.example.alt'])}]()
diff --git a/src/courseware/course/sequence/lock-paywall/LockPaywall.test.jsx b/src/courseware/course/sequence/lock-paywall/LockPaywall.test.jsx
index 60ac4286..c37c6b4a 100644
--- a/src/courseware/course/sequence/lock-paywall/LockPaywall.test.jsx
+++ b/src/courseware/course/sequence/lock-paywall/LockPaywall.test.jsx
@@ -11,7 +11,7 @@ jest.mock('@edx/frontend-platform/analytics');
describe('Lock Paywall', () => {
let store;
- const mockData = { notificationTrayVisible: false };
+ const mockData = { currentSidebar: null };
beforeAll(async () => {
store = await initializeTestStore();
diff --git a/src/courseware/course/sequence/sequence-navigation/SequenceNavigation.jsx b/src/courseware/course/sequence/sequence-navigation/SequenceNavigation.jsx
index 81cd5a6b..7fc986cd 100644
--- a/src/courseware/course/sequence/sequence-navigation/SequenceNavigation.jsx
+++ b/src/courseware/course/sequence/sequence-navigation/SequenceNavigation.jsx
@@ -41,7 +41,7 @@ const SequenceNavigation = ({
sequence.gatedContent !== undefined && sequence.gatedContent.gated
) : undefined;
- const shouldDisplayNotificationTriggerInSequence = useWindowSize().width < breakpoints.small.minWidth;
+ const isSmallScreen = useWindowSize().width < breakpoints.small.minWidth;
const renderUnitButtons = () => {
if (isLocked) {
@@ -71,7 +71,7 @@ const SequenceNavigation = ({
onClick={previousHandler}
previousLink={previousLink}
isFirstUnit={isFirstUnit}
- buttonLabel={shouldDisplayNotificationTriggerInSequence ? null : intl.formatMessage(messages.previousButton)}
+ buttonLabel={isSmallScreen ? null : intl.formatMessage(messages.previousButton)}
/>
);
@@ -82,7 +82,7 @@ const SequenceNavigation = ({
if (isLastUnit && exitText) {
buttonText = exitText;
- } else if (!shouldDisplayNotificationTriggerInSequence) {
+ } else if (!isSmallScreen) {
buttonText = intl.formatMessage(messages.nextButton);
}
return navigationDisabledNextSequence || (
@@ -101,7 +101,7 @@ const SequenceNavigation = ({
};
return sequenceStatus === LOADED ? (
-
) : null}
{showTitleBar && (
- <>
-
-
{title}
- {shouldDisplayFullScreen
- ? null
- : (
-
- toggleSidebar(null)}
- variant="primary"
- alt={intl.formatMessage(messages.closeNotificationTrigger)}
- />
-
- )}
-
- >
+
+
{title}
+ {shouldDisplayFullScreen
+ ? null
+ : (
+
+ toggleSidebar(null)}
+ variant="primary"
+ alt={intl.formatMessage(messages.closeSidebarTrigger)}
+ />
+
+ )}
+
)}
{children}
diff --git a/src/courseware/course/sidebar/common/SidebarBase.test.jsx b/src/courseware/course/sidebar/common/SidebarBase.test.jsx
new file mode 100644
index 00000000..7c3b4b77
--- /dev/null
+++ b/src/courseware/course/sidebar/common/SidebarBase.test.jsx
@@ -0,0 +1,158 @@
+import React from 'react';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+import { render, screen, fireEvent } from '@testing-library/react';
+import SidebarContext from '../SidebarContext';
+import SidebarBase from './SidebarBase';
+
+const mockToggleSidebar = jest.fn();
+
+const defaultContextValue = {
+ toggleSidebar: mockToggleSidebar,
+ shouldDisplayFullScreen: false,
+ currentSidebar: 'TEST_SIDEBAR',
+};
+
+const defaultProps = {
+ title: 'Test Sidebar Title',
+ ariaLabel: 'Test Sidebar',
+ sidebarId: 'TEST_SIDEBAR',
+ className: '',
+ children:
Sidebar child content
,
+};
+
+function renderSidebarBase(props = {}, contextValue = {}) {
+ const mergedContext = { ...defaultContextValue, ...contextValue };
+ return render(
+
+
+
+
+ ,
+ );
+}
+
+describe('SidebarBase', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('renders children when currentSidebar matches sidebarId', () => {
+ renderSidebarBase();
+
+ expect(screen.getByText('Sidebar child content')).toBeInTheDocument();
+ });
+
+ it('renders the sidebar section with correct data-testid', () => {
+ const { container } = renderSidebarBase();
+
+ expect(container.querySelector('[data-testid="sidebar-TEST_SIDEBAR"]')).toBeInTheDocument();
+ });
+
+ it('adds d-none class when currentSidebar does not match sidebarId', () => {
+ const { container } = renderSidebarBase({}, { currentSidebar: 'OTHER_SIDEBAR' });
+ const section = container.querySelector('[data-testid="sidebar-TEST_SIDEBAR"]');
+
+ expect(section).toHaveClass('d-none');
+ });
+
+ it('does not add d-none class when currentSidebar matches sidebarId', () => {
+ const { container } = renderSidebarBase();
+ const section = container.querySelector('[data-testid="sidebar-TEST_SIDEBAR"]');
+
+ expect(section).not.toHaveClass('d-none');
+ });
+
+ describe('desktop mode (shouldDisplayFullScreen=false)', () => {
+ it('renders the sidebar title', () => {
+ renderSidebarBase();
+
+ expect(screen.getByText('Test Sidebar Title')).toBeInTheDocument();
+ });
+
+ it('renders the close button', () => {
+ renderSidebarBase();
+
+ expect(screen.getByRole('button', { name: /close sidebar/i })).toBeInTheDocument();
+ });
+
+ it('calls toggleSidebar(null) when close button is clicked', () => {
+ renderSidebarBase();
+ fireEvent.click(screen.getByRole('button', { name: /close sidebar/i }));
+
+ expect(mockToggleSidebar).toHaveBeenCalledWith(null);
+ });
+
+ it('does not render the back-to-course button', () => {
+ renderSidebarBase();
+
+ expect(screen.queryByText(/back to course/i)).not.toBeInTheDocument();
+ });
+ });
+
+ describe('mobile mode (shouldDisplayFullScreen=true)', () => {
+ it('renders the "Back to course" back-navigation button', () => {
+ renderSidebarBase({}, { shouldDisplayFullScreen: true });
+
+ expect(screen.getByText(/back to course/i)).toBeInTheDocument();
+ });
+
+ it('calls toggleSidebar(null) when back-navigation button is clicked', () => {
+ renderSidebarBase({}, { shouldDisplayFullScreen: true });
+ fireEvent.click(screen.getByRole('button', { name: /back to course/i }));
+
+ expect(mockToggleSidebar).toHaveBeenCalledWith(null);
+ });
+
+ it('does not render the desktop close button', () => {
+ renderSidebarBase({}, { shouldDisplayFullScreen: true });
+
+ expect(screen.queryByRole('button', { name: /close sidebar/i })).not.toBeInTheDocument();
+ });
+ });
+
+ describe('showTitleBar prop', () => {
+ it('hides title and close button when showTitleBar=false', () => {
+ renderSidebarBase({ showTitleBar: false });
+
+ expect(screen.queryByText('Test Sidebar Title')).not.toBeInTheDocument();
+ expect(screen.queryByRole('button', { name: /close sidebar/i })).not.toBeInTheDocument();
+ });
+
+ it('shows title bar by default when showTitleBar is not specified', () => {
+ renderSidebarBase();
+
+ expect(screen.getByText('Test Sidebar Title')).toBeInTheDocument();
+ });
+ });
+
+ describe('postMessage event handling', () => {
+ it('calls toggleSidebar(null) when receiving learning.events.sidebar.close message', () => {
+ renderSidebarBase();
+ fireEvent(
+ window,
+ new MessageEvent('message', { data: { type: 'learning.events.sidebar.close' } }),
+ );
+
+ expect(mockToggleSidebar).toHaveBeenCalledWith(null);
+ });
+
+ it('does not call toggleSidebar for unrelated message types', () => {
+ renderSidebarBase();
+ fireEvent(
+ window,
+ new MessageEvent('message', { data: { type: 'some.other.event' } }),
+ );
+
+ expect(mockToggleSidebar).not.toHaveBeenCalled();
+ });
+ });
+
+ describe('width prop', () => {
+ it('applies custom width to the section element in desktop mode', () => {
+ const { container } = renderSidebarBase({ width: '50rem' });
+ const section = container.querySelector('[data-testid="sidebar-TEST_SIDEBAR"]');
+
+ expect(section.style.width).toBe('50rem');
+ });
+ });
+});
diff --git a/src/courseware/course/sidebar/common/TriggerBase.jsx b/src/courseware/course/sidebar/common/TriggerBase.jsx
index d40303d0..753d44ab 100644
--- a/src/courseware/course/sidebar/common/TriggerBase.jsx
+++ b/src/courseware/course/sidebar/common/TriggerBase.jsx
@@ -7,7 +7,7 @@ const SidebarTriggerBase = ({
children,
}) => (