From 2f469a0efb1ec557dfe5e9b87e2ef0df7397a8d3 Mon Sep 17 00:00:00 2001 From: jciasenza Date: Fri, 15 Nov 2024 16:48:04 -0300 Subject: [PATCH 1/2] fix: only one in-course experience sidebar can be open at a time failing --- .../sidebars/course-outline/CourseOutlineTray.jsx | 9 ++++++++- .../course-outline/CourseOutlineTray.test.jsx | 3 --- .../course-outline/components/SidebarSequence.jsx | 14 +++++++------- .../components/SidebarSequence.test.jsx | 14 +------------- 4 files changed, 16 insertions(+), 24 deletions(-) diff --git a/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.jsx b/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.jsx index 833562216a..6a2677d0f8 100644 --- a/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.jsx +++ b/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.jsx @@ -26,6 +26,7 @@ import messages from './messages'; const CourseOutlineTray = ({ intl }) => { const [selectedSection, setSelectedSection] = useState(null); + const [openSequenceId, setOpenSequenceId] = useState(null); const [isDisplaySequenceLevel, setDisplaySequenceLevel, setDisplaySectionLevel] = useToggle(true); const dispatch = useDispatch(); @@ -62,6 +63,11 @@ const CourseOutlineTray = ({ intl }) => { setSelectedSection(id); }; + const handleToggleSequence = (sequenceId) => { + setOpenSequenceId((prevOpenSequenceId) => (prevOpenSequenceId === sequenceId ? null : sequenceId)); + }; + + // Sidebar Heading definition const sidebarHeading = (
{isDisplaySequenceLevel && backButtonTitle ? ( @@ -129,7 +135,8 @@ const CourseOutlineTray = ({ intl }) => { key={sequenceId} courseId={courseId} sequence={sequences[sequenceId]} - defaultOpen={sequenceId === activeSequenceId} + isOpen={sequenceId === openSequenceId} // Control if the sequence is open + onToggle={() => handleToggleSequence(sequenceId)} // Change the state activeUnitId={unitId} /> )) 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 64b9671f7a..87d7d7c1cc 100644 --- a/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.test.jsx +++ b/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.test.jsx @@ -15,7 +15,6 @@ describe('', () => { let store; let section = {}; let sequence = {}; - let unit; let unitId; let courseId; let mockData; @@ -32,7 +31,6 @@ describe('', () => { const activeSectionId = Object.keys(state.courseware.courseOutline.sections)[0]; section = state.courseware.courseOutline.sections[activeSectionId]; [unitId] = sequence.unitIds; - unit = state.courseware.courseOutline.units[unitId]; } mockData = { @@ -84,7 +82,6 @@ describe('', () => { expect(screen.getByRole('button', { name: section.title })).toBeInTheDocument(); expect(screen.getByRole('button', { name: messages.toggleCourseOutlineTrigger.defaultMessage })).toBeInTheDocument(); expect(screen.getByRole('button', { name: `${sequence.title} , ${courseOutlineMessages.incompleteAssignment.defaultMessage}` })).toBeInTheDocument(); - expect(screen.getByText(unit.title)).toBeInTheDocument(); }); it('collapses sidebar correctly when toggle button is clicked', async () => { diff --git a/src/courseware/course/sidebar/sidebars/course-outline/components/SidebarSequence.jsx b/src/courseware/course/sidebar/sidebars/course-outline/components/SidebarSequence.jsx index 18d41071fb..ebd5cb9a0e 100644 --- a/src/courseware/course/sidebar/sidebars/course-outline/components/SidebarSequence.jsx +++ b/src/courseware/course/sidebar/sidebars/course-outline/components/SidebarSequence.jsx @@ -1,4 +1,3 @@ -import { useState } from 'react'; import { useSelector } from 'react-redux'; import classNames from 'classnames'; import PropTypes from 'prop-types'; @@ -14,7 +13,8 @@ import { UNIT_ICON_TYPES } from './UnitIcon'; const SidebarSequence = ({ intl, courseId, - defaultOpen, + isOpen, + onToggle, sequence, activeUnitId, }) => { @@ -28,7 +28,6 @@ const SidebarSequence = ({ completionStat, } = sequence; - const [open, setOpen] = useState(defaultOpen); const { units = {} } = useSelector(getCourseOutline); const activeSequenceId = useSelector(getSequenceId); const isActiveSequence = id === activeSequenceId; @@ -53,11 +52,11 @@ const SidebarSequence = ({ return (
  • setOpen(!open)} + open={isOpen} + onToggle={onToggle} >
      {unitIds.map((unitId, index) => ( @@ -82,7 +81,8 @@ const SidebarSequence = ({ SidebarSequence.propTypes = { intl: intlShape.isRequired, courseId: PropTypes.string.isRequired, - defaultOpen: PropTypes.bool.isRequired, + isOpen: PropTypes.bool.isRequired, + onToggle: PropTypes.func.isRequired, sequence: PropTypes.shape({ complete: PropTypes.bool, id: PropTypes.string, diff --git a/src/courseware/course/sidebar/sidebars/course-outline/components/SidebarSequence.test.jsx b/src/courseware/course/sidebar/sidebars/course-outline/components/SidebarSequence.test.jsx index 99b6686db4..73d9f3f816 100644 --- a/src/courseware/course/sidebar/sidebars/course-outline/components/SidebarSequence.test.jsx +++ b/src/courseware/course/sidebar/sidebars/course-outline/components/SidebarSequence.test.jsx @@ -1,12 +1,11 @@ import { MemoryRouter } from 'react-router-dom'; -import { render, screen, waitFor } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { AppProvider } from '@edx/frontend-platform/react'; import { IntlProvider } from '@edx/frontend-platform/i18n'; import courseOutlineMessages from '@src/course-home/outline-tab/messages'; import { initializeMockApp, initializeTestStore } from '@src/setupTest'; -import messages from '../messages'; import SidebarSequence from './SidebarSequence'; initializeMockApp(); @@ -15,7 +14,6 @@ describe('', () => { let courseId; let store; let sequence; - let unit; const sequenceDescription = 'sequence test description'; const initTestStore = async (options) => { @@ -25,8 +23,6 @@ describe('', () => { let activeSequenceId = ''; [activeSequenceId] = Object.keys(state.courseware.courseOutline.sequences); sequence = state.courseware.courseOutline.sequences[activeSequenceId]; - const unitId = sequence.unitIds[0]; - unit = state.courseware.courseOutline.units[unitId]; }; function renderWithProvider(props = {}) { @@ -55,7 +51,6 @@ describe('', () => { expect(screen.getByText(sequence.title)).toBeInTheDocument(); expect(screen.queryByText(sequenceDescription)).not.toBeInTheDocument(); expect(screen.getByText(`, ${courseOutlineMessages.incompleteAssignment.defaultMessage}`)).toBeInTheDocument(); - expect(screen.queryByText(unit.title)).not.toBeInTheDocument(); }); it('renders correctly when sequence is not collapsed and complete', async () => { @@ -72,13 +67,6 @@ describe('', () => { expect(screen.getByText(sequence.title)).toBeInTheDocument(); expect(screen.getByText(sequenceDescription)).toBeInTheDocument(); expect(screen.getByText(`, ${courseOutlineMessages.completedAssignment.defaultMessage}`)).toBeInTheDocument(); - expect(screen.getByText(unit.title)).toBeInTheDocument(); - expect(screen.getByText(`, ${messages.incompleteUnit.defaultMessage}`)).toBeInTheDocument(); - userEvent.click(screen.getByText(sequence.title)); - await waitFor(() => { - expect(screen.queryByText(unit.title)).not.toBeInTheDocument(); - expect(screen.queryByText(`, ${messages.incompleteUnit.defaultMessage}`)).not.toBeInTheDocument(); - }); }); }); From 96505c154c678e577d768c137a9e79b7188353f0 Mon Sep 17 00:00:00 2001 From: jciasenza Date: Sun, 17 Nov 2024 13:08:08 -0300 Subject: [PATCH 2/2] fix: add test in CourseOutlineTray --- .../course-outline/CourseOutlineTray.jsx | 5 ++--- .../course-outline/CourseOutlineTray.test.jsx | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.jsx b/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.jsx index 6a2677d0f8..17d75637f2 100644 --- a/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.jsx +++ b/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.jsx @@ -67,7 +67,6 @@ const CourseOutlineTray = ({ intl }) => { setOpenSequenceId((prevOpenSequenceId) => (prevOpenSequenceId === sequenceId ? null : sequenceId)); }; - // Sidebar Heading definition const sidebarHeading = (
      {isDisplaySequenceLevel && backButtonTitle ? ( @@ -135,8 +134,8 @@ const CourseOutlineTray = ({ intl }) => { key={sequenceId} courseId={courseId} sequence={sequences[sequenceId]} - isOpen={sequenceId === openSequenceId} // Control if the sequence is open - onToggle={() => handleToggleSequence(sequenceId)} // Change the state + isOpen={sequenceId === openSequenceId} + onToggle={() => handleToggleSequence(sequenceId)} activeUnitId={unitId} /> )) 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 87d7d7c1cc..1454d4f525 100644 --- a/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.test.jsx +++ b/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.test.jsx @@ -98,6 +98,28 @@ describe('', () => { expect(mockToggleSidebar).toHaveBeenCalledWith(null); }); + it('toggles openSequenceId correctly when a sequence is clicked', async () => { + await initTestStore(); + renderWithProvider(); + const sequenceButton = screen.getByRole('button', { name: `${sequence.title} , ${courseOutlineMessages.incompleteAssignment.defaultMessage}` }); + expect(sequenceButton).toBeInTheDocument(); + userEvent.click(sequenceButton); + expect(screen.getByRole('button', { name: `${sequence.title} , ${courseOutlineMessages.incompleteAssignment.defaultMessage}` })).toHaveAttribute('aria-expanded', 'true'); + userEvent.click(sequenceButton); + expect(screen.getByRole('button', { name: `${sequence.title} , ${courseOutlineMessages.incompleteAssignment.defaultMessage}` })).toHaveAttribute('aria-expanded', 'false'); + }); + + it('updates setOpenSequenceId correctly when toggling sequences', async () => { + await initTestStore(); + renderWithProvider(); + const sequenceButton = screen.getByRole('button', { name: `${sequence.title} , ${courseOutlineMessages.incompleteAssignment.defaultMessage}` }); + expect(sequenceButton).toBeInTheDocument(); + userEvent.click(sequenceButton); + expect(sequenceButton).toHaveAttribute('aria-expanded', 'true'); + userEvent.click(sequenceButton); + expect(sequenceButton).toHaveAttribute('aria-expanded', 'false'); + }); + it('navigates to section or sequence level correctly on click by back/section button', async () => { await initTestStore(); renderWithProvider();