Skip to content

Commit

Permalink
feat: section list and new section button
Browse files Browse the repository at this point in the history
Also refactor api and hooks

fix: publish button behaviour and card header tests

fix: warning in highlights and publish modal test

fix: courseoutline tests

test: add test for new section functionality

fix(lint): lint issues

refactor: remove unnecessary css in CardHeader

refactor: rename emptyPlaceholder test file

refactor: replace ternary operator with 'and' condition

refactor: add black color to expand/collapse button

refactor: display only changed subsection and units in publish modal
  • Loading branch information
navinkarkera committed Dec 7, 2023
1 parent b048b9f commit 61ff8f4
Show file tree
Hide file tree
Showing 26 changed files with 377 additions and 139 deletions.
1 change: 1 addition & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const NOTIFICATION_MESSAGES = {
saving: 'Saving',
duplicating: 'Duplicating',
deleting: 'Deleting',
empty: '',
};

export const DEFAULT_TIME_STAMP = '00:00';
Expand Down
45 changes: 31 additions & 14 deletions src/course-outline/CourseOutline.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import React from 'react';
import PropTypes from 'prop-types';
import { useIntl } from '@edx/frontend-platform/i18n';
import {
Button,
Container,
Layout,
TransitionReplace,
} from '@edx/paragon';
import {
Add as IconAdd,
CheckCircle as CheckCircleIcon,
Warning as WarningIcon,
} from '@edx/paragon/icons';
Expand Down Expand Up @@ -66,6 +68,7 @@ const CourseOutline = ({ courseId }) => {
handleEditSectionSubmit,
handleDeleteSectionSubmit,
handleDuplicateSectionSubmit,
handleNewSectionSubmit,
} = useCourseOutline({ courseId });

document.title = getPageHeadTitle(courseName, intl.formatMessage(messages.headingTitle));
Expand Down Expand Up @@ -131,20 +134,34 @@ const CourseOutline = ({ courseId }) => {
openEnableHighlightsModal={openEnableHighlightsModal}
/>
<div className="pt-4">
{/* TODO add create new section handler in EmptyPlaceholder */}
{sectionsList.length ? sectionsList.map((section) => (
<SectionCard
section={section}
savingStatus={savingStatus}
onOpenHighlightsModal={handleOpenHighlightsModal}
onOpenPublishModal={openPublishModal}
onOpenDeleteModal={openDeleteModal}
onEditSectionSubmit={handleEditSectionSubmit}
onDuplicateSubmit={handleDuplicateSectionSubmit}
isSectionsExpanded={isSectionsExpanded}
/>
)) : (
<EmptyPlaceholder onCreateNewSection={() => ({})} />
{sectionsList.length ? (
<>
{sectionsList.map((section) => (
<SectionCard
key={section.id}
section={section}
savingStatus={savingStatus}
onOpenHighlightsModal={handleOpenHighlightsModal}
onOpenPublishModal={openPublishModal}
onOpenDeleteModal={openDeleteModal}
onEditSectionSubmit={handleEditSectionSubmit}
onDuplicateSubmit={handleDuplicateSectionSubmit}
isSectionsExpanded={isSectionsExpanded}
/>
))}
<Button
data-testid="new-section-button"
className="mt-4"
variant="outline-primary"
onClick={handleNewSectionSubmit}
iconBefore={IconAdd}
block
>
{intl.formatMessage(messages.newSectionButton)}
</Button>
</>
) : (
<EmptyPlaceholder onCreateNewSection={handleNewSectionSubmit} />
)}
</div>
</section>
Expand Down
52 changes: 42 additions & 10 deletions src/course-outline/CourseOutline.test.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React from 'react';
import { render, waitFor, fireEvent } from '@testing-library/react';
import {
render, waitFor, cleanup, fireEvent,
} from '@testing-library/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { AppProvider } from '@edx/frontend-platform/react';
import { initializeMockApp } from '@edx/frontend-platform';
Expand All @@ -11,13 +13,13 @@ import {
getCourseLaunchApiUrl,
getCourseOutlineIndexApiUrl,
getCourseReindexApiUrl,
getCourseReindexApiUrl,
getCourseSectionApiUrl,
getCourseSectionDuplicateApiUrl,
getXBlockApiUrl,
getEnableHighlightsEmailsApiUrl,
getUpdateCourseSectionApiUrl,
getXBlockBaseApiUrl,
} from './data/api';
import {
addNewCourseSectionQuery,
deleteCourseSectionQuery,
duplicateCourseSectionQuery,
editCourseSectionQuery,
Expand All @@ -36,10 +38,12 @@ import {
courseOutlineIndexWithoutSections,
courseBestPracticesMock,
courseLaunchMock,
courseSectionMock,
} from './__mocks__';
import { executeThunk } from '../utils';
import CourseOutline from './CourseOutline';
import messages from './messages';
import headerMessages from './header-navigations/messages';

let axiosMock;
let store;
Expand All @@ -53,6 +57,15 @@ jest.mock('react-router-dom', () => ({
}),
}));

jest.mock('../help-urls/hooks', () => ({
useHelpUrls: () => ({
contentHighlights: 'some',
visibility: 'some',
grading: 'some',
outline: 'some',
}),
}));

const RootWrapper = () => (
<AppProvider store={store}>
<IntlProvider locale="en">
Expand Down Expand Up @@ -100,6 +113,25 @@ describe('<CourseOutline />', () => {
expect(getByText(messages.alertSuccessDescription.defaultMessage)).toBeInTheDocument();
});

it('adds new section correctly', async () => {
const { findAllByTestId } = render(<RootWrapper />);
let element = await findAllByTestId('section-card');
expect(element.length).toBe(4);

axiosMock
.onPost(getXBlockBaseApiUrl())
.reply(200, {
locator: courseSectionMock.id,
});
axiosMock
.onGet(getXBlockApiUrl(courseSectionMock.id))
.reply(200, courseSectionMock);
await executeThunk(addNewCourseSectionQuery(courseId), store.dispatch);

element = await findAllByTestId('section-card');
expect(element.length).toBe(5);
});

it('render error alert after failed reindex correctly', async () => {
const { getByText } = render(<RootWrapper />);

Expand Down Expand Up @@ -163,11 +195,11 @@ describe('<CourseOutline />', () => {
const { queryAllByTestId, getByText } = render(<RootWrapper />);

await waitFor(() => {
const collapseBtn = getByText(messages.collapseAllButton.defaultMessage);
const collapseBtn = getByText(headerMessages.collapseAllButton.defaultMessage);
expect(collapseBtn).toBeInTheDocument();
fireEvent.click(collapseBtn);

const expendBtn = getByText(messages.expandAllButton.defaultMessage);
const expendBtn = getByText(headerMessages.expandAllButton.defaultMessage);
expect(expendBtn).toBeInTheDocument();

fireEvent.click(expendBtn);
Expand Down Expand Up @@ -210,7 +242,7 @@ describe('<CourseOutline />', () => {
await executeThunk(editCourseSectionQuery(section.id, newDisplayName), store.dispatch);

axiosMock
.onGet(getCourseSectionApiUrl(section.id))
.onGet(getXBlockApiUrl(section.id))
.reply(200);
await executeThunk(fetchCourseSectionQuery(section.id), store.dispatch);

Expand All @@ -237,7 +269,7 @@ describe('<CourseOutline />', () => {
const courseBlockId = courseOutlineIndexMock.courseStructure.id;

axiosMock
.onPost(getCourseSectionDuplicateApiUrl())
.onPost(getXBlockBaseApiUrl())
.reply(200, {
duplicate_source_locator: section.id,
parent_locator: courseBlockId,
Expand Down Expand Up @@ -279,7 +311,7 @@ describe('<CourseOutline />', () => {
await executeThunk(publishCourseSectionQuery(section.id), store.dispatch);

axiosMock
.onGet(getCourseSectionApiUrl(section.id))
.onGet(getXBlockApiUrl(section.id))
.reply(200, {
...section,
published: true,
Expand Down Expand Up @@ -316,7 +348,7 @@ describe('<CourseOutline />', () => {
await executeThunk(updateCourseSectionHighlightsQuery(section.id, highlights), store.dispatch);

axiosMock
.onGet(getCourseSectionApiUrl(section.id))
.onGet(getXBlockApiUrl(section.id))
.reply(200, {
...section,
highlights,
Expand Down
93 changes: 93 additions & 0 deletions src/course-outline/__mocks__/courseSection.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
module.exports = {
id: 'block-v1:edX+DemoX+Demo_Course+type@chapter+block@d0e78d363a424da6be5c22704c34f7a7',
display_name: 'Section',
category: 'chapter',
has_children: true,
edited_on: 'Nov 22, 2023 at 07:45 UTC',
published: true,
published_on: 'Nov 22, 2023 at 07:45 UTC',
studio_url: '/course/course-v1:edX+DemoX+Demo_Course?show=block-v1%3AedX%2BDemoX%2BDemo_Course%2Btype%40chapter%2Bblock%40d0e78d363a424da6be5c22704c34f7a7',
released_to_students: true,
release_date: 'Feb 05, 2013 at 05:00 UTC',
visibility_state: 'live',
has_explicit_staff_lock: false,
start: '2013-02-05T05:00:00Z',
graded: false,
due_date: '',
due: null,
relative_weeks_due: null,
format: null,
course_graders: [
'Homework',
'Exam',
],
has_changes: false,
actions: {
deletable: true,
draggable: true,
childAddable: true,
duplicable: true,
},
explanatory_message: null,
group_access: {},
user_partitions: [
{
id: 50,
name: 'Enrollment Track Groups',
scheme: 'enrollment_track',
groups: [
{
id: 2,
name: 'Verified Certificate',
selected: false,
deleted: false,
},
{
id: 1,
name: 'Audit',
selected: false,
deleted: false,
},
],
},
],
show_correctness: 'always',
highlights: [],
highlights_enabled: true,
highlights_preview_only: false,
highlights_doc_url: 'http://edx.readthedocs.io/projects/open-edx-building-and-running-a-course/en/latest/developing_course/course_sections.html#set-section-highlights-for-weekly-course-highlight-messages',
child_info: {
category: 'sequential',
display_name: 'Subsection',
children: [],
},
ancestor_has_staff_lock: false,
staff_only_message: false,
enable_copy_paste_units: false,
has_partition_group_components: false,
user_partition_info: {
selectable_partitions: [
{
id: 50,
name: 'Enrollment Track Groups',
scheme: 'enrollment_track',
groups: [
{
id: 2,
name: 'Verified Certificate',
selected: false,
deleted: false,
},
{
id: 1,
name: 'Audit',
selected: false,
deleted: false,
},
],
},
],
selected_partition_index: -1,
selected_groups_label: '',
},
};
1 change: 1 addition & 0 deletions src/course-outline/__mocks__/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export { default as courseOutlineIndexMock } from './courseOutlineIndex';
export { default as courseOutlineIndexWithoutSections } from './courseOutlineIndexWithoutSections';
export { default as courseBestPracticesMock } from './courseBestPractices';
export { default as courseLaunchMock } from './courseLaunch';
export { default as courseSectionMock } from './courseSection';
13 changes: 7 additions & 6 deletions src/course-outline/card-header/CardHeader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
} from '@edx/paragon';
import {
ArrowDropDown as ArrowDownIcon,
ArrowDropUp as ArrowUpIcon,
MoreVert as MoveVertIcon,
EditOutline as EditIcon,
} from '@edx/paragon/icons';
Expand All @@ -26,6 +27,7 @@ import messages from './messages';
const CardHeader = ({
title,
sectionStatus,
hasChanges,
isExpanded,
onClickPublish,
onClickMenuButton,
Expand All @@ -42,8 +44,8 @@ const CardHeader = ({
const [titleValue, setTitleValue] = useState(title);

const { badgeTitle, badgeIcon } = getSectionStatusBadgeContent(sectionStatus, messages, intl);
const isDisabledPublish = sectionStatus === SECTION_BADGE_STATUTES.live
|| sectionStatus === SECTION_BADGE_STATUTES.publishedNotLive;
const isDisabledPublish = (sectionStatus === SECTION_BADGE_STATUTES.live
|| sectionStatus === SECTION_BADGE_STATUTES.publishedNotLive) && !hasChanges;

useEscapeClick({
onEscape: () => {
Expand Down Expand Up @@ -86,12 +88,10 @@ const CardHeader = ({
)}
>
<Button
iconBefore={ArrowDownIcon}
iconBefore={isExpanded ? ArrowUpIcon : ArrowDownIcon}
variant="tertiary"
data-testid="section-card-header__expanded-btn"
className={classNames('section-card-header__expanded-btn', {
collapsed: !isExpanded,
})}
className="section-card-header__expanded-btn"
onClick={() => onExpand((prevState) => !prevState)}
>
<Truncate lines={1} className="h3 mb-0">{title}</Truncate>
Expand Down Expand Up @@ -149,6 +149,7 @@ const CardHeader = ({
CardHeader.propTypes = {
title: PropTypes.string.isRequired,
sectionStatus: PropTypes.string.isRequired,
hasChanges: PropTypes.bool.isRequired,
isExpanded: PropTypes.bool.isRequired,
onExpand: PropTypes.func.isRequired,
onClickPublish: PropTypes.func.isRequired,
Expand Down
23 changes: 1 addition & 22 deletions src/course-outline/card-header/CardHeader.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,7 @@
height: 1.5rem;
margin-right: .25rem;
background: transparent;

&::before {
display: none;
}

& svg {
width: 1.5rem;
height: 1.5rem;
}

&.collapsed > .pgn__icon {
transform: rotate(180deg);
}

& span:first-child {
color: $black;
}
color: $black;
}

.section-card-header__badge-status {
Expand All @@ -44,11 +28,6 @@
}
}

.section-card-header__menu {
display: flex;
align-items: center;
}

.pgn__form-group {
width: 80%;
}
Expand Down
Loading

0 comments on commit 61ff8f4

Please sign in to comment.