fix: only one in-course experience sidebar can be open at a time failing

This commit is contained in:
jciasenza
2024-11-15 16:48:04 -03:00
committed by Farhaan Bukhsh
parent ead98538b9
commit 020e7fb42c
4 changed files with 16 additions and 24 deletions

View File

@@ -26,6 +26,7 @@ import messages from './messages';
const CourseOutlineTray = ({ intl }) => { const CourseOutlineTray = ({ intl }) => {
const [selectedSection, setSelectedSection] = useState(null); const [selectedSection, setSelectedSection] = useState(null);
const [openSequenceId, setOpenSequenceId] = useState(null);
const [isDisplaySequenceLevel, setDisplaySequenceLevel, setDisplaySectionLevel] = useToggle(true); const [isDisplaySequenceLevel, setDisplaySequenceLevel, setDisplaySectionLevel] = useToggle(true);
const dispatch = useDispatch(); const dispatch = useDispatch();
@@ -62,6 +63,11 @@ const CourseOutlineTray = ({ intl }) => {
setSelectedSection(id); setSelectedSection(id);
}; };
const handleToggleSequence = (sequenceId) => {
setOpenSequenceId((prevOpenSequenceId) => (prevOpenSequenceId === sequenceId ? null : sequenceId));
};
// Sidebar Heading definition
const sidebarHeading = ( const sidebarHeading = (
<div className="outline-sidebar-heading-wrapper sticky d-flex justify-content-between align-self-start align-items-center bg-light-200 p-2.5 pl-4"> <div className="outline-sidebar-heading-wrapper sticky d-flex justify-content-between align-self-start align-items-center bg-light-200 p-2.5 pl-4">
{isDisplaySequenceLevel && backButtonTitle ? ( {isDisplaySequenceLevel && backButtonTitle ? (
@@ -129,7 +135,8 @@ const CourseOutlineTray = ({ intl }) => {
key={sequenceId} key={sequenceId}
courseId={courseId} courseId={courseId}
sequence={sequences[sequenceId]} sequence={sequences[sequenceId]}
defaultOpen={sequenceId === activeSequenceId} isOpen={sequenceId === openSequenceId} // Control if the sequence is open
onToggle={() => handleToggleSequence(sequenceId)} // Change the state
activeUnitId={unitId} activeUnitId={unitId}
/> />
)) ))

View File

@@ -15,7 +15,6 @@ describe('<CourseOutlineTray />', () => {
let store; let store;
let section = {}; let section = {};
let sequence = {}; let sequence = {};
let unit;
let unitId; let unitId;
let courseId; let courseId;
let mockData; let mockData;
@@ -32,7 +31,6 @@ describe('<CourseOutlineTray />', () => {
const activeSectionId = Object.keys(state.courseware.courseOutline.sections)[0]; const activeSectionId = Object.keys(state.courseware.courseOutline.sections)[0];
section = state.courseware.courseOutline.sections[activeSectionId]; section = state.courseware.courseOutline.sections[activeSectionId];
[unitId] = sequence.unitIds; [unitId] = sequence.unitIds;
unit = state.courseware.courseOutline.units[unitId];
} }
mockData = { mockData = {
@@ -84,7 +82,6 @@ describe('<CourseOutlineTray />', () => {
expect(screen.getByRole('button', { name: section.title })).toBeInTheDocument(); expect(screen.getByRole('button', { name: section.title })).toBeInTheDocument();
expect(screen.getByRole('button', { name: messages.toggleCourseOutlineTrigger.defaultMessage })).toBeInTheDocument(); expect(screen.getByRole('button', { name: messages.toggleCourseOutlineTrigger.defaultMessage })).toBeInTheDocument();
expect(screen.getByRole('button', { name: `${sequence.title} , ${courseOutlineMessages.incompleteAssignment.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 () => { it('collapses sidebar correctly when toggle button is clicked', async () => {

View File

@@ -1,4 +1,3 @@
import { useState } from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import classNames from 'classnames'; import classNames from 'classnames';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
@@ -14,7 +13,8 @@ import { UNIT_ICON_TYPES } from './UnitIcon';
const SidebarSequence = ({ const SidebarSequence = ({
intl, intl,
courseId, courseId,
defaultOpen, isOpen,
onToggle,
sequence, sequence,
activeUnitId, activeUnitId,
}) => { }) => {
@@ -28,7 +28,6 @@ const SidebarSequence = ({
completionStat, completionStat,
} = sequence; } = sequence;
const [open, setOpen] = useState(defaultOpen);
const { units = {} } = useSelector(getCourseOutline); const { units = {} } = useSelector(getCourseOutline);
const activeSequenceId = useSelector(getSequenceId); const activeSequenceId = useSelector(getSequenceId);
const isActiveSequence = id === activeSequenceId; const isActiveSequence = id === activeSequenceId;
@@ -53,11 +52,11 @@ const SidebarSequence = ({
return ( return (
<li> <li>
<Collapsible <Collapsible
className={classNames('mb-2', { 'active-section': isActiveSequence, 'bg-info-100': isActiveSequence && !open })} className={classNames('mb-2', { 'active-section': isActiveSequence, 'bg-info-100': isActiveSequence && !isOpen })}
styling="card-lg text-break" styling="card-lg text-break"
title={sectionTitle} title={sectionTitle}
open={open} open={isOpen}
onToggle={() => setOpen(!open)} onToggle={onToggle}
> >
<ol className="list-unstyled"> <ol className="list-unstyled">
{unitIds.map((unitId, index) => ( {unitIds.map((unitId, index) => (
@@ -82,7 +81,8 @@ const SidebarSequence = ({
SidebarSequence.propTypes = { SidebarSequence.propTypes = {
intl: intlShape.isRequired, intl: intlShape.isRequired,
courseId: PropTypes.string.isRequired, courseId: PropTypes.string.isRequired,
defaultOpen: PropTypes.bool.isRequired, isOpen: PropTypes.bool.isRequired,
onToggle: PropTypes.func.isRequired,
sequence: PropTypes.shape({ sequence: PropTypes.shape({
complete: PropTypes.bool, complete: PropTypes.bool,
id: PropTypes.string, id: PropTypes.string,

View File

@@ -1,12 +1,11 @@
import { MemoryRouter } from 'react-router-dom'; 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 userEvent from '@testing-library/user-event';
import { AppProvider } from '@edx/frontend-platform/react'; import { AppProvider } from '@edx/frontend-platform/react';
import { IntlProvider } from '@edx/frontend-platform/i18n'; import { IntlProvider } from '@edx/frontend-platform/i18n';
import courseOutlineMessages from '@src/course-home/outline-tab/messages'; import courseOutlineMessages from '@src/course-home/outline-tab/messages';
import { initializeMockApp, initializeTestStore } from '@src/setupTest'; import { initializeMockApp, initializeTestStore } from '@src/setupTest';
import messages from '../messages';
import SidebarSequence from './SidebarSequence'; import SidebarSequence from './SidebarSequence';
initializeMockApp(); initializeMockApp();
@@ -15,7 +14,6 @@ describe('<SidebarSequence />', () => {
let courseId; let courseId;
let store; let store;
let sequence; let sequence;
let unit;
const sequenceDescription = 'sequence test description'; const sequenceDescription = 'sequence test description';
const initTestStore = async (options) => { const initTestStore = async (options) => {
@@ -25,8 +23,6 @@ describe('<SidebarSequence />', () => {
let activeSequenceId = ''; let activeSequenceId = '';
[activeSequenceId] = Object.keys(state.courseware.courseOutline.sequences); [activeSequenceId] = Object.keys(state.courseware.courseOutline.sequences);
sequence = state.courseware.courseOutline.sequences[activeSequenceId]; sequence = state.courseware.courseOutline.sequences[activeSequenceId];
const unitId = sequence.unitIds[0];
unit = state.courseware.courseOutline.units[unitId];
}; };
function renderWithProvider(props = {}) { function renderWithProvider(props = {}) {
@@ -55,7 +51,6 @@ describe('<SidebarSequence />', () => {
expect(screen.getByText(sequence.title)).toBeInTheDocument(); expect(screen.getByText(sequence.title)).toBeInTheDocument();
expect(screen.queryByText(sequenceDescription)).not.toBeInTheDocument(); expect(screen.queryByText(sequenceDescription)).not.toBeInTheDocument();
expect(screen.getByText(`, ${courseOutlineMessages.incompleteAssignment.defaultMessage}`)).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 () => { it('renders correctly when sequence is not collapsed and complete', async () => {
@@ -72,13 +67,6 @@ describe('<SidebarSequence />', () => {
expect(screen.getByText(sequence.title)).toBeInTheDocument(); expect(screen.getByText(sequence.title)).toBeInTheDocument();
expect(screen.getByText(sequenceDescription)).toBeInTheDocument(); expect(screen.getByText(sequenceDescription)).toBeInTheDocument();
expect(screen.getByText(`, ${courseOutlineMessages.completedAssignment.defaultMessage}`)).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)); userEvent.click(screen.getByText(sequence.title));
await waitFor(() => {
expect(screen.queryByText(unit.title)).not.toBeInTheDocument();
expect(screen.queryByText(`, ${messages.incompleteUnit.defaultMessage}`)).not.toBeInTheDocument();
});
}); });
}); });