Skip to content

Commit

Permalink
feat: [FC-0044] Unit page - add new component section (#828)
Browse files Browse the repository at this point in the history
* feat: Course unit - add new component section

* feat: Course unit - make Discussion and Drag-and-Drop button functional

* feat: Course unit - make Problem button functional

* feat: Unit page - make Video button functional
  • Loading branch information
ihor-romaniuk authored Feb 9, 2024
1 parent 3938015 commit 1555e9f
Show file tree
Hide file tree
Showing 34 changed files with 1,966 additions and 21 deletions.
1 change: 1 addition & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export const BADGE_STATES = {
};

export const NOTIFICATION_MESSAGES = {
adding: 'Adding',
saving: 'Saving',
duplicating: 'Duplicating',
deleting: 'Deleting',
Expand Down
9 changes: 7 additions & 2 deletions src/course-unit/CourseUnit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import getPageHeadTitle from '../generic/utils';
import ProcessingNotification from '../generic/processing-notification';
import InternetConnectionAlert from '../generic/internet-connection-alert';
import Loading from '../generic/Loading';
import AddComponent from './add-component/AddComponent';
import HeaderTitle from './header-title/HeaderTitle';
import Breadcrumbs from './breadcrumbs/Breadcrumbs';
import HeaderNavigations from './header-navigations/HeaderNavigations';
Expand All @@ -33,6 +34,7 @@ const CourseUnit = ({ courseId }) => {
headerNavigationsActions,
handleTitleEdit,
handleInternetConnectionFailed,
handleCreateNewCourseXblock,
} = useCourseUnit({ courseId, blockId });

document.title = getPageHeadTitle('', unitTitle);
Expand Down Expand Up @@ -87,9 +89,12 @@ const CourseUnit = ({ courseId }) => {
xl={[{ span: 9 }, { span: 3 }]}
>
<Layout.Element>
{/* TODO: Unit content will be added in the following tasks. */}
Unit content
<AddComponent
blockId={blockId}
handleCreateNewCourseXblock={handleCreateNewCourseXblock}
/>
</Layout.Element>
<Layout.Element />
</Layout>
</section>
</Container>
Expand Down
1 change: 1 addition & 0 deletions src/course-unit/CourseUnit.scss
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
@import "./breadcrumbs/Breadcrumbs";
@import "./course-sequence/CourseSequence";
@import "./add-component/AddComponent";
65 changes: 65 additions & 0 deletions src/course-unit/CourseUnit.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,27 @@ import { getConfig, initializeMockApp } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';

import {
getCourseSectionVerticalApiUrl,
getCourseUnitApiUrl,
getXBlockBaseApiUrl,
postXBlockBaseApiUrl,
} from './data/api';
import {
fetchCourseSectionVerticalData,
fetchCourseUnitQuery,
} from './data/thunk';
import initializeStore from '../store';
import {
courseCreateXblockMock,
courseSectionVerticalMock,
courseUnitIndexMock,
} from './__mocks__';
import { executeThunk } from '../utils';
import CourseUnit from './CourseUnit';
import headerNavigationsMessages from './header-navigations/messages';
import headerTitleMessages from './header-title/messages';
import { getUnitPreviewPath, getUnitViewLivePath } from './utils';
import messages from './add-component/messages';

let axiosMock;
let store;
Expand All @@ -32,10 +38,12 @@ const sectionId = 'graded_interactions';
const subsectionId = '19a30717eff543078a5d94ae9d6c18a5';
const blockId = '567890';
const unitDisplayName = courseUnitIndexMock.metadata.display_name;
const mockedUsedNavigate = jest.fn();

jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useParams: () => ({ blockId }),
useNavigate: () => mockedUsedNavigate,
}));

const RootWrapper = () => (
Expand Down Expand Up @@ -63,6 +71,10 @@ describe('<CourseUnit />', () => {
.onGet(getCourseUnitApiUrl(courseId))
.reply(200, courseUnitIndexMock);
await executeThunk(fetchCourseUnitQuery(courseId), store.dispatch);
axiosMock
.onGet(getCourseSectionVerticalApiUrl(blockId))
.reply(200, courseSectionVerticalMock);
await executeThunk(fetchCourseSectionVerticalData(blockId), store.dispatch);
});

it('render CourseUnit component correctly', async () => {
Expand Down Expand Up @@ -146,4 +158,57 @@ describe('<CourseUnit />', () => {
expect(titleEditField).not.toBeInTheDocument();
expect(await findByText(newDisplayName)).toBeInTheDocument();
});

it('doesn\'t handle creating xblock and displays an error message', async () => {
const { courseKey, locator } = courseCreateXblockMock;
axiosMock
.onPost(postXBlockBaseApiUrl({ type: 'video', category: 'video', parentLocator: blockId }))
.reply(500, {});
const { getByRole } = render(<RootWrapper />);

await waitFor(() => {
const videoButton = getByRole('button', {
name: new RegExp(`${messages.buttonText.defaultMessage} Video`, 'i'),
});

userEvent.click(videoButton);
expect(mockedUsedNavigate).not.toHaveBeenCalledWith(`/course/${courseKey}/editor/video/${locator}`);
});
});

it('handle creating Problem xblock and navigate to editor page', async () => {
const { courseKey, locator } = courseCreateXblockMock;
axiosMock
.onPost(postXBlockBaseApiUrl({ type: 'problem', category: 'problem', parentLocator: blockId }))
.reply(200, courseCreateXblockMock);
const { getByRole } = render(<RootWrapper />);

await waitFor(() => {
const problemButton = getByRole('button', {
name: new RegExp(`${messages.buttonText.defaultMessage} Problem`, 'i'),
});

userEvent.click(problemButton);
expect(mockedUsedNavigate).toHaveBeenCalled();
expect(mockedUsedNavigate).toHaveBeenCalledWith(`/course/${courseKey}/editor/problem/${locator}`);
});
});

it('handles creating Video xblock and navigates to editor page', async () => {
const { courseKey, locator } = courseCreateXblockMock;
axiosMock
.onPost(postXBlockBaseApiUrl({ type: 'video', category: 'video', parentLocator: blockId }))
.reply(200, courseCreateXblockMock);
const { getByRole } = render(<RootWrapper />);

await waitFor(() => {
const videoButton = getByRole('button', {
name: new RegExp(`${messages.buttonText.defaultMessage} Video`, 'i'),
});

userEvent.click(videoButton);
expect(mockedUsedNavigate).toHaveBeenCalled();
expect(mockedUsedNavigate).toHaveBeenCalledWith(`/course/${courseKey}/editor/video/${locator}`);
});
});
});
4 changes: 4 additions & 0 deletions src/course-unit/__mocks__/courseCreateXblock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
locator: 'block-v1:edX+L153+3T2023+type@drag-and-drop-v2+block@dc52e3cf8e6145e39ba5c1ff4888db4b',
courseKey: 'course-v1:edX+L153+3T2023',
};
Loading

0 comments on commit 1555e9f

Please sign in to comment.