Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
feat: improved accessibility for Show/Hide Accordions
  • Loading branch information
vladislavkeblysh authored and filippovskii09 committed Oct 23, 2025
commit 65610f58847269d7823d40f31fdfbee43cc0a3ec
18 changes: 18 additions & 0 deletions src/course-home/outline-tab/OutlineTab.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,24 @@ describe('Outline Tab', () => {
expect(expandedSectionNode).toHaveAttribute('aria-expanded', 'true');
});

it('displays correct heading for expanded section', async () => {
const { courseBlocks } = await buildMinimalCourseBlocks(courseId, 'Title', { resumeBlock: true });
setTabData({ course_blocks: { blocks: courseBlocks.blocks } });
await fetchAndRender();
const headingContent = screen.getByText('Title of Section');
const { parentElement } = headingContent;
expect(parentElement.tagName).toBe('H2');
});

it('checks that the expanded section is within the correct list', async () => {
const { courseBlocks } = await buildMinimalCourseBlocks(courseId, 'Title', { resumeBlock: true });
setTabData({ course_blocks: { blocks: courseBlocks.blocks } });
await fetchAndRender();
const listElement = screen.getByRole('presentation', { id: 'courseHome-outline' });
expect(listElement).toBeInTheDocument();
expect(listElement.tagName).toBe('OL');
});

it('includes outline_tab_notifications_slot', async () => {
const { courseBlocks } = await buildMinimalCourseBlocks(courseId, 'Title', { resumeBlock: true });
setTabData({
Expand Down
139 changes: 139 additions & 0 deletions src/course-home/outline-tab/Section.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Collapsible, IconButton, Icon } from '@openedx/paragon';
import { faCheckCircle as fasCheckCircle, faMinus, faPlus } from '@fortawesome/free-solid-svg-icons';
import { faCheckCircle as farCheckCircle } from '@fortawesome/free-regular-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

import { DisabledVisible } from '@openedx/paragon/icons';
import SequenceLink from './SequenceLink';

Check failure on line 10 in src/course-home/outline-tab/Section.jsx

View workflow job for this annotation

GitHub Actions / tests

Missing file extension for "./SequenceLink"

Check failure on line 10 in src/course-home/outline-tab/Section.jsx

View workflow job for this annotation

GitHub Actions / tests

Unable to resolve path to module './SequenceLink'
import { useModel } from '../../generic/model-store';

import genericMessages from '../../generic/messages';
import messages from './messages';

const Section = ({
courseId,
defaultOpen,
expand,
intl,
section,
}) => {
const {
complete,
sequenceIds,
title,
hideFromTOC,
} = section;
const {
courseBlocks: {
sequences,
},
} = useModel('outline', courseId);

const [open, setOpen] = useState(defaultOpen);

useEffect(() => {
setOpen(expand);
}, [expand]);

useEffect(() => {
setOpen(defaultOpen);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

const sectionTitle = (
<div className="d-flex row w-100 m-0">
<div className="col-auto p-0">
{complete ? (
<FontAwesomeIcon
icon={fasCheckCircle}
fixedWidth
className="float-left mt-1 text-success"
aria-hidden="true"
title={intl.formatMessage(messages.completedSection)}
/>
) : (
<FontAwesomeIcon
icon={farCheckCircle}
fixedWidth
className="float-left mt-1 text-gray-400"
aria-hidden="true"
title={intl.formatMessage(messages.incompleteSection)}
/>
)}
</div>
<div className="col-7 ml-3 p-0 font-weight-bold text-dark-500">
<h2 className="course-outline-tab-section-title text-dark-500 mb-0">
<span className="align-middle col-6">{title}</span>
</h2>
<span className="sr-only">
, {intl.formatMessage(complete ? messages.completedSection : messages.incompleteSection)}
</span>
</div>
{hideFromTOC && (
<div className="row">
{hideFromTOC && (
<span className="small d-flex align-content-end">
<Icon className="mr-2" src={DisabledVisible} data-testid="hide-from-toc-section-icon" />
<span data-testid="hide-from-toc-section-text">
{intl.formatMessage(messages.hiddenSection)}
</span>
</span>
)}
</div>
)}
</div>
);

return (
<li>
<Collapsible
className="mb-2"
styling="card-lg"
title={sectionTitle}
open={open}
onToggle={() => { setOpen(!open); }}
iconWhenClosed={(
<IconButton
alt={intl.formatMessage(messages.openSection)}
icon={faPlus}
onClick={() => { setOpen(true); }}
size="sm"
/>
)}
iconWhenOpen={(
<IconButton
alt={intl.formatMessage(genericMessages.close)}
icon={faMinus}
onClick={() => { setOpen(false); }}
size="sm"
/>
)}
>
<ol className="list-unstyled">
{sequenceIds.map((sequenceId, index) => (
<SequenceLink
key={sequenceId}
id={sequenceId}
courseId={courseId}
sequence={sequences[sequenceId]}
first={index === 0}
/>
))}
</ol>
</Collapsible>
</li>
);
};

Section.propTypes = {
courseId: PropTypes.string.isRequired,
defaultOpen: PropTypes.bool.isRequired,
expand: PropTypes.bool.isRequired,
intl: intlShape.isRequired,
section: PropTypes.shape().isRequired,
};

export default injectIntl(Section);
4 changes: 4 additions & 0 deletions src/course-home/outline-tab/Section.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.course-outline-tab-section-title {
font-size: $font-size-base;
line-height: $line-height-base;
}
7 changes: 5 additions & 2 deletions src/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -436,12 +436,12 @@
.course-outline-tab .pgn__card {
.pgn__card-header {
display: block;

.pgn__card-header-content {
margin-top: 0;
}
}

.pgn__card-header-actions {
margin-left: 0;
}
Expand All @@ -456,6 +456,9 @@
@import "courseware/course/content-tools/calculator/calculator.scss";
@import "courseware/course/content-tools/contentTools.scss";
@import "course-home/dates-tab/timeline/Day.scss";
@import "course-home/outline-tab/Section.scss";
@import "generic/upgrade-notification/UpgradeNotification.scss";
@import "generic/upsell-bullets/UpsellBullets.scss";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[important]: We need to verify what do we need to do with UpsellBullets.scss

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was removed, no errors occurred

@import "course-home/outline-tab/widgets/ProctoringInfoPanel.scss";
@import "course-home/outline-tab/widgets/FlagButton.scss";
@import "course-home/progress-tab/course-completion/CompletionDonutChart.scss";
Expand Down
Loading