From 74f739540cfba6585120e261158058d2a626f2b9 Mon Sep 17 00:00:00 2001 From: javier ontiveros Date: Thu, 29 May 2025 12:22:16 -0600 Subject: [PATCH 1/8] feat: hide sidebar on screen resize --- .../sidebar/sidebars/course-outline/hooks.jsx | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx b/src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx index 8f15906fe8..90fa9cc7fc 100644 --- a/src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx +++ b/src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx @@ -1,7 +1,9 @@ -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 { debounce } from 'lodash'; +import { breakpoints } from '@openedx/paragon'; import { useModel } from '@src/generic/model-store'; import { LOADED } from '@src/constants'; @@ -18,6 +20,8 @@ import { } from '@src/courseware/data/selectors'; import { ID } from './constants'; +const DEBOUNCE_WAIT = 100; // ms + // eslint-disable-next-line import/prefer-default-export export const useCourseOutlineSidebar = () => { const dispatch = useDispatch(); @@ -107,6 +111,24 @@ 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 (global.innerWidth < breakpoints.large.maxWidth) { + if (isOpen) { + handleToggleCollapse(); + } + } + }; + const debounceHandleResize = debounce(handleResize, DEBOUNCE_WAIT, { leading: true }); + + global.addEventListener('resize', debounceHandleResize); + return () => { + global.removeEventListener('resize', debounceHandleResize); + }; + }, []); + return { courseId, unitId, From 3f467852cc6e573215ea73d21cb8f953c2d8504a Mon Sep 17 00:00:00 2001 From: javier ontiveros Date: Thu, 29 May 2025 12:25:05 -0600 Subject: [PATCH 2/8] chore: removed extra condition --- .../course/sidebar/sidebars/course-outline/hooks.jsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx b/src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx index 90fa9cc7fc..f65e425480 100644 --- a/src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx +++ b/src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx @@ -116,9 +116,7 @@ export const useCourseOutlineSidebar = () => { const handleResize = () => { // breakpoints.large.maxWidth is 1200px and currently the breakpoint for showing the sidebar if (global.innerWidth < breakpoints.large.maxWidth) { - if (isOpen) { - handleToggleCollapse(); - } + handleToggleCollapse(); } }; const debounceHandleResize = debounce(handleResize, DEBOUNCE_WAIT, { leading: true }); From 5f02bc66a49aa9cf0bbc6a96b8f9b65efb97a228 Mon Sep 17 00:00:00 2001 From: javier ontiveros Date: Thu, 29 May 2025 12:55:46 -0600 Subject: [PATCH 3/8] chore: improve logic and remove debounce to prevent flashing --- .../sidebar/sidebars/course-outline/hooks.jsx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx b/src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx index f65e425480..88adc18668 100644 --- a/src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx +++ b/src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx @@ -20,7 +20,7 @@ import { } from '@src/courseware/data/selectors'; import { ID } from './constants'; -const DEBOUNCE_WAIT = 100; // ms +const DEBOUNCE_WAIT = 1; // ms // eslint-disable-next-line import/prefer-default-export export const useCourseOutlineSidebar = () => { @@ -115,15 +115,16 @@ export const useCourseOutlineSidebar = () => { useLayoutEffect(() => { const handleResize = () => { // breakpoints.large.maxWidth is 1200px and currently the breakpoint for showing the sidebar - if (global.innerWidth < breakpoints.large.maxWidth) { - handleToggleCollapse(); + if (isOpen && global.innerWidth < breakpoints.large.maxWidth) { + if (isOpen) { + handleToggleCollapse(); + } } }; - const debounceHandleResize = debounce(handleResize, DEBOUNCE_WAIT, { leading: true }); - global.addEventListener('resize', debounceHandleResize); + global.addEventListener('resize', handleResize); return () => { - global.removeEventListener('resize', debounceHandleResize); + global.removeEventListener('resize', handleResize); }; }, []); From 529a46a6a5cea03f740071c3f00b9bb0d8b816b3 Mon Sep 17 00:00:00 2001 From: javier ontiveros Date: Thu, 29 May 2025 13:07:50 -0600 Subject: [PATCH 4/8] chore: fixed linting --- .../course/sidebar/sidebars/course-outline/hooks.jsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx b/src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx index 88adc18668..e7e8ed3a4a 100644 --- a/src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx +++ b/src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx @@ -1,8 +1,9 @@ -import { useContext, useEffect, useLayoutEffect, 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 { debounce } from 'lodash'; import { breakpoints } from '@openedx/paragon'; import { useModel } from '@src/generic/model-store'; @@ -20,8 +21,6 @@ import { } from '@src/courseware/data/selectors'; import { ID } from './constants'; -const DEBOUNCE_WAIT = 1; // ms - // eslint-disable-next-line import/prefer-default-export export const useCourseOutlineSidebar = () => { const dispatch = useDispatch(); From 09faf81e0fd27cafcc636414b35482fb079b93ca Mon Sep 17 00:00:00 2001 From: javier ontiveros Date: Thu, 29 May 2025 13:15:50 -0600 Subject: [PATCH 5/8] chore: incorporated suggestions from copilot --- .../course/sidebar/sidebars/course-outline/hooks.jsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx b/src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx index e7e8ed3a4a..ced353d842 100644 --- a/src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx +++ b/src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx @@ -115,9 +115,7 @@ export const useCourseOutlineSidebar = () => { const handleResize = () => { // breakpoints.large.maxWidth is 1200px and currently the breakpoint for showing the sidebar if (isOpen && global.innerWidth < breakpoints.large.maxWidth) { - if (isOpen) { - handleToggleCollapse(); - } + handleToggleCollapse(); } }; @@ -125,7 +123,7 @@ export const useCourseOutlineSidebar = () => { return () => { global.removeEventListener('resize', handleResize); }; - }, []); + }, [isOpen]); return { courseId, From af416ddee45d3457adbd72fe7e522db0a7729a2d Mon Sep 17 00:00:00 2001 From: javier ontiveros Date: Fri, 30 May 2025 12:06:03 -0600 Subject: [PATCH 6/8] chore: update tests --- .../course-outline/CourseOutlineTray.test.jsx | 17 +++++++++++++++++ .../course-outline/{hooks.jsx => hooks.js} | 0 2 files changed, 17 insertions(+) rename src/courseware/course/sidebar/sidebars/course-outline/{hooks.jsx => hooks.js} (100%) 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 5fda3616a7..86c5a9eacf 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,23 @@ describe('', () => { expect(mockToggleSidebar).toHaveBeenCalledWith(null); }); + it('collapses sidebar correctly when screen is resized', async () => { + const user = userEvent.setup(); + 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 100% rename from src/courseware/course/sidebar/sidebars/course-outline/hooks.jsx rename to src/courseware/course/sidebar/sidebars/course-outline/hooks.js From fde163328ea0b39d6c982148eb138b22b20c4a79 Mon Sep 17 00:00:00 2001 From: javier ontiveros Date: Fri, 30 May 2025 12:07:58 -0600 Subject: [PATCH 7/8] chore: fixed linting issues --- .../sidebars/course-outline/CourseOutlineTray.test.jsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 86c5a9eacf..1f826a690a 100644 --- a/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.test.jsx +++ b/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.test.jsx @@ -103,7 +103,6 @@ describe('', () => { }); it('collapses sidebar correctly when screen is resized', async () => { - const user = userEvent.setup(); const mockToggleSidebar = jest.fn(); await initTestStore(); renderWithProvider({ toggleSidebar: mockToggleSidebar }); @@ -116,8 +115,7 @@ describe('', () => { 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(); From b292e6c4c83e7e2afae9d777d743b07eb25b9da2 Mon Sep 17 00:00:00 2001 From: javier ontiveros Date: Wed, 4 Jun 2025 09:35:09 -0600 Subject: [PATCH 8/8] chore: udpated code to make sure sidebar is collapsed and not toggled --- .../course/sidebar/sidebars/course-outline/hooks.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/courseware/course/sidebar/sidebars/course-outline/hooks.js b/src/courseware/course/sidebar/sidebars/course-outline/hooks.js index ced353d842..6d4ea8ca35 100644 --- a/src/courseware/course/sidebar/sidebars/course-outline/hooks.js +++ b/src/courseware/course/sidebar/sidebars/course-outline/hooks.js @@ -57,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'); @@ -114,8 +118,8 @@ export const useCourseOutlineSidebar = () => { useLayoutEffect(() => { const handleResize = () => { // breakpoints.large.maxWidth is 1200px and currently the breakpoint for showing the sidebar - if (isOpen && global.innerWidth < breakpoints.large.maxWidth) { - handleToggleCollapse(); + if (currentSidebar === ID && global.innerWidth < breakpoints.large.maxWidth) { + collapseSidebar(); } };