diff --git a/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.jsx b/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.jsx index 17d75637f2..833562216a 100644 --- a/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.jsx +++ b/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.jsx @@ -26,7 +26,6 @@ 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(); @@ -63,10 +62,6 @@ const CourseOutlineTray = ({ intl }) => { setSelectedSection(id); }; - const handleToggleSequence = (sequenceId) => { - setOpenSequenceId((prevOpenSequenceId) => (prevOpenSequenceId === sequenceId ? null : sequenceId)); - }; - const sidebarHeading = (
{isDisplaySequenceLevel && backButtonTitle ? ( @@ -134,8 +129,7 @@ const CourseOutlineTray = ({ intl }) => { key={sequenceId} courseId={courseId} sequence={sequences[sequenceId]} - isOpen={sequenceId === openSequenceId} - onToggle={() => handleToggleSequence(sequenceId)} + defaultOpen={sequenceId === activeSequenceId} 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 10f8315f01..0302f1e74f 100644 --- a/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.test.jsx +++ b/src/courseware/course/sidebar/sidebars/course-outline/CourseOutlineTray.test.jsx @@ -15,6 +15,7 @@ describe('', () => { let store; let section = {}; let sequence = {}; + let unit; let unitId; let courseId; let mockData; @@ -31,6 +32,7 @@ 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 = { @@ -82,6 +84,7 @@ 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 () => { @@ -99,30 +102,6 @@ describe('', () => { expect(mockToggleSidebar).toHaveBeenCalledWith(null); }); - it('toggles openSequenceId correctly when a sequence is clicked', async () => { - const user = userEvent.setup(); - await initTestStore(); - renderWithProvider(); - const sequenceButton = screen.getByRole('button', { name: `${sequence.title} , ${courseOutlineMessages.incompleteAssignment.defaultMessage}` }); - expect(sequenceButton).toBeInTheDocument(); - await user.click(sequenceButton); - expect(screen.getByRole('button', { name: `${sequence.title} , ${courseOutlineMessages.incompleteAssignment.defaultMessage}` })).toHaveAttribute('aria-expanded', 'true'); - await user.click(sequenceButton); - expect(screen.getByRole('button', { name: `${sequence.title} , ${courseOutlineMessages.incompleteAssignment.defaultMessage}` })).toHaveAttribute('aria-expanded', 'false'); - }); - - it('updates setOpenSequenceId correctly when toggling sequences', async () => { - const user = userEvent.setup(); - await initTestStore(); - renderWithProvider(); - const sequenceButton = screen.getByRole('button', { name: `${sequence.title} , ${courseOutlineMessages.incompleteAssignment.defaultMessage}` }); - expect(sequenceButton).toBeInTheDocument(); - await user.click(sequenceButton); - expect(sequenceButton).toHaveAttribute('aria-expanded', 'true'); - await user.click(sequenceButton); - expect(sequenceButton).toHaveAttribute('aria-expanded', 'false'); - }); - 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/components/SidebarSequence.jsx b/src/courseware/course/sidebar/sidebars/course-outline/components/SidebarSequence.jsx index ebd5cb9a0e..18d41071fb 100644 --- a/src/courseware/course/sidebar/sidebars/course-outline/components/SidebarSequence.jsx +++ b/src/courseware/course/sidebar/sidebars/course-outline/components/SidebarSequence.jsx @@ -1,3 +1,4 @@ +import { useState } from 'react'; import { useSelector } from 'react-redux'; import classNames from 'classnames'; import PropTypes from 'prop-types'; @@ -13,8 +14,7 @@ import { UNIT_ICON_TYPES } from './UnitIcon'; const SidebarSequence = ({ intl, courseId, - isOpen, - onToggle, + defaultOpen, sequence, activeUnitId, }) => { @@ -28,6 +28,7 @@ const SidebarSequence = ({ completionStat, } = sequence; + const [open, setOpen] = useState(defaultOpen); const { units = {} } = useSelector(getCourseOutline); const activeSequenceId = useSelector(getSequenceId); const isActiveSequence = id === activeSequenceId; @@ -52,11 +53,11 @@ const SidebarSequence = ({ return (
  • setOpen(!open)} >
      {unitIds.map((unitId, index) => ( @@ -81,8 +82,7 @@ const SidebarSequence = ({ SidebarSequence.propTypes = { intl: intlShape.isRequired, courseId: PropTypes.string.isRequired, - isOpen: PropTypes.bool.isRequired, - onToggle: PropTypes.func.isRequired, + defaultOpen: PropTypes.bool.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 8f2b0dcb16..26a0c72261 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,11 +1,12 @@ import { MemoryRouter } from 'react-router-dom'; -import { render, screen } from '@testing-library/react'; +import { render, screen, waitFor } 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(); @@ -14,6 +15,7 @@ describe('', () => { let courseId; let store; let sequence; + let unit; const sequenceDescription = 'sequence test description'; const initTestStore = async (options) => { @@ -23,6 +25,8 @@ 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 = {}) { @@ -51,6 +55,7 @@ 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 () => { @@ -68,6 +73,13 @@ 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(); + await user.click(screen.getByText(sequence.title)); + await waitFor(() => { + expect(screen.queryByText(unit.title)).not.toBeInTheDocument(); + expect(screen.queryByText(`, ${messages.incompleteUnit.defaultMessage}`)).not.toBeInTheDocument(); + }); }); });