From d1dede568ef1f4f0f98e293efc928494205a7ef8 Mon Sep 17 00:00:00 2001 From: Javier Ontiveros Date: Wed, 4 Jun 2025 13:22:01 -0600 Subject: [PATCH] feat: hide sidebar on screen resize (#1720) Adds an event handler on the window resize to check if the sidebar isOpen and the size of the viewport is smaller than the sidebar display to hide the sidebar and prevent it from blocking the course view. --- .../course-outline/CourseOutlineTray.test.jsx | 15 ++++++++++ .../course-outline/{hooks.jsx => hooks.js} | 28 +++++++++++++++++-- 2 files changed, 40 insertions(+), 3 deletions(-) rename src/courseware/course/sidebar/sidebars/course-outline/{hooks.jsx => hooks.js} (84%) diff --git a/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.test.jsx b/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.test.jsx index 5fda3616..1f826a69 100644 --- a/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.test.jsx +++ b/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.test.jsx @@ -102,6 +102,21 @@ describe('', () => { expect(mockToggleSidebar).toHaveBeenCalledWith(null); }); + it('collapses sidebar correctly when screen is resized', async () => { + const mockToggleSidebar = jest.fn(); + await initTestStore(); + renderWithProvider({ toggleSidebar: mockToggleSidebar }); + + const collapseBtn = screen.getByRole('button', { name: messages.toggleCourseOutlineTrigger.defaultMessage }); + expect(collapseBtn).toBeInTheDocument(); + + // Simulate screen resize + window.innerWidth = 500; + window.dispatchEvent(new Event('resize')); + + expect(mockToggleSidebar).toHaveBeenCalledWith(null); + }); + it('navigates to section or sequence level correctly on click by back/section button', async () => { const user = userEvent.setup(); await initTestStore(); diff --git a/src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx b/src/courseware/course/sidebar/sidebars/course-outline/hooks.js similarity index 84% rename from src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx rename to src/courseware/course/sidebar/sidebars/course-outline/hooks.js index 8f15906f..6d4ea8ca 100644 --- a/src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx +++ b/src/courseware/course/sidebar/sidebars/course-outline/hooks.js @@ -1,7 +1,10 @@ -import { useContext, useEffect, useState } from 'react'; +import { + useContext, useEffect, useLayoutEffect, useState, +} from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { useParams } from 'react-router-dom'; import { sendTrackEvent, sendTrackingLogEvent } from '@edx/frontend-platform/analytics'; +import { breakpoints } from '@openedx/paragon'; import { useModel } from '@src/generic/model-store'; import { LOADED } from '@src/constants'; @@ -54,10 +57,14 @@ export const useCourseOutlineSidebar = () => { } = course.entranceExamData || {}; const isActiveEntranceExam = entranceExamEnabled && !entranceExamPassed; + const collapseSidebar = () => { + toggleSidebar(null); + window.sessionStorage.setItem('hideCourseOutlineSidebar', 'true'); + }; + const handleToggleCollapse = () => { if (currentSidebar === ID) { - toggleSidebar(null); - window.sessionStorage.setItem('hideCourseOutlineSidebar', 'true'); + collapseSidebar(); } else { toggleSidebar(ID); window.sessionStorage.removeItem('hideCourseOutlineSidebar'); @@ -107,6 +114,21 @@ export const useCourseOutlineSidebar = () => { } }, [courseId, isEnabledSidebar, courseOutlineShouldUpdate]); + // Collapse sidebar if screen resized to a width that displays the sidebar automatically + useLayoutEffect(() => { + const handleResize = () => { + // breakpoints.large.maxWidth is 1200px and currently the breakpoint for showing the sidebar + if (currentSidebar === ID && global.innerWidth < breakpoints.large.maxWidth) { + collapseSidebar(); + } + }; + + global.addEventListener('resize', handleResize); + return () => { + global.removeEventListener('resize', handleResize); + }; + }, [isOpen]); + return { courseId, unitId,