Skip to content

Commit

Permalink
feat: [FC-0070] rendering library content in unit page
Browse files Browse the repository at this point in the history
  • Loading branch information
ihor-romaniuk committed Nov 25, 2024
1 parent bc8d59b commit 26c3235
Show file tree
Hide file tree
Showing 21 changed files with 259 additions and 120 deletions.
1 change: 1 addition & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const COURSE_BLOCK_NAMES = ({
chapter: { id: 'chapter', name: 'Section' },
sequential: { id: 'sequential', name: 'Subsection' },
vertical: { id: 'vertical', name: 'Unit' },
libraryContent: { id: 'library_content', name: 'Library content' },
component: { id: 'component', name: 'Component' },
});

Expand Down
84 changes: 52 additions & 32 deletions src/course-unit/CourseUnit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import ProcessingNotification from '../generic/processing-notification';
import { SavingErrorAlert } from '../generic/saving-error-alert';
import ConnectionErrorAlert from '../generic/ConnectionErrorAlert';
import Loading from '../generic/Loading';
import { COURSE_BLOCK_NAMES } from '../constants';
import AddComponent from './add-component/AddComponent';
import HeaderTitle from './header-title/HeaderTitle';
import Breadcrumbs from './breadcrumbs/Breadcrumbs';
Expand All @@ -44,6 +45,7 @@ const CourseUnit = ({ courseId }) => {
isLoading,
sequenceId,
unitTitle,
unitCategory,
errorMessage,
sequenceStatus,
savingStatus,
Expand All @@ -70,6 +72,19 @@ const CourseUnit = ({ courseId }) => {
handleNavigateToTargetUnit,
} = useCourseUnit({ courseId, blockId });

const isUnitVerticalType = unitCategory === COURSE_BLOCK_NAMES.vertical.id;
const isUnitLibraryType = unitCategory === COURSE_BLOCK_NAMES.libraryContent.id;

const unitLayout = [{ span: 12 }, { span: 0 }];
const defaultLayout = {
lg: [{ span: 8 }, { span: 4 }],
md: [{ span: 8 }, { span: 4 }],
sm: [{ span: 8 }, { span: 3 }],
xs: [{ span: 9 }, { span: 3 }],
xl: [{ span: 9 }, { span: 3 }],
};
const layoutGrid = isUnitLibraryType ? { lg: unitLayout } : defaultLayout;

useEffect(() => {
document.title = getPageHeadTitle('', unitTitle);
}, [unitTitle]);
Expand Down Expand Up @@ -141,30 +156,30 @@ const CourseUnit = ({ courseId }) => {
/>
)}
breadcrumbs={(
<Breadcrumbs />
<Breadcrumbs
courseId={courseId}
sequenceId={sequenceId}
/>
)}
headerActions={(
<HeaderNavigations
unitCategory={unitCategory}
headerNavigationsActions={headerNavigationsActions}
/>
)}
/>
<Sequence
courseId={courseId}
sequenceId={sequenceId}
unitId={blockId}
handleCreateNewCourseXBlock={handleCreateNewCourseXBlock}
showPasteUnit={showPasteUnit}
/>
<Layout
lg={[{ span: 8 }, { span: 4 }]}
md={[{ span: 8 }, { span: 4 }]}
sm={[{ span: 8 }, { span: 3 }]}
xs={[{ span: 9 }, { span: 3 }]}
xl={[{ span: 9 }, { span: 3 }]}
>
{isUnitVerticalType && (
<Sequence
courseId={courseId}
sequenceId={sequenceId}
unitId={blockId}
handleCreateNewCourseXBlock={handleCreateNewCourseXBlock}
showPasteUnit={showPasteUnit}
/>
)}
<Layout {...layoutGrid}>
<Layout.Element>
{currentlyVisibleToStudents && (
{!currentlyVisibleToStudents && (
<AlertMessage
className="course-unit__alert"
title={intl.formatMessage(messages.alertUnpublishedVersion)}
Expand All @@ -183,11 +198,13 @@ const CourseUnit = ({ courseId }) => {
unitXBlockActions={unitXBlockActions}
courseVerticalChildren={courseVerticalChildren.children}
/>
<AddComponent
blockId={blockId}
handleCreateNewCourseXBlock={handleCreateNewCourseXBlock}
/>
{showPasteXBlock && canPasteComponent && (
{isUnitVerticalType && (
<AddComponent
blockId={blockId}
handleCreateNewCourseXBlock={handleCreateNewCourseXBlock}
/>
)}
{showPasteXBlock && canPasteComponent && isUnitVerticalType && (
<PasteComponent
clipboardData={sharedClipboardData}
onClick={handleCreateNewCourseXBlock}
Expand All @@ -203,18 +220,21 @@ const CourseUnit = ({ courseId }) => {
</Layout.Element>
<Layout.Element>
<Stack gap={3}>
<Sidebar data-testid="course-unit-sidebar">
<PublishControls blockId={blockId} />
</Sidebar>
{getConfig().ENABLE_TAGGING_TAXONOMY_PAGES === 'true'
&& (
<Sidebar className="tags-sidebar">
<TagsSidebarControls />
</Sidebar>
{isUnitVerticalType && (
<>
<Sidebar data-testid="course-unit-sidebar">
<PublishControls blockId={blockId} />
</Sidebar>
{getConfig().ENABLE_TAGGING_TAXONOMY_PAGES === 'true' && (
<Sidebar className="tags-sidebar">
<TagsSidebarControls />
</Sidebar>
)}
<Sidebar data-testid="course-unit-location-sidebar">
<LocationInfo />
</Sidebar>
</>
)}
<Sidebar data-testid="course-unit-location-sidebar">
<LocationInfo />
</Sidebar>
</Stack>
</Layout.Element>
</Layout>
Expand Down
4 changes: 4 additions & 0 deletions src/course-unit/CourseUnit.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
@import "./header-title/HeaderTitle";
@import "./move-modal";

.course-unit {
min-width: 900px;
}

.course-unit__alert {
margin-bottom: 1.75rem;
}
2 changes: 1 addition & 1 deletion src/course-unit/CourseUnit.test.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import MockAdapter from 'axios-mock-adapter';
import {
act, render, waitFor, within, screen,
act, render, waitFor, within,
} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { IntlProvider } from '@edx/frontend-platform/i18n';
Expand Down
2 changes: 1 addition & 1 deletion src/course-unit/add-component/AddComponent.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const AddComponent = ({ blockId, handleCreateNewCourseXBlock }) => {
const [isOpenAdvanced, openAdvanced, closeAdvanced] = useToggle(false);
const [isOpenHtml, openHtml, closeHtml] = useToggle(false);
const [isOpenOpenAssessment, openOpenAssessment, closeOpenAssessment] = useToggle(false);
const { componentTemplates } = useSelector(getCourseSectionVertical);
const { componentTemplates = {} } = useSelector(getCourseSectionVertical);
const [isAddLibraryContentModalOpen, showAddLibraryContentModal, closeAddLibraryContentModal] = useToggle();

const handleLibraryV2Selection = (selection) => {
Expand Down
113 changes: 58 additions & 55 deletions src/course-unit/breadcrumbs/Breadcrumbs.jsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Dropdown, Icon } from '@openedx/paragon';
Expand All @@ -10,77 +11,79 @@ import { getConfig } from '@edx/frontend-platform';

import { getWaffleFlags } from '../../data/selectors';
import { getCourseSectionVertical } from '../data/selectors';
import { adoptCourseSectionUrl } from '../utils';
import messages from './messages';

const Breadcrumbs = () => {
const Breadcrumbs = ({ courseId, sequenceId }) => {
const intl = useIntl();
const { ancestorXblocks } = useSelector(getCourseSectionVertical);
const [section, subsection] = ancestorXblocks ?? [];
const { ancestorXblocks = [] } = useSelector(getCourseSectionVertical);
const waffleFlags = useSelector(getWaffleFlags);

const getPathToCourseOutlinePage = (url) => (waffleFlags.useNewCourseOutlinePage
? url : `${getConfig().STUDIO_BASE_URL}${url}`);

const getPathToCourseUnitPage = (url) => (waffleFlags.useNewUnitPage
? adoptCourseSectionUrl({ url, courseId, sequenceId })
: `${getConfig().STUDIO_BASE_URL}${url}`);

const getPathToCoursePage = (isOutlinePage, url) => (
isOutlinePage ? getPathToCourseOutlinePage(url) : getPathToCourseUnitPage(url)
);

return (
<nav className="d-flex align-center mb-2.5">
<ol className="p-0 m-0 d-flex align-center">
<li className="d-flex">
<Dropdown>
<Dropdown.Toggle id="breadcrumbs-dropdown-section" variant="link" className="p-0 text-primary small">
<span className="small text-gray-700">{section.title}</span>
{ancestorXblocks.map(({ children, title, isLast }, index) => (
<li
className="d-flex"
key={title}
>
<Dropdown>
<Dropdown.Toggle
id="breadcrumbs-dropdown-section"
variant="link"
className="p-0 text-primary small"
>
<span className="small text-gray-700">
{title}
</span>
<Icon
src={ArrowDropDownIcon}
className="text-primary ml-1"
/>
</Dropdown.Toggle>
<Dropdown.Menu>
{children.map(({ url, displayName }) => (
<Dropdown.Item
as={Link}
key={url}
to={getPathToCoursePage(index < 2, url)}
className="small"
data-testid="breadcrumbs-section-dropdown-item"
>
{displayName}
</Dropdown.Item>
))}
</Dropdown.Menu>
</Dropdown>
{!isLast && (
<Icon
src={ArrowDropDownIcon}
className="text-primary ml-1"
src={ChevronRightIcon}
size="md"
className="text-primary mx-2"
alt={intl.formatMessage(messages.altIconChevron)}
/>
</Dropdown.Toggle>
<Dropdown.Menu>
{section.children.map(({ url, displayName }) => (
<Dropdown.Item
as={Link}
key={url}
to={getPathToCourseOutlinePage(url)}
className="small"
data-testid="breadcrumbs-section-dropdown-item"
>
{displayName}
</Dropdown.Item>
))}
</Dropdown.Menu>
</Dropdown>
<Icon
src={ChevronRightIcon}
size="md"
className="text-primary mx-2"
alt={intl.formatMessage(messages.altIconChevron)}
/>
</li>
<li className="d-flex">
<Dropdown>
<Dropdown.Toggle id="breadcrumbs-dropdown-subsection" variant="link" className="p-0 text-primary">
<span className="small text-gray-700">{subsection.title}</span>
<Icon
src={ArrowDropDownIcon}
className="text-primary ml-1"
/>
</Dropdown.Toggle>
<Dropdown.Menu>
{subsection.children.map(({ url, displayName }) => (
<Dropdown.Item
as={Link}
key={url}
to={getPathToCourseOutlinePage(url)}
className="small"
data-testid="breadcrumbs-subsection-dropdown-item"
>
{displayName}
</Dropdown.Item>
))}
</Dropdown.Menu>
</Dropdown>
</li>
)}
</li>
))}
</ol>
</nav>
);
};

Breadcrumbs.propTypes = {
courseId: PropTypes.string.isRequired,
sequenceId: PropTypes.string.isRequired,
};

export default Breadcrumbs;
1 change: 1 addition & 0 deletions src/course-unit/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export const messageTypes = {
videoFullScreen: 'plugin.videoFullScreen',
refreshXBlock: 'refreshXBlock',
showMoveXBlockModal: 'showMoveXBlockModal',
handleViewXBlockContent: 'handleViewXBlockContent',
};

export const IFRAME_FEATURE_POLICY = (
Expand Down
2 changes: 1 addition & 1 deletion src/course-unit/context/iFrameContext.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {
import React, {
createContext, MutableRefObject, useRef, useCallback, useMemo, ReactNode,
} from 'react';
import { logError } from '@edx/frontend-platform/logging';
Expand Down
1 change: 1 addition & 0 deletions src/course-unit/data/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export async function createCourseXblock({
* @param {string} type - The action type (e.g., PUBLISH_TYPES.discardChanges).
* @param {boolean} isVisible - The visibility status for students.
* @param {boolean} groupAccess - Access group key set.
* @param {boolean} isDiscussionEnabled - Indicates whether the discussion feature is enabled.
* @returns {Promise<any>} A promise that resolves with the response data.
*/
export async function handleCourseUnitVisibilityAndData(unitId, type, isVisible, groupAccess, isDiscussionEnabled) {
Expand Down
4 changes: 2 additions & 2 deletions src/course-unit/data/thunk.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export function fetchCourseSectionVerticalData(courseId, sequenceId) {
}));
dispatch(updateModels({
modelType: 'units',
models: courseSectionVerticalData.units,
models: courseSectionVerticalData.units || [],
}));
dispatch(fetchStaticFileNoticesSuccess(JSON.parse(localStorage.getItem('staticFileNotices'))));
localStorage.removeItem('staticFileNotices');
Expand Down Expand Up @@ -107,7 +107,7 @@ export function editCourseItemQuery(itemId, displayName, sequenceId) {
}));
dispatch(updateModels({
modelType: 'units',
models: courseSectionVerticalData.units,
models: courseSectionVerticalData.units || [],
}));
dispatch(fetchSequenceSuccess({ sequenceId }));
dispatch(fetchCourseItemSuccess(courseUnit));
Expand Down
4 changes: 2 additions & 2 deletions src/course-unit/data/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ export function normalizeCourseSectionVerticalData(metadata) {
sequence: {
id: data.subsectionLocation,
title: data.xblock.displayName,
unitIds: data.xblockInfo.ancestorInfo.ancestors[0].childInfo.children.map((item) => item.id),
unitIds: data.xblockInfo.ancestorInfo?.ancestors[0].childInfo.children.map((item) => item.id),
},
units: data.xblockInfo.ancestorInfo.ancestors[0].childInfo.children.map((unit) => ({
units: data.xblockInfo.ancestorInfo?.ancestors[0].childInfo.children.map((unit) => ({
id: unit.id,
sequenceId: data.subsectionLocation,
bookmarked: unit.bookmarked,
Expand Down
Loading

0 comments on commit 26c3235

Please sign in to comment.