Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [FC-0044] Unit page - render xblock component #964

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
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
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,4 @@ INVITE_STUDENTS_EMAIL_TO=''
AI_TRANSLATIONS_BASE_URL=''
ENABLE_HOME_PAGE_COURSE_API_V2=false
ENABLE_CHECKLIST_QUALITY=''
SECURE_ORIGIN_XBLOCK_BOOTSTRAP_HTML_URL=null
1 change: 1 addition & 0 deletions .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,4 @@ INVITE_STUDENTS_EMAIL_TO="[email protected]"
AI_TRANSLATIONS_BASE_URL='http://localhost:18760'
ENABLE_HOME_PAGE_COURSE_API_V2=false
ENABLE_CHECKLIST_QUALITY=true
SECURE_ORIGIN_XBLOCK_BOOTSTRAP_HTML_URL=/xblock-bootstrap.html
1 change: 1 addition & 0 deletions .env.test
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ BBB_LEARN_MORE_URL=''
INVITE_STUDENTS_EMAIL_TO="[email protected]"
ENABLE_HOME_PAGE_COURSE_API_V2=true
ENABLE_CHECKLIST_QUALITY=true
SECURE_ORIGIN_XBLOCK_BOOTSTRAP_HTML_URL=/xblock-bootstrap.html
121 changes: 121 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
"@testing-library/user-event": "^13.2.1",
"axios": "^0.28.0",
"axios-mock-adapter": "1.22.0",
"copy-webpack-plugin": "^11.0.0",
"eslint-import-resolver-webpack": "^0.13.8",
"fetch-mock-jest": "^1.5.1",
"glob": "7.2.3",
Expand Down
36 changes: 33 additions & 3 deletions src/course-unit/CourseUnit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
import { Container, Layout, Stack } from '@openedx/paragon';
import {
Container, Layout, Stack, Button,
} from '@openedx/paragon';
import { useIntl, injectIntl } from '@edx/frontend-platform/i18n';
import { DraggableList, ErrorAlert } from '@edx/frontend-lib-content-components';
import { Warning as WarningIcon } from '@openedx/paragon/icons';
import {
Warning as WarningIcon,
ArrowDropDown as ArrowDownIcon,
ArrowDropUp as ArrowUpIcon,
} from '@openedx/paragon/icons';
import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';

import { getProcessingNotification } from '../generic/processing-notification/data/selectors';
Expand Down Expand Up @@ -60,6 +66,9 @@
courseVerticalChildren,
handleXBlockDragAndDrop,
canPasteComponent,
isXBlocksExpanded,
isXBlocksRendered,
handleExpandAll,
} = useCourseUnit({ courseId, blockId });

const initialXBlocksData = useMemo(() => courseVerticalChildren.children ?? [], [courseVerticalChildren.children]);
Expand All @@ -73,6 +82,10 @@
setUnitXBlocks(courseVerticalChildren.children);
}, [courseVerticalChildren.children]);

useEffect(() => {
window.scrollTo({ top: 0, behavior: 'smooth' });
}, []);

const {
isShow: isShowProcessingNotification,
title: processingNotificationTitle,
Expand Down Expand Up @@ -141,6 +154,7 @@
{currentlyVisibleToStudents && (
<AlertMessage
className="course-unit__alert"
data-testid="course-unit-unpublished-alert"
title={intl.formatMessage(messages.alertUnpublishedVersion)}
variant="warning"
icon={WarningIcon}
Expand All @@ -158,26 +172,42 @@
setState={setUnitXBlocks}
updateOrder={finalizeXBlockOrder}
>
{unitXBlocks.length ? (
<Button
variant="outline-primary"
iconBefore={isXBlocksExpanded ? ArrowUpIcon : ArrowDownIcon}
onClick={handleExpandAll}
>
{isXBlocksExpanded
? intl.formatMessage(messages.collapseAllButton)
: intl.formatMessage(messages.expandAllButton)}
</Button>
) : null}

Check warning on line 185 in src/course-unit/CourseUnit.jsx

View check run for this annotation

Codecov / codecov/patch

src/course-unit/CourseUnit.jsx#L185

Added line #L185 was not covered by tests
<SortableContext
id="root"
items={unitXBlocks}
strategy={verticalListSortingStrategy}
>
{unitXBlocks.map(({
name, id, blockType: type, shouldScroll, userPartitionInfo, validationMessages,
name, id, blockType: type, renderError, shouldScroll,
userPartitionInfo, validationMessages, actions,
}) => (
<CourseXBlock
id={id}
key={id}
title={name}
type={type}
renderError={renderError}
blockId={blockId}
validationMessages={validationMessages}
shouldScroll={shouldScroll}
handleConfigureSubmit={handleConfigureSubmit}
unitXBlockActions={unitXBlockActions}
data-testid="course-xblock"
userPartitionInfo={userPartitionInfo}
actions={actions}
isXBlocksExpanded={isXBlocksExpanded}
isXBlocksRendered={isXBlocksRendered}
/>
))}
</SortableContext>
Expand Down
88 changes: 85 additions & 3 deletions src/course-unit/CourseUnit.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -520,10 +520,10 @@ describe('<CourseUnit />', () => {
});

it('should display a warning alert for unpublished course unit version', async () => {
const { getByRole } = render(<RootWrapper />);
const { getByTestId } = render(<RootWrapper />);

await waitFor(() => {
const unpublishedAlert = getByRole('alert', { class: 'course-unit-unpublished-alert' });
const unpublishedAlert = getByTestId('course-unit-unpublished-alert');
expect(unpublishedAlert).toHaveTextContent(messages.alertUnpublishedVersion.defaultMessage);
expect(unpublishedAlert).toHaveClass('alert-warning');
});
Expand All @@ -542,7 +542,7 @@ describe('<CourseUnit />', () => {
await executeThunk(fetchCourseUnitQuery(courseId), store.dispatch);

await waitFor(() => {
const unpublishedAlert = queryByRole('alert', { class: 'course-unit-unpublished-alert' });
const unpublishedAlert = queryByRole('alert', { name: messages.alertUnpublishedVersion });
expect(unpublishedAlert).toBeNull();
});
});
Expand Down Expand Up @@ -594,6 +594,7 @@ describe('<CourseUnit />', () => {
block_id: '1234567890',
block_type: 'drag-and-drop-v2',
user_partition_info: {},
actions: courseVerticalChildrenMock.children[0].actions,
},
],
});
Expand Down Expand Up @@ -966,6 +967,55 @@ describe('<CourseUnit />', () => {
)).toBeInTheDocument();
});

it('should hide action buttons when their corresponding properties are set to false', async () => {
const {
getByText,
getAllByLabelText,
queryByRole,
} = render(<RootWrapper />);

const convertedXBlockActions = camelCaseObject(courseVerticalChildrenMock.children[0].actions);

const updatedXBlockActions = Object.keys(convertedXBlockActions).reduce((acc, key) => {
acc[key] = false;
return acc;
}, {});

axiosMock
.onGet(getCourseVerticalChildrenApiUrl(blockId))
.reply(200, {
children: [
{
...courseVerticalChildrenMock.children[0],
actions: updatedXBlockActions,
},
],
});

await executeThunk(fetchCourseVerticalChildrenData(blockId), store.dispatch);

await waitFor(() => {
expect(getByText(unitDisplayName)).toBeInTheDocument();
const [xblockActionBtn] = getAllByLabelText(courseXBlockMessages.blockActionsDropdownAlt.defaultMessage);
userEvent.click(xblockActionBtn);
const deleteBtn = queryByRole('button', { name: courseXBlockMessages.blockLabelButtonDelete.defaultMessage });
const duplicateBtn = queryByRole('button', { name: courseXBlockMessages.blockLabelButtonDuplicate.defaultMessage });
const moveBtn = queryByRole('button', { name: courseXBlockMessages.blockLabelButtonMove.defaultMessage });
const copyToClipboardBtn = queryByRole('button', { name: courseXBlockMessages.blockLabelButtonCopyToClipboard.defaultMessage });
const manageAccessBtn = queryByRole('button', { name: courseXBlockMessages.blockLabelButtonManageAccess.defaultMessage });
// ToDo: uncomment next lines when manage tags will be realized
// eslint-disable-next-line max-len
// const manageTagsBtn = queryByRole('button', { name: courseXBlockMessages.blockLabelButtonManageTags.defaultMessage });

expect(deleteBtn).not.toBeInTheDocument();
expect(duplicateBtn).not.toBeInTheDocument();
expect(moveBtn).not.toBeInTheDocument();
expect(copyToClipboardBtn).not.toBeInTheDocument();
expect(manageAccessBtn).not.toBeInTheDocument();
// expect(manageTagsBtn).not.toBeInTheDocument();
});
});

it('should toggle visibility from header configure modal and update course unit state accordingly', async () => {
const { getByRole, getByTestId } = render(<RootWrapper />);
let courseUnitSidebar;
Expand Down Expand Up @@ -1152,6 +1202,7 @@ describe('<CourseUnit />', () => {
selected_partition_index: -1,
selected_groups_label: '',
},
actions: courseVerticalChildrenMock.children[0].actions,
},
],
});
Expand Down Expand Up @@ -1499,4 +1550,35 @@ describe('<CourseUnit />', () => {
expect(xBlock1).toBe(xBlock2);
});
});

it('should expand xblocks when "Expand all" button is clicked', async () => {
const { getByRole, getAllByTestId } = render(<RootWrapper />);

axiosMock
.onGet(getCourseVerticalChildrenApiUrl(blockId))
.reply(200, courseVerticalChildrenMock);

await executeThunk(fetchCourseVerticalChildrenData(blockId), store.dispatch);

const expandAllXBlocksBtn = getByRole('button', { name: messages.expandAllButton.defaultMessage });
const unitXBlocks = getAllByTestId('course-xblock');

unitXBlocks.forEach((unitXBlock) => {
const unitXBlockContentSections = unitXBlock.querySelectorAll('.pgn__card-section');
expect(unitXBlockContentSections).toHaveLength(0);
});

userEvent.click(expandAllXBlocksBtn);

await waitFor(() => {
const collapseAllXBlocksBtn = getByRole('button', { name: messages.collapseAllButton.defaultMessage });
expect(collapseAllXBlocksBtn).toBeInTheDocument();

unitXBlocks.forEach((unitXBlock) => {
const unitXBlockContentSections = unitXBlock.querySelectorAll('.pgn__card-section');
// xblock content appears inside the xblock element
expect(unitXBlockContentSections.length).toBeGreaterThan(0);
});
});
});
});
2 changes: 2 additions & 0 deletions src/course-unit/__mocks__/courseVerticalChildren.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module.exports = {
can_duplicate: true,
can_move: true,
can_manage_access: true,
can_manage_tags: true,
can_delete: true,
},
user_partition_info: {
Expand Down Expand Up @@ -80,6 +81,7 @@ module.exports = {
can_duplicate: true,
can_move: true,
can_manage_access: true,
can_manage_tags: true,
can_delete: true,
},
user_partition_info: {
Expand Down
Loading
Loading