Skip to content

Commit

Permalink
feat: [FC-0044] Unit page - Manage access modal (unit & xblocks)
Browse files Browse the repository at this point in the history
  • Loading branch information
ihor-romaniuk committed Mar 19, 2024
1 parent 972a7f3 commit a5476d1
Show file tree
Hide file tree
Showing 35 changed files with 1,218 additions and 335 deletions.
7 changes: 7 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,10 @@ export const DECODED_ROUTES = {
'/container/:blockId',
],
};

export const COURSE_BLOCK_NAMES = /** @type {const} */ ({
chapter: { id: 'chapter', name: 'Section' },
sequential: { id: 'sequential', name: 'Subsection' },
vertical: { id: 'vertical', name: 'Unit' },
component: { id: 'component', name: 'Component' },
});
12 changes: 8 additions & 4 deletions src/course-outline/CourseOutline.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,10 @@ import SubHeader from '../generic/sub-header/SubHeader';
import ProcessingNotification from '../generic/processing-notification';
import InternetConnectionAlert from '../generic/internet-connection-alert';
import DeleteModal from '../generic/delete-modal/DeleteModal';
import ConfigureModal from '../generic/configure-modal/ConfigureModal';
import AlertMessage from '../generic/alert-message';
import getPageHeadTitle from '../generic/utils';
import { getCurrentItem } from './data/selectors';
import { getCurrentItem, getProctoredExamsFlag } from './data/selectors';
import { COURSE_BLOCK_NAMES } from './constants';
import HeaderNavigations from './header-navigations/HeaderNavigations';
import OutlineSideBar from './outline-sidebar/OutlineSidebar';
Expand All @@ -39,7 +40,6 @@ import UnitCard from './unit-card/UnitCard';
import HighlightsModal from './highlights-modal/HighlightsModal';
import EmptyPlaceholder from './empty-placeholder/EmptyPlaceholder';
import PublishModal from './publish-modal/PublishModal';
import ConfigureModal from './configure-modal/ConfigureModal';
import PageAlerts from './page-alerts/PageAlerts';
import { useCourseOutline } from './hooks';
import messages from './messages';
Expand Down Expand Up @@ -118,8 +118,10 @@ const CourseOutline = ({ courseId }) => {
title: processingNotificationTitle,
} = useSelector(getProcessingNotification);

const { category } = useSelector(getCurrentItem);
const deleteCategory = COURSE_BLOCK_NAMES[category]?.name.toLowerCase();
const currentItemData = useSelector(getCurrentItem);
const deleteCategory = COURSE_BLOCK_NAMES[currentItemData.category]?.name.toLowerCase();

const enableProctoredExams = useSelector(getProctoredExamsFlag);

const finalizeSectionOrder = () => (newSections) => {
initialSections = [...sectionsList];
Expand Down Expand Up @@ -485,6 +487,8 @@ const CourseOutline = ({ courseId }) => {
isOpen={isConfigureModalOpen}
onClose={handleConfigureModalClose}
onConfigureSubmit={handleConfigureItemSubmit}
currentItemData={currentItemData}
enableProctoredExams={enableProctoredExams}
/>
<DeleteModal
category={deleteCategory}
Expand Down
1 change: 0 additions & 1 deletion src/course-outline/CourseOutline.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
@import "./empty-placeholder/EmptyPlaceholder";
@import "./highlights-modal/HighlightsModal";
@import "./publish-modal/PublishModal";
@import "./configure-modal/ConfigureModal";
@import "./drag-helper/ConditionalSortableElement";
@import "./xblock-status/XBlockStatus";
@import "./paste-button/PasteButton";
5 changes: 3 additions & 2 deletions src/course-outline/CourseOutline.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,16 @@ import {
import { executeThunk } from '../utils';
import { COURSE_BLOCK_NAMES, VIDEO_SHARING_OPTIONS } from './constants';
import CourseOutline from './CourseOutline';
import messages from './messages';

import configureModalMessages from '../generic/configure-modal/messages';
import headerMessages from './header-navigations/messages';
import cardHeaderMessages from './card-header/messages';
import enableHighlightsModalMessages from './enable-highlights-modal/messages';
import statusBarMessages from './status-bar/messages';
import configureModalMessages from './configure-modal/messages';
import pasteButtonMessages from './paste-button/messages';
import subsectionMessages from './subsection-card/messages';
import pageAlertMessages from './page-alerts/messages';
import messages from './messages';

let axiosMock;
let store;
Expand Down
10 changes: 9 additions & 1 deletion src/course-unit/CourseUnit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const CourseUnit = ({ courseId }) => {
handleTitleEdit,
handleInternetConnectionFailed,
handleCreateNewCourseXBlock,
handleConfigureSubmit,
courseVerticalChildren,
} = useCourseUnit({ courseId, blockId });

Expand Down Expand Up @@ -85,6 +86,7 @@ const CourseUnit = ({ courseId }) => {
isTitleEditFormOpen={isTitleEditFormOpen}
handleTitleEdit={handleTitleEdit}
handleTitleEditSubmit={handleTitleEditSubmit}
handleConfigureSubmit={handleConfigureSubmit}
/>
)}
breadcrumbs={(
Expand Down Expand Up @@ -118,14 +120,20 @@ const CourseUnit = ({ courseId }) => {
/>
)}
<Stack gap={4} className="mb-4">
{courseVerticalChildren.children.map(({ name, blockId: id, shouldScroll }) => (
{courseVerticalChildren.children.map(({
name, blockId: id, shouldScroll, userPartitionInfo, validationMessages,
}) => (
<CourseXBlock
id={id}
key={id}
title={name}
blockId={blockId}
validationMessages={validationMessages}
shouldScroll={shouldScroll}
unitXBlockActions={unitXBlockActions}
handleConfigureSubmit={handleConfigureSubmit}
data-testid="course-xblock"
userPartitionInfo={userPartitionInfo}
/>
))}
</Stack>
Expand Down
79 changes: 75 additions & 4 deletions src/course-unit/CourseUnit.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,13 +38,14 @@ import courseSequenceMessages from './course-sequence/messages';
import sidebarMessages from './sidebar/messages';
import { extractCourseUnitId } from './sidebar/utils';
import CourseUnit from './CourseUnit';
import messages from './messages';

import deleteModalMessages from '../generic/delete-modal/messages';
import configureModalMessages from '../generic/configure-modal/messages';
import courseXBlockMessages from './course-xblock/messages';
import addComponentMessages from './add-component/messages';
import { PUBLISH_TYPES, UNIT_VISIBILITY_STATES } from './constants';
import { getContentTaxonomyTagsApiUrl, getContentTaxonomyTagsCountApiUrl } from '../content-tags-drawer/data/api';
import messages from './messages';

let axiosMock;
let store;
Expand Down Expand Up @@ -571,6 +572,7 @@ describe('<CourseUnit />', () => {
name: 'New Cloned XBlock',
block_id: '1234567890',
block_type: 'drag-and-drop-v2',
user_partition_info: {},
},
],
});
Expand All @@ -594,7 +596,7 @@ describe('<CourseUnit />', () => {
});
});

it('should toggle visibility and update course unit state accordingly', async () => {
it('should toggle visibility from sidebar and update course unit state accordingly', async () => {
const { getByRole, getByTestId } = render(<RootWrapper />);
let courseUnitSidebar;
let draftUnpublishedChangesHeading;
Expand All @@ -617,7 +619,7 @@ describe('<CourseUnit />', () => {
axiosMock
.onPost(getXBlockBaseApiUrl(blockId), {
publish: PUBLISH_TYPES.republish,
metadata: { visible_to_staff_only: true },
metadata: { visible_to_staff_only: true, group_access: null },
})
.reply(200, { dummy: 'value' });
axiosMock
Expand Down Expand Up @@ -654,7 +656,7 @@ describe('<CourseUnit />', () => {
axiosMock
.onPost(getXBlockBaseApiUrl(blockId), {
publish: PUBLISH_TYPES.republish,
metadata: { visible_to_staff_only: null },
metadata: { visible_to_staff_only: null, group_access: null },
})
.reply(200, { dummy: 'value' });
axiosMock
Expand Down Expand Up @@ -942,4 +944,73 @@ describe('<CourseUnit />', () => {
.replace('{sectionName}', courseUnitIndexMock.release_date_from),
)).toBeInTheDocument();
});

it('should toggle visibility from header configure modal and update course unit state accordingly', async () => {
const { getByRole, getByTestId } = render(<RootWrapper />);
let courseUnitSidebar;
let sidebarVisibilityCheckbox;
let modalVisibilityCheckbox;
let configureModal;
let restrictAccessSelect;

await waitFor(() => {
courseUnitSidebar = getByTestId('course-unit-sidebar');
sidebarVisibilityCheckbox = within(courseUnitSidebar)
.getByLabelText(sidebarMessages.visibilityCheckboxTitle.defaultMessage);
expect(sidebarVisibilityCheckbox).not.toBeChecked();

const headerConfigureBtn = getByRole('button', { name: /settings/i });
expect(headerConfigureBtn).toBeInTheDocument();

userEvent.click(headerConfigureBtn);
configureModal = getByTestId('configure-modal');
restrictAccessSelect = within(configureModal)
.getByRole('combobox', { name: configureModalMessages.restrictAccessTo.defaultMessage });
expect(within(configureModal)
.getByText(configureModalMessages.unitVisibility.defaultMessage)).toBeInTheDocument();
expect(within(configureModal)
.getByText(configureModalMessages.restrictAccessTo.defaultMessage)).toBeInTheDocument();
expect(restrictAccessSelect).toBeInTheDocument();
expect(restrictAccessSelect).toHaveValue('-1');

modalVisibilityCheckbox = within(configureModal)
.getByRole('checkbox', { name: configureModalMessages.hideFromLearners.defaultMessage });
expect(modalVisibilityCheckbox).not.toBeChecked();

userEvent.click(modalVisibilityCheckbox);
expect(modalVisibilityCheckbox).toBeChecked();

userEvent.selectOptions(restrictAccessSelect, '0');
const [, group1Checkbox] = within(configureModal).getAllByRole('checkbox');

userEvent.click(group1Checkbox);
expect(group1Checkbox).toBeChecked();
});

axiosMock
.onPost(getXBlockBaseApiUrl(courseUnitIndexMock.id), {
publish: null,
metadata: { visible_to_staff_only: true, group_access: { 50: [2] } },
})
.reply(200, { dummy: 'value' });
axiosMock
.onGet(getCourseUnitApiUrl(blockId))
.replyOnce(200, {
...courseUnitIndexMock,
visibility_state: UNIT_VISIBILITY_STATES.staffOnly,
has_explicit_staff_lock: true,
});

const modalSaveBtn = within(configureModal)
.getByRole('button', { name: configureModalMessages.saveButton.defaultMessage });
userEvent.click(modalSaveBtn);

await waitFor(() => {
expect(sidebarVisibilityCheckbox).toBeChecked();
expect(within(courseUnitSidebar)
.getByText(sidebarMessages.sidebarTitleVisibleToStaffOnly.defaultMessage)).toBeInTheDocument();
expect(within(courseUnitSidebar)
.getByText(sidebarMessages.visibilityStaffOnlyTitle.defaultMessage)).toBeInTheDocument();
});
});
});
136 changes: 134 additions & 2 deletions src/course-unit/__mocks__/courseVerticalChildren.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,146 @@ module.exports = {
children: [
{
name: 'Discussion',
block_id: 'block-v1:OpenedX+L153+3T2023+type@discussion+block@fecd20842dd24f50bdc06643e791b013',
block_id: 'block-v1:OpenedX+L153+3T2023+type@discussion+block@5a28279f24344723a96b1268d3b7cfc0',
block_type: 'discussion',
actions: {
can_copy: true,
can_duplicate: true,
can_move: true,
can_manage_access: true,
can_delete: true,
},
user_partition_info: {
selectable_partitions: [
{
id: 970807507,
name: 'Content Groups',
scheme: 'cohort',
groups: [
{
id: 1959537066,
name: 'Group 1',
selected: false,
deleted: false,
},
{
id: 108068059,
name: 'Group 2',
selected: false,
deleted: false,
},
],
},
],
selected_partition_index: -1,
selected_groups_label: '',
},
user_partitions: [
{
id: 970807507,
name: 'Content Groups',
scheme: 'cohort',
groups: [
{
id: 1959537066,
name: 'Group 1',
selected: false,
deleted: false,
},
{
id: 108068059,
name: 'Group 2',
selected: false,
deleted: false,
},
],
},
{
id: 50,
name: 'Enrollment Track Groups',
scheme: 'enrollment_track',
groups: [
{
id: 1,
name: 'Audit',
selected: false,
deleted: false,
},
],
},
],
},
{
name: 'Drag and Drop',
block_id: 'block-v1:OpenedX+L153+3T2023+type@drag-and-drop-v2+block@b33cf1f6df4c41639659bc91132eeb02',
block_type: 'drag-and-drop-v2',
actions: {
can_copy: true,
can_duplicate: true,
can_move: true,
can_manage_access: true,
can_delete: true,
},
user_partition_info: {
selectable_partitions: [
{
id: 970807507,
name: 'Content Groups',
scheme: 'cohort',
groups: [
{
id: 1959537066,
name: 'Group 1',
selected: false,
deleted: false,
},
{
id: 108068059,
name: 'Group 2',
selected: false,
deleted: false,
},
],
},
],
selected_partition_index: -1,
selected_groups_label: '',
},
user_partitions: [
{
id: 970807507,
name: 'Content Groups',
scheme: 'cohort',
groups: [
{
id: 1959537066,
name: 'Group 1',
selected: false,
deleted: false,
},
{
id: 108068059,
name: 'Group 2',
selected: false,
deleted: false,
},
],
},
{
id: 50,
name: 'Enrollment Track Groups',
scheme: 'enrollment_track',
groups: [
{
id: 1,
name: 'Audit',
selected: false,
deleted: false,
},
],
},
],
},
],
is_published: false,
isPublished: false,
};
Loading

0 comments on commit a5476d1

Please sign in to comment.