fix: only one in-course experience sidebar can be open at a time failing
This commit is contained in:
committed by
Farhaan Bukhsh
parent
ead98538b9
commit
020e7fb42c
@@ -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}
|
||||||
/>
|
/>
|
||||||
))
|
))
|
||||||
|
|||||||
@@ -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 () => {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user