Skip to content

Commit

Permalink
feat: show hide from toc
Browse files Browse the repository at this point in the history
  • Loading branch information
BryanttV authored and arbrandes committed Mar 6, 2024
1 parent b1c2c1f commit 7c8e26d
Show file tree
Hide file tree
Showing 7 changed files with 137 additions and 4 deletions.
2 changes: 2 additions & 0 deletions src/course-home/data/__snapshots__/redux.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ Object {
"block-v1:edX+DemoX+Demo_Course+type@chapter+block@bcdabcdabcdabcdabcdabcdabcdabcd2": Object {
"complete": false,
"courseId": "course-v1:edX+DemoX+Demo_Course",
"hideFromTOC": undefined,
"id": "block-v1:edX+DemoX+Demo_Course+type@chapter+block@bcdabcdabcdabcdabcdabcdabcdabcd2",
"resumeBlock": false,
"sequenceIds": Array [
Expand All @@ -514,6 +515,7 @@ Object {
"due": null,
"effortActivities": 2,
"effortTime": 15,
"hideFromTOC": undefined,
"icon": null,
"id": "block-v1:edX+DemoX+Demo_Course+type@sequential+block@bcdabcdabcdabcdabcdabcdabcdabcd1",
"sectionId": "block-v1:edX+DemoX+Demo_Course+type@chapter+block@bcdabcdabcdabcdabcdabcdabcdabcd2",
Expand Down
2 changes: 2 additions & 0 deletions src/course-home/data/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ export function normalizeOutlineBlocks(courseId, blocks) {
title: block.display_name,
resumeBlock: block.resume_block,
sequenceIds: block.children || [],
hideFromTOC: block.hide_from_toc,
};
break;

Expand All @@ -152,6 +153,7 @@ export function normalizeOutlineBlocks(courseId, blocks) {
// link in the outline (even though we ignore the given url and use an internal <Link> to ourselves).
showLink: !!block.lms_web_url,
title: block.display_name,
hideFromTOC: block.hide_from_toc,
};
break;

Expand Down
92 changes: 92 additions & 0 deletions src/course-home/outline-tab/OutlineTab.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -1269,5 +1269,97 @@ describe('Outline Tab', () => {
await waitFor(() => expect(axiosMock.history.post).toHaveLength(1));
expect(axiosMock.history.post[0].url).toEqual(resendEmailUrl);
});

it('section should show hidden from toc message when hide_from_toc is true', async () => {
const { courseBlocks } = await buildMinimalCourseBlocks(courseId, 'Title', { resumeBlock: true });
const courseBlocksIds = Object.keys(courseBlocks.blocks);
const newCourseBlocks = courseBlocksIds.reduce((blocks, blockId) => ({
...blocks,
[blockId]: {
...courseBlocks.blocks[blockId],
hide_from_toc: true,
},
}), {});

setTabData({
course_blocks: { blocks: newCourseBlocks },
});
await fetchAndRender();

const iconHiddenFromTocSectionNode = screen.getByTestId('hide-from-toc-section-icon');
const textHiddenFromTocSectionNode = screen.getByTestId('hide-from-toc-section-text');
expect(iconHiddenFromTocSectionNode).toBeInTheDocument();
expect(textHiddenFromTocSectionNode).toBeInTheDocument();
expect(textHiddenFromTocSectionNode.textContent).toBe('Hidden in Course Outline, accessible via link');
});

it('section should not show hidden from toc message when hide_from_toc is false', async () => {
const { courseBlocks } = await buildMinimalCourseBlocks(courseId, 'Title', { resumeBlock: true });
const courseBlocksIds = Object.keys(courseBlocks.blocks);
const newCourseBlocks = courseBlocksIds.reduce((blocks, blockId) => ({
...blocks,
[blockId]: {
...courseBlocks.blocks[blockId],
hide_from_toc: false,
},
}), {});

setTabData({
course_blocks: { blocks: newCourseBlocks },
});
await fetchAndRender();

const iconHiddenFromTocSectionNode = screen.queryByTestId('hide-from-toc-section-icon');
const textHiddenFromTocSectionNode = screen.queryByTestId('hide-from-toc-section-text');

expect(iconHiddenFromTocSectionNode).not.toBeInTheDocument();
expect(textHiddenFromTocSectionNode).not.toBeInTheDocument();
});

it('sequence link should show hidden from toc message when hide_from_toc is true', async () => {
const { courseBlocks } = await buildMinimalCourseBlocks(courseId, 'Title', { resumeBlock: true });
const courseBlocksIds = Object.keys(courseBlocks.blocks);
const newCourseBlocks = courseBlocksIds.reduce((blocks, blockId) => ({
...blocks,
[blockId]: {
...courseBlocks.blocks[blockId],
hide_from_toc: true,
},
}), {});

setTabData({
course_blocks: { blocks: newCourseBlocks },
});
await fetchAndRender();

const iconHiddenFromTocSequenceLinkNode = screen.getByTestId('hide-from-toc-sequence-link-icon');
const textHiddenFromTocSequenceLink = screen.getByTestId('hide-from-toc-sequence-link-text');
expect(iconHiddenFromTocSequenceLinkNode).toBeInTheDocument();
expect(textHiddenFromTocSequenceLink).toBeInTheDocument();
expect(textHiddenFromTocSequenceLink.textContent).toBe('Subsections are not navigable between each other, they can only be accessed through their link.');
});

it('sequence link not show hidden from toc message when hide_from_toc is false', async () => {
const { courseBlocks } = await buildMinimalCourseBlocks(courseId, 'Title', { resumeBlock: true });
const courseBlocksIds = Object.keys(courseBlocks.blocks);
const newCourseBlocks = courseBlocksIds.reduce((blocks, blockId) => ({
...blocks,
[blockId]: {
...courseBlocks.blocks[blockId],
hide_from_toc: false,
},
}), {});

setTabData({
course_blocks: { blocks: newCourseBlocks },
});
await fetchAndRender();

const iconHiddenFromTocSequenceLink = screen.queryByTestId('hide-from-toc-sequence-link-icon');
const textHiddenFromTocSequenceLink = screen.queryByTestId('hide-from-toc-sequence-link-text');

expect(iconHiddenFromTocSequenceLink).not.toBeInTheDocument();
expect(textHiddenFromTocSequenceLink).not.toBeInTheDocument();
});
});
});
22 changes: 18 additions & 4 deletions src/course-home/outline-tab/Section.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Collapsible, IconButton } from '@openedx/paragon';
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';
import { useModel } from '../../generic/model-store';

Expand All @@ -23,6 +24,7 @@ const Section = ({
complete,
sequenceIds,
title,
hideFromTOC,
} = section;
const {
courseBlocks: {
Expand All @@ -42,7 +44,7 @@ const Section = ({
}, []);

const sectionTitle = (
<div className="row w-100 m-0">
<div className="d-flex row w-100 m-0">
<div className="col-auto p-0">
{complete ? (
<FontAwesomeIcon
Expand All @@ -62,12 +64,24 @@ const Section = ({
/>
)}
</div>
<div className="col-10 ml-3 p-0 font-weight-bold text-dark-500">
<span className="align-middle">{title}</span>
<div className="col-7 ml-3 p-0 font-weight-bold text-dark-500">
<span className="align-middle col-6">{title}</span>
<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>
);

Expand Down
13 changes: 13 additions & 0 deletions src/course-home/outline-tab/SequenceLink.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { faCheckCircle as fasCheckCircle } from '@fortawesome/free-solid-svg-ico
import { faCheckCircle as farCheckCircle } from '@fortawesome/free-regular-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

import { Icon } from '@openedx/paragon';
import { Block } from '@openedx/paragon/icons';
import EffortEstimate from '../../shared/effort-estimate';
import { useModel } from '../../generic/model-store';
import messages from './messages';
Expand All @@ -29,6 +31,7 @@ const SequenceLink = ({
due,
showLink,
title,
hideFromTOC,
} = sequence;
const {
userTimezone,
Expand Down Expand Up @@ -114,6 +117,16 @@ const SequenceLink = ({
<EffortEstimate className="ml-3 align-middle" block={sequence} />
</div>
</div>
{hideFromTOC && (
<div className="row w-100 my-2 mx-4 pl-3">
<span className="small d-flex">
<Icon className="mr-2" src={Block} data-testid="hide-from-toc-sequence-link-icon" />
<span data-testid="hide-from-toc-sequence-link-text">
{intl.formatMessage(messages.hiddenSequenceLink)}
</span>
</span>
</div>
)}
<div className="row w-100 m-0 ml-3 pl-3">
<small className="text-body pl-2">
{due ? dueDateMessage : noDueDateMessage}
Expand Down
10 changes: 10 additions & 0 deletions src/course-home/outline-tab/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,16 @@ const messages = defineMessages({
defaultMessage: 'Completed section',
description: 'Text used to describe the green checkmark icon in front of a section title',
},
hiddenSection: {
id: 'learning.outline.hiddenSection',
defaultMessage: 'Hidden in Course Outline, accessible via link',
description: 'Label for hidden section in course outline',
},
hiddenSequenceLink: {
id: 'learning.outline.hiddenSequenceLink',
defaultMessage: 'Subsections are not navigable between each other, they can only be accessed through their link.',
description: 'Label for hidden sequence in course outline',
},
dates: {
id: 'learning.outline.dates',
defaultMessage: 'Important dates',
Expand Down
Empty file added webpack.dev-tutor.config.js
Empty file.

0 comments on commit 7c8e26d

Please sign in to comment.