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-0070] Manage Tags interoperation #1454

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
4 changes: 2 additions & 2 deletions src/CourseAuthoringRoutes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import ScheduleAndDetails from './schedule-and-details';
import { GradingSettings } from './grading-settings';
import CourseTeam from './course-team/CourseTeam';
import { CourseUpdates } from './course-updates';
import { CourseUnit } from './course-unit';
import { CourseUnit, IframeProvider } from './course-unit';
import { Certificates } from './certificates';
import CourseExportPage from './export-page/CourseExportPage';
import CourseImportPage from './import-page/CourseImportPage';
Expand Down Expand Up @@ -79,7 +79,7 @@ const CourseAuthoringRoutes = () => {
<Route
key={path}
path={path}
element={<PageWrap><CourseUnit courseId={courseId} /></PageWrap>}
element={<PageWrap><IframeProvider><CourseUnit courseId={courseId} /></IframeProvider></PageWrap>}
/>
))}
<Route
Expand Down
2 changes: 2 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export const NOTIFICATION_MESSAGES = {
copying: 'Copying',
pasting: 'Pasting',
discardChanges: 'Discarding changes',
moving: 'Moving',
undoMoving: 'Undo moving',
publishing: 'Publishing',
hidingFromStudents: 'Hiding from students',
makingVisibleToStudents: 'Making visible to students',
Expand Down
37 changes: 33 additions & 4 deletions src/content-tags-drawer/ContentTagsDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@
import { useIntl, FormattedMessage } from '@edx/frontend-platform/i18n';
import { useParams, useNavigate } from 'react-router-dom';
import classNames from 'classnames';
import { getConfig } from '@edx/frontend-platform';

import { messageTypes } from '../course-unit/constants';
import { useIframe } from '../course-unit/context/hooks';
import messages from './messages';
import ContentTagsCollapsible from './ContentTagsCollapsible';
import Loading from '../generic/Loading';
Expand Down Expand Up @@ -113,6 +117,33 @@
toEditMode,
} = useContext(ContentTagsDrawerContext);

// TODO: Replace with functionality for waffle flag tracking to switch between new React pages.
// Conditional usage of the useIframe hook will also be based on waffle flag tracking.
// https://github.com/openedx/frontend-app-authoring/pull/1372
const isNewUnitPage = getConfig().ENABLE_UNIT_PAGE === 'true';

// Define a type with sendMessageToIframe as an optional function
type IframeFunctionType = { sendMessageToIframe?: (message: string, data: any) => void };

// The useIframe hook is called here due to embedding tagging functionality
// into a legacy page that does not have a context provider for iframe messaging.
// eslint-disable-next-line react-hooks/rules-of-hooks
const iframeFunction: IframeFunctionType = isNewUnitPage ? useIframe() : {};

const handleClick = () => {
if (isEditMode) {
if (isNewUnitPage) {
// TODO: this artificial delay is a temporary solution
// to ensure the iframe content is properly refreshed.
setTimeout(() => {

Check warning on line 138 in src/content-tags-drawer/ContentTagsDrawer.tsx

View check run for this annotation

Codecov / codecov/patch

src/content-tags-drawer/ContentTagsDrawer.tsx#L138

Added line #L138 was not covered by tests
iframeFunction.sendMessageToIframe?.(messageTypes.refreshXBlock, null);
}, 1000);
}
return commitGlobalStagedTags();
}
return toEditMode();
};

return (
<Container
className="bg-white position-sticky p-3.5 box-shadow-up-2 tags-drawer-footer"
Expand All @@ -134,9 +165,7 @@
{!readOnly && (
<Button
className="rounded-0"
onClick={isEditMode
? commitGlobalStagedTags
: toEditMode}
onClick={handleClick}
>
{ intl.formatMessage(isEditMode
? messages.tagsDrawerSaveButtonText
Expand Down Expand Up @@ -216,7 +245,7 @@
};

interface ContentTagsDrawerProps {
id?: string;
id?: string | null;
onClose?: () => void;
variant?: 'drawer' | 'component';
readOnly?: boolean;
Expand Down
115 changes: 64 additions & 51 deletions src/course-unit/CourseUnit.jsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { useEffect, useMemo, useState } from 'react';
import { useEffect } from 'react';
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, TransitionReplace,
} from '@openedx/paragon';
import { getConfig } from '@edx/frontend-platform';
import { useIntl, injectIntl } from '@edx/frontend-platform/i18n';
import { Warning as WarningIcon } from '@openedx/paragon/icons';
import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
import {
Warning as WarningIcon,
CheckCircle as CheckCircleIcon,
} from '@openedx/paragon/icons';

import DraggableList from '../editors/sharedComponents/DraggableList';
import { getProcessingNotification } from '../generic/processing-notification/data/selectors';
import SubHeader from '../generic/sub-header/SubHeader';
import { RequestStatus } from '../data/constants';
Expand All @@ -20,7 +23,6 @@
import ConnectionErrorAlert from '../generic/ConnectionErrorAlert';
import Loading from '../generic/Loading';
import AddComponent from './add-component/AddComponent';
import CourseXBlock from './course-xblock/CourseXBlock';
import HeaderTitle from './header-title/HeaderTitle';
import Breadcrumbs from './breadcrumbs/Breadcrumbs';
import HeaderNavigations from './header-navigations/HeaderNavigations';
Expand All @@ -32,6 +34,8 @@
import LocationInfo from './sidebar/LocationInfo';
import TagsSidebarControls from '../content-tags-drawer/tags-sidebar-controls';
import { PasteNotificationAlert } from './clipboard';
import XBlockContainerIframe from './xblock-container-iframe';
import MoveModal from './move-modal';

const CourseUnit = ({ courseId }) => {
const { blockId } = useParams();
Expand All @@ -56,21 +60,20 @@
handleCreateNewCourseXBlock,
handleConfigureSubmit,
courseVerticalChildren,
handleXBlockDragAndDrop,
canPasteComponent,
isMoveModalOpen,
openMoveModal,
closeMoveModal,
movedXBlockParams,
handleRollbackMovedXBlock,
handleCloseXBlockMovedAlert,
handleNavigateToTargetUnit,
} = useCourseUnit({ courseId, blockId });

const initialXBlocksData = useMemo(() => courseVerticalChildren.children ?? [], [courseVerticalChildren.children]);
const [unitXBlocks, setUnitXBlocks] = useState(initialXBlocksData);

useEffect(() => {
document.title = getPageHeadTitle('', unitTitle);
}, [unitTitle]);

useEffect(() => {
setUnitXBlocks(courseVerticalChildren.children);
}, [courseVerticalChildren.children]);

const {
isShow: isShowProcessingNotification,
title: processingNotificationTitle,
Expand All @@ -88,16 +91,44 @@
);
}

const finalizeXBlockOrder = () => (newXBlocks) => {
handleXBlockDragAndDrop(newXBlocks.map(xBlock => xBlock.id), () => {
setUnitXBlocks(initialXBlocksData);
});
};

return (
<>
<Container size="xl" className="course-unit px-4">
<section className="course-unit-container mb-4 mt-5">
<TransitionReplace>
{movedXBlockParams.isSuccess ? (
<AlertMessage

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

View check run for this annotation

Codecov / codecov/patch

src/course-unit/CourseUnit.jsx#L100

Added line #L100 was not covered by tests
key="xblock-moved-alert"
data-testid="xblock-moved-alert"
show={movedXBlockParams.isSuccess}
variant="success"
icon={CheckCircleIcon}
title={movedXBlockParams.isUndo
? intl.formatMessage(messages.alertMoveCancelTitle)
: intl.formatMessage(messages.alertMoveSuccessTitle)}

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

View check run for this annotation

Codecov / codecov/patch

src/course-unit/CourseUnit.jsx#L107-L108

Added lines #L107 - L108 were not covered by tests
description={movedXBlockParams.isUndo
? intl.formatMessage(messages.alertMoveCancelDescription, { title: movedXBlockParams.title })
: intl.formatMessage(messages.alertMoveSuccessDescription, { title: movedXBlockParams.title })}

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

View check run for this annotation

Codecov / codecov/patch

src/course-unit/CourseUnit.jsx#L110-L111

Added lines #L110 - L111 were not covered by tests
aria-hidden={movedXBlockParams.isSuccess}
dismissible
actions={movedXBlockParams.isUndo ? null : [
<Button
onClick={handleRollbackMovedXBlock}
key="xblock-moved-alert-undo-move-button"
>
{intl.formatMessage(messages.undoMoveButton)}
</Button>,
<Button
onClick={handleNavigateToTargetUnit}
key="xblock-moved-alert-new-location-button"
>
{intl.formatMessage(messages.newLocationButton)}
</Button>,
]}
onClose={handleCloseXBlockMovedAlert}
/>
) : null}
</TransitionReplace>
<SubHeader
hideBorder
title={(
Expand Down Expand Up @@ -147,37 +178,13 @@
courseId={courseId}
/>
)}
<Stack className="mb-4 course-unit__xblocks">
<DraggableList
itemList={unitXBlocks}
setState={setUnitXBlocks}
updateOrder={finalizeXBlockOrder}
>
<SortableContext
id="root"
items={unitXBlocks}
strategy={verticalListSortingStrategy}
>
{unitXBlocks.map(({
name, id, blockType: type, shouldScroll, userPartitionInfo, validationMessages,
}) => (
<CourseXBlock
id={id}
key={id}
title={name}
type={type}
blockId={blockId}
validationMessages={validationMessages}
shouldScroll={shouldScroll}
handleConfigureSubmit={handleConfigureSubmit}
unitXBlockActions={unitXBlockActions}
data-testid="course-xblock"
userPartitionInfo={userPartitionInfo}
/>
))}
</SortableContext>
</DraggableList>
</Stack>
<XBlockContainerIframe
courseId={courseId}
blockId={blockId}
unitXBlockActions={unitXBlockActions}
xblocks={courseVerticalChildren.children}
handleConfigureSubmit={handleConfigureSubmit}
/>
<AddComponent
blockId={blockId}
handleCreateNewCourseXBlock={handleCreateNewCourseXBlock}
Expand All @@ -189,6 +196,12 @@
text={intl.formatMessage(messages.pasteButtonText)}
/>
)}
<MoveModal
isOpenModal={isMoveModalOpen}
openModal={openMoveModal}
closeModal={closeMoveModal}
courseId={courseId}
/>
</Layout.Element>
<Layout.Element>
<Stack gap={3}>
Expand Down
2 changes: 1 addition & 1 deletion src/course-unit/CourseUnit.scss
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
@import "./breadcrumbs/Breadcrumbs";
@import "./course-sequence/CourseSequence";
@import "./add-component/AddComponent";
@import "./course-xblock/CourseXBlock";
@import "./sidebar/Sidebar";
@import "./header-title/HeaderTitle";
@import "./move-modal";

.course-unit__alert {
margin-bottom: 1.75rem;
Expand Down
Loading