Skip to content
Open
2 changes: 1 addition & 1 deletion src/CourseAuthoringContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { useCourseDetails, useWaffleFlags } from './data/apiHooks';
import { RequestStatus, RequestStatusType } from './data/constants';

type ModalState = {
value: XBlock | UnitXBlock;
value?: XBlock | UnitXBlock;
subsectionId?: string;
sectionId?: string;
};
Expand Down
13 changes: 11 additions & 2 deletions src/content-tags-drawer/data/api.mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -403,10 +403,19 @@ mockTaxonomyTagsData.applyMock = () => jest.spyOn(api, 'getTaxonomyTagsData').mo
/**
* Mock for `getContentData()`
*/
export async function mockContentData(): Promise<any> {
return mockContentData.data;
export async function mockContentData(contentId: string): Promise<any> {
switch (contentId) {
case mockContentData.textXBlock:
return mockContentData.textXBlockData;
default:
return mockContentData.data;
}
}
mockContentData.data = {
displayName: 'Unit 1',
};
mockContentData.textXBlock = 'block-v1:edX+DemoX+Demo_Course+type@html+block@030e35c4756a4ddc8d40b95fbbfff4d4';
mockContentData.textXBlockData = {
displayName: 'Text XBlock 1',
};
mockContentData.applyMock = () => jest.spyOn(api, 'getContentData').mockImplementation(mockContentData);
3 changes: 3 additions & 0 deletions src/course-outline/card-header/CardHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ interface CardHeaderProps {
onClickMoveDown: () => void;
onClickCopy?: () => void;
onClickCard?: (e: React.MouseEvent) => void;
onClickManageTags?: () => void;
titleComponent: ReactNode;
namePrefix: string;
proctoringExamConfigurationLink?: string,
Expand Down Expand Up @@ -86,6 +87,7 @@ const CardHeader = ({
onClickMoveDown,
onClickCopy,
onClickCard,
onClickManageTags,
titleComponent,
namePrefix,
actions,
Expand Down Expand Up @@ -113,6 +115,7 @@ const CardHeader = ({
if (showNewSidebar && showAlignSidebar) {
setCurrentPageKey('align');
onClickMenuButton();
onClickManageTags?.();
} else {
openLegacyTagsDrawer();
}
Expand Down
3 changes: 1 addition & 2 deletions src/course-outline/outline-sidebar/OutlineAlignSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@ import { useOutlineSidebarContext } from './OutlineSidebarContext';
export const OutlineAlignSidebar = () => {
const {
courseId,
currentSelection,
setCurrentSelection,
} = useCourseAuthoringContext();
const { selectedContainerState, clearSelection } = useOutlineSidebarContext();

const sidebarContentId = currentSelection?.currentId || selectedContainerState?.currentId || courseId;
const sidebarContentId = selectedContainerState?.currentId || courseId;

const { data: contentData } = useContentData(sidebarContentId);

Expand Down
3 changes: 3 additions & 0 deletions src/course-outline/outline-sidebar/OutlineSidebarContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ interface OutlineSidebarContextData {
open: () => void;
toggle: () => void;
selectedContainerState?: SelectionState;
setSelectedContainerState: (selectedContainerState?: SelectionState) => void;
openContainerInfoSidebar: (containerId: string, subsectionId?: string, sectionId?: string) => void;
clearSelection: () => void;
/** Stores last section that allows adding subsections inside it. */
Expand Down Expand Up @@ -188,6 +189,7 @@ export const OutlineSidebarProvider = ({ children }: { children?: React.ReactNod
open,
toggle,
selectedContainerState,
setSelectedContainerState,
openContainerInfoSidebar,
clearSelection,
lastEditableSection,
Expand All @@ -205,6 +207,7 @@ export const OutlineSidebarProvider = ({ children }: { children?: React.ReactNod
open,
toggle,
selectedContainerState,
setSelectedContainerState,
openContainerInfoSidebar,
clearSelection,
lastEditableSection,
Expand Down
35 changes: 32 additions & 3 deletions src/course-outline/outline-sidebar/info-sidebar/InfoSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,19 @@ import { useIntl } from '@edx/frontend-platform/i18n';
import { useToggle } from '@openedx/paragon';
import { SchoolOutline, Tag } from '@openedx/paragon/icons';
import { ContentTagsDrawerSheet, ContentTagsSnippet } from '@src/content-tags-drawer';
import { useCourseItemData } from '@src/course-outline/data/apiHooks';
import { LibraryReferenceCard } from '@src/course-outline/outline-sidebar/LibraryReferenceCard';
import { invalidateLinksQuery } from '@src/course-libraries/data/apiHooks';
import { courseOutlineQueryKeys, useCourseItemData } from '@src/course-outline/data/apiHooks';
import { fetchCourseSectionQuery } from '@src/course-outline/data/thunk';
import { useOutlineSidebarContext } from '@src/course-outline/outline-sidebar/OutlineSidebarContext';
import { useCourseAuthoringContext } from '@src/CourseAuthoringContext';
import { ComponentCountSnippet, getItemIcon } from '@src/generic/block-type-utils';
import { normalizeContainerType } from '@src/generic/key-utils';
import { SidebarContent, SidebarSection } from '@src/generic/sidebar';
import { useGetBlockTypes } from '@src/search-manager';
import { useQueryClient } from '@tanstack/react-query';
import { useCallback } from 'react';
import { useDispatch } from 'react-redux';
import { LibraryReferenceCard } from '@src/generic/library-reference-card/LibraryReferenceCard';
import messages from '../messages';

interface Props {
Expand All @@ -16,17 +23,39 @@ interface Props {

export const InfoSection = ({ itemId }: Props) => {
const intl = useIntl();
const dispatch = useDispatch();
const queryClient = useQueryClient();
const { data: itemData } = useCourseItemData(itemId);
const { data: componentData } = useGetBlockTypes(
[`breadcrumbs.usage_key = "${itemId}"`],
);
const category = normalizeContainerType(itemData?.category || '');
const { selectedContainerState, openContainerInfoSidebar } = useOutlineSidebarContext();
const { courseId } = useCourseAuthoringContext();

const [isManageTagsDrawerOpen, openManageTagsDrawer, closeManageTagsDrawer] = useToggle(false);

// istanbul ignore next
const handleOnPostChangeSync = useCallback(() => {
if (selectedContainerState?.sectionId) {
dispatch(fetchCourseSectionQuery([selectedContainerState.sectionId]));
}
if (courseId) {
invalidateLinksQuery(queryClient, courseId);
queryClient.invalidateQueries({
queryKey: courseOutlineQueryKeys.course(courseId),
});
}
}, [dispatch, selectedContainerState, queryClient, courseId]);

return (
<>
<LibraryReferenceCard itemId={itemId} />
<LibraryReferenceCard
itemId={itemId}
sectionId={selectedContainerState?.sectionId}
postChange={handleOnPostChangeSync}
goToParent={openContainerInfoSidebar}
/>
<SidebarContent>
<SidebarSection
title={intl.formatMessage(messages[`${category}ContentSummaryText`])}
Expand Down
60 changes: 0 additions & 60 deletions src/course-outline/outline-sidebar/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,66 +170,6 @@ const messages = defineMessages({
defaultMessage: 'Settings',
description: 'Settings tab title in container sidebar',
},
libraryReferenceCardText: {
id: 'course-authoring.course-outline.sidebar.library.reference.card.text',
defaultMessage: 'Library Reference',
description: 'Library reference card text in sidebar',
},
hasTopParentText: {
id: 'course-authoring.course-outline.sidebar.library.reference.card.has-top-parent-text',
defaultMessage: '{name} was reused as part of a {parentType}.',
description: 'Text displayed in sidebar library reference card when a block was reused as part of a parent block',
},
hasTopParentBtn: {
id: 'course-authoring.course-outline.sidebar.library.reference.card.has-top-parent-btn',
defaultMessage: 'View {parentType}',
description: 'Text displayed in sidebar library reference card button when a block was reused as part of a parent block',
},
hasTopParentReadyToSyncText: {
id: 'course-authoring.course-outline.sidebar.library.reference.card.has-top-parent-sync-text',
defaultMessage: '{name} was reused as part of a {parentType} which has updates available.',
description: 'Text displayed in sidebar library reference card when a block has updates available as it was reused as part of a parent block',
},
hasTopParentReadyToSyncBtn: {
id: 'course-authoring.course-outline.sidebar.library.reference.card.has-top-parent-sync-btn',
defaultMessage: 'Review Updates',
description: 'Text displayed in sidebar library reference card button when a block has updates available as it was reused as part of a parent block',
},
hasTopParentBrokenLinkText: {
id: 'course-authoring.course-outline.sidebar.library.reference.card.has-top-parent-broken-link-text',
defaultMessage: '{name} was reused as part of a {parentType} which has a broken link. To recieve library updates to this component, unlink the broken link.',
description: 'Text displayed in sidebar library reference card when a block was reused as part of a parent block which has a broken link.',
},
hasTopParentBrokenLinkBtn: {
id: 'course-authoring.course-outline.sidebar.library.reference.card.has-top-parent-broken-link-btn',
defaultMessage: 'Unlink {parentType}',
description: 'Text displayed in sidebar library reference card button when a block was reused as part of a parent block which has a broken link.',
},
topParentBrokenLinkText: {
id: 'course-authoring.course-outline.sidebar.library.reference.card.top-parent-broken-link-text',
defaultMessage: 'The link between {name} and the library version has been broken. To edit or make changes, unlink component.',
description: 'Text displayed in sidebar library reference card when a block has a broken link.',
},
topParentBrokenLinkBtn: {
id: 'course-authoring.course-outline.sidebar.library.reference.card.top-parent-broken-link-btn',
defaultMessage: 'Unlink from library',
description: 'Text displayed in sidebar library reference card button when a block has a broken link.',
},
topParentModifiedText: {
id: 'course-authoring.course-outline.sidebar.library.reference.card.top-parent-modified-text',
defaultMessage: '{name} has been modified in this course.',
description: 'Text displayed in sidebar library reference card when it is modified in course.',
},
topParentReaadyToSyncText: {
id: 'course-authoring.course-outline.sidebar.library.reference.card.top-parent-ready-to-sync-text',
defaultMessage: '{name} has available updates',
description: 'Text displayed in sidebar library reference card when it is has updates available.',
},
topParentReaadyToSyncBtn: {
id: 'course-authoring.course-outline.sidebar.library.reference.card.top-parent-ready-to-sync-btn',
defaultMessage: 'Review Updates',
description: 'Text displayed in sidebar library reference card button when it is has updates available.',
},
cannotAddAlertMsg: {
id: 'course-authoring.course-outline.sidebar.library.reference.add-sidebar.alert.text',
defaultMessage: '{name} is a library {category}. Content cannot be added to Library referenced {category}s.',
Expand Down
6 changes: 6 additions & 0 deletions src/course-outline/section-card/SectionCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,7 @@ describe('<SectionCard />', () => {
it('should open align sidebar', async () => {
const user = userEvent.setup();
const mockSetCurrentPageKey = jest.fn();
const mockSetSelectedContainerState = jest.fn();

const testSidebarPage = {
component: CourseInfoSidebar,
Expand All @@ -346,6 +347,7 @@ describe('<SectionCard />', () => {
stopCurrentFlow: jest.fn(),
openContainerInfoSidebar: jest.fn(),
clearSelection: jest.fn(),
setSelectedContainerState: mockSetSelectedContainerState,
}));
setConfig({
...getConfig(),
Expand All @@ -369,5 +371,9 @@ describe('<SectionCard />', () => {
currentId: section.id,
sectionId: section.id,
});
expect(mockSetSelectedContainerState).toHaveBeenCalledWith({
currentId: section.id,
sectionId: section.id,
});
});
});
10 changes: 9 additions & 1 deletion src/course-outline/section-card/SectionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ const SectionCard = ({
const currentRef = useRef(null);
const dispatch = useDispatch();
const { activeId, overId } = useContext(DragContext);
const { selectedContainerState, openContainerInfoSidebar } = useOutlineSidebarContext();
const { selectedContainerState, openContainerInfoSidebar, setSelectedContainerState } = useOutlineSidebarContext();
const [searchParams] = useSearchParams();
const locatorId = searchParams.get('show');
const {
Expand Down Expand Up @@ -199,6 +199,13 @@ const SectionCard = ({
});
};

const handleClickManageTags = () => {
setSelectedContainerState({
currentId: section.id,
sectionId: section.id,
});
};

const handleOpenHighlightsModal = () => {
onOpenHighlightsModal(section);
};
Expand Down Expand Up @@ -284,6 +291,7 @@ const SectionCard = ({
onClickSync={openSyncModal}
onClickCard={(e) => onClickCard(e, true)}
onClickDuplicate={onDuplicateSubmit}
onClickManageTags={handleClickManageTags}
titleComponent={titleComponent}
namePrefix={namePrefix}
actions={actions}
Expand Down
7 changes: 7 additions & 0 deletions src/course-outline/subsection-card/SubsectionCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,7 @@ describe('<SubsectionCard />', () => {
it('should open align sidebar', async () => {
const user = userEvent.setup();
const mockSetCurrentPageKey = jest.fn();
const mockSetSelectedContainerState = jest.fn();

const testSidebarPage = {
component: CourseInfoSidebar,
Expand All @@ -441,6 +442,7 @@ describe('<SubsectionCard />', () => {
stopCurrentFlow: jest.fn(),
openContainerInfoSidebar: jest.fn(),
clearSelection: jest.fn(),
setSelectedContainerState: mockSetSelectedContainerState,
}));
setConfig({
...getConfig(),
Expand All @@ -465,5 +467,10 @@ describe('<SubsectionCard />', () => {
subsectionId: subsection.id,
sectionId: section.id,
});
expect(mockSetSelectedContainerState).toHaveBeenCalledWith({
currentId: subsection.id,
subsectionId: subsection.id,
sectionId: section.id,
});
});
});
11 changes: 10 additions & 1 deletion src/course-outline/subsection-card/SubsectionCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ const SubsectionCard = ({
const intl = useIntl();
const dispatch = useDispatch();
const { activeId, overId } = useContext(DragContext);
const { selectedContainerState, openContainerInfoSidebar } = useOutlineSidebarContext();
const { selectedContainerState, openContainerInfoSidebar, setSelectedContainerState } = useOutlineSidebarContext();
const [searchParams] = useSearchParams();
const locatorId = searchParams.get('show');
const [isSyncModalOpen, openSyncModal, closeSyncModal] = useToggle(false);
Expand Down Expand Up @@ -162,6 +162,14 @@ const SubsectionCard = ({
});
};

const handleClickManageTags = () => {
setSelectedContainerState({
currentId: subsection.id,
subsectionId: subsection.id,
sectionId: section.id,
});
};

const handleOnPostChangeSync = useCallback(() => {
dispatch(fetchCourseSectionQuery([section.id]));
if (courseId) {
Expand Down Expand Up @@ -290,6 +298,7 @@ const SubsectionCard = ({
onClickSync={openSyncModal}
onClickCard={(e) => onClickCard(e, true)}
onClickDuplicate={onDuplicateSubmit}
onClickManageTags={handleClickManageTags}
titleComponent={titleComponent}
namePrefix={namePrefix}
actions={actions}
Expand Down
7 changes: 7 additions & 0 deletions src/course-outline/unit-card/UnitCard.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ describe('<UnitCard />', () => {
it('should open align sidebar', async () => {
const user = userEvent.setup();
const mockSetCurrentPageKey = jest.fn();
const mockSetSelectedContainerState = jest.fn();

const testSidebarPage = {
component: CourseInfoSidebar,
Expand All @@ -332,6 +333,7 @@ describe('<UnitCard />', () => {
stopCurrentFlow: jest.fn(),
openContainerInfoSidebar: jest.fn(),
clearSelection: jest.fn(),
setSelectedContainerState: mockSetSelectedContainerState,
}));
setConfig({
...getConfig(),
Expand All @@ -356,5 +358,10 @@ describe('<UnitCard />', () => {
subsectionId: subsection.id,
sectionId: section.id,
});
expect(mockSetSelectedContainerState).toHaveBeenCalledWith({
currentId: unit.id,
subsectionId: subsection.id,
sectionId: section.id,
});
});
});
11 changes: 10 additions & 1 deletion src/course-outline/unit-card/UnitCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ const UnitCard = ({
const currentRef = useRef(null);
const dispatch = useDispatch();
const [searchParams] = useSearchParams();
const { selectedContainerState, openContainerInfoSidebar } = useOutlineSidebarContext();
const { selectedContainerState, openContainerInfoSidebar, setSelectedContainerState } = useOutlineSidebarContext();
const locatorId = searchParams.get('show');
const [isSyncModalOpen, openSyncModal, closeSyncModal] = useToggle(false);
const namePrefix = 'unit';
Expand Down Expand Up @@ -140,6 +140,14 @@ const UnitCard = ({
});
};

const handleClickManageTags = () => {
setSelectedContainerState({
currentId: unit.id,
subsectionId: subsection.id,
sectionId: section.id,
});
};

const handleUnitMoveUp = () => {
onOrderChange(section, moveUpDetails);
};
Expand Down Expand Up @@ -269,6 +277,7 @@ const UnitCard = ({
onClickSync={openSyncModal}
onClickCard={onClickCard}
onClickDuplicate={onDuplicateSubmit}
onClickManageTags={handleClickManageTags}
titleComponent={titleComponent}
namePrefix={namePrefix}
actions={actions}
Expand Down
Loading